From 8851c2dceb03d1bacc3422a5f829bff10c42673b Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 3 Jul 2024 11:12:42 +0400 Subject: [PATCH 01/36] fix: Fix mask --- src/qibocal/protocols/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/utils.py b/src/qibocal/protocols/utils.py index 0584e4c21..3f145f4d2 100644 --- a/src/qibocal/protocols/utils.py +++ b/src/qibocal/protocols/utils.py @@ -770,5 +770,5 @@ def extract_feature( z[first_mask], [100 - ci_second_mask, ci_second_mask], ) - second_mask = z[first_mask] < max if feat == "min" else z[first_mask] > min + second_mask = z[first_mask] < min if feat == "min" else z[first_mask] > max return x[first_mask][second_mask], y[first_mask][second_mask] From ea0db455094af7a19891d71712ac7ce9e012d99b Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 10 Jul 2024 17:30:35 +0400 Subject: [PATCH 02/36] feat: Improve sweetspot guess but just taking maximum --- .../protocols/flux_dependence/qubit_flux_dependence.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index 303d19d54..f20b2597f 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -218,8 +218,10 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - # sweetspot obtain by solving transmon_frequency == w_max - sweetspot[qubit] = -popt[2] / popt[1] + # just take maximum + sweetspot[qubit] = biases[ + np.argmax([fit_function(x, *popt) for x in biases]) + ] matrix_element[qubit] = popt[1] except ValueError as e: log.error( From 3df6ccd4c4db52acb64ad3bcec0e0b5ddc064327 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 17 Jul 2024 15:20:30 +0400 Subject: [PATCH 03/36] fix: run transpiled circuit in platform with qubit strings --- src/qibocal/auto/transpile.py | 9 ++++++++- src/qibocal/protocols/randomized_benchmarking/utils.py | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index 72a7e1a48..2b2666b41 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -1,9 +1,11 @@ +from copy import deepcopy from typing import Optional from qibo import Circuit from qibo.backends.abstract import Backend from qibo.transpiler.pipeline import Passes from qibo.transpiler.unroller import NativeGates, Unroller +from qibolab.qubits import QubitId def execute_transpiled_circuits( @@ -39,7 +41,7 @@ def execute_transpiled_circuits( def execute_transpiled_circuit( circuit: Circuit, - qubit_map: list[int], + qubit_map: list[QubitId], backend: Backend, initial_state=None, nshots=1000, @@ -54,6 +56,11 @@ def execute_transpiled_circuit( For the qubit map look :func:`dummy_transpiler`. This function returns the transpiled circuit and the execution results. """ + # TODO: propagate the following lines in execute_transpiled_circuits + qubits = list(backend.platform.qubits.keys()) + if isinstance(qubit_map[0], str): + qubit_map_copy = deepcopy(qubit_map) + qubit_map = [qubits.index(i) for i in qubit_map_copy] if backend.name == "qibolab": platform_nqubits = backend.platform.nqubits new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 6ec8b5186..ae6780139 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -439,6 +439,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True): if single_qubit else [list(i) for i in targets] * (len(params.depths) * params.niter) ) + print("GGGGGGGG", qubit_maps) if params.unrolling: _, executed_circuits = execute_transpiled_circuits( circuits, @@ -572,7 +573,7 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]: """ full_circuit = None random_indexes = [] - if isinstance(target, int): # int for qubit + if isinstance(target, int) or isinstance(target, str): # int for qubit nqubits = 1 rb_gen_layer = rb_gen.layer_gen_single_qubit() elif isinstance(target, Tuple): # Tuple for qubit pair From 709afe41311d70f4325bc275f8617b3550d51978 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Wed, 17 Jul 2024 16:36:18 +0400 Subject: [PATCH 04/36] fix: typing using QubitId --- src/qibocal/protocols/randomized_benchmarking/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index ae6780139..0a200e077 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -439,7 +439,6 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True): if single_qubit else [list(i) for i in targets] * (len(params.depths) * params.niter) ) - print("GGGGGGGG", qubit_maps) if params.unrolling: _, executed_circuits = execute_transpiled_circuits( circuits, @@ -573,7 +572,7 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]: """ full_circuit = None random_indexes = [] - if isinstance(target, int) or isinstance(target, str): # int for qubit + if isinstance(target, QubitId): nqubits = 1 rb_gen_layer = rb_gen.layer_gen_single_qubit() elif isinstance(target, Tuple): # Tuple for qubit pair From 4e7942fba8e1e29c26978569bd2b0b9e5b6970b2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 16:23:28 +0200 Subject: [PATCH 05/36] fix: Set platform in RB backend --- src/qibocal/protocols/randomized_benchmarking/standard_rb.py | 2 +- src/qibocal/protocols/randomized_benchmarking/utils.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py index 70353eb82..aa1374b31 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb.py @@ -88,7 +88,7 @@ def _acquisition( RBData: The depths, samples and ground state probability of each experiment in the scan. """ - return rb_acquisition(params, targets) + return rb_acquisition(params, targets, platform) def _fit(data: RBData) -> StandardRBResult: diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 0a200e077..432a37ecc 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -10,6 +10,7 @@ from qibo.backends import GlobalBackend from qibo.config import raise_error from qibo.models import Circuit +from qibolab.platform import Platform from qibolab.qubits import QubitId, QubitPairId from qibocal.auto.operation import Data, Parameters, Results @@ -464,6 +465,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True): def rb_acquisition( params: Parameters, targets: list[QubitId], + platform: Platform, add_inverse_layer: bool = True, interleave: str = None, ) -> RBData: @@ -481,6 +483,7 @@ def rb_acquisition( RBData: The depths, samples, and ground state probability of each experiment in the scan. """ data, noise_model, backend = setup(params, single_qubit=True) + backend.platform = platform circuits, indexes, npulses_per_clifford = get_circuits( params, targets, add_inverse_layer, interleave, noise_model, single_qubit=True ) From 3368e6827b18ffc6086e769de40bee5beb971303 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 17:03:12 +0200 Subject: [PATCH 06/36] fix: Propagate platform parameter addition to filtered RB --- src/qibocal/protocols/randomized_benchmarking/filtered_rb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py b/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py index fb04d84df..95f041322 100644 --- a/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py +++ b/src/qibocal/protocols/randomized_benchmarking/filtered_rb.py @@ -42,7 +42,7 @@ def _acquisition( RBData: The depths, samples and ground state probability of each experiment in the scan. """ - return rb_acquisition(params, targets, add_inverse_layer=False) + return rb_acquisition(params, targets, platform, add_inverse_layer=False) def _fit(data: RBData) -> FilteredRBResult: From 0065b6f33a591a00c740a771cebdc00ba60c3b8f Mon Sep 17 00:00:00 2001 From: Andrea Date: Thu, 18 Jul 2024 17:45:55 +0400 Subject: [PATCH 07/36] feat: propagate fix to other circuit protocols --- src/qibocal/protocols/readout_mitigation_matrix.py | 1 + src/qibocal/protocols/state_tomography.py | 1 + src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py | 1 + src/qibocal/protocols/two_qubit_state_tomography.py | 1 + 4 files changed, 4 insertions(+) diff --git a/src/qibocal/protocols/readout_mitigation_matrix.py b/src/qibocal/protocols/readout_mitigation_matrix.py index f6612e678..61397e4f5 100644 --- a/src/qibocal/protocols/readout_mitigation_matrix.py +++ b/src/qibocal/protocols/readout_mitigation_matrix.py @@ -98,6 +98,7 @@ def _acquisition( nshots=params.nshots, qubit_list=[list(qq) for qq in targets] ) backend = GlobalBackend() + backend.platform = platform transpiler = dummy_transpiler(backend) qubit_map = [i for i in range(platform.nqubits)] for qubits in targets: diff --git a/src/qibocal/protocols/state_tomography.py b/src/qibocal/protocols/state_tomography.py index bae44b910..21411ef61 100644 --- a/src/qibocal/protocols/state_tomography.py +++ b/src/qibocal/protocols/state_tomography.py @@ -99,6 +99,7 @@ def _acquisition( params.circuit = Circuit(len(targets)) backend = GlobalBackend() + backend.platform = platform transpiler = dummy_transpiler(backend) data = StateTomographyData() diff --git a/src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py b/src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py index 17d95f9fe..6adc6f147 100644 --- a/src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py +++ b/src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py @@ -221,6 +221,7 @@ def _acquisition_circuits( thetas=thetas.tolist(), ) backend = GlobalBackend() + backend.platform = platform transpiler = dummy_transpiler(backend) qubit_map = [i for i in range(platform.nqubits)] if params.apply_error_mitigation: diff --git a/src/qibocal/protocols/two_qubit_state_tomography.py b/src/qibocal/protocols/two_qubit_state_tomography.py index 4abdc9524..0d3ecabc4 100644 --- a/src/qibocal/protocols/two_qubit_state_tomography.py +++ b/src/qibocal/protocols/two_qubit_state_tomography.py @@ -98,6 +98,7 @@ def _acquisition( params.circuit = Circuit(len(qubits)) backend = GlobalBackend() + backend.platform = platform simulator = NumpyBackend() transpiler = dummy_transpiler(backend) From 4f363a9e9107bf3fc28de8d05a3456ae6167abe6 Mon Sep 17 00:00:00 2001 From: Andrea Date: Thu, 18 Jul 2024 17:50:04 +0400 Subject: [PATCH 08/36] feat: Propagate changes to 2qRB --- src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py | 2 +- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py index d76aa59ec..2f055df10 100644 --- a/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py +++ b/src/qibocal/protocols/randomized_benchmarking/standard_rb_2q.py @@ -32,7 +32,7 @@ def _acquisition( ) -> RB2QData: """Data acquisition for two qubit Standard Randomized Benchmarking.""" - return twoq_rb_acquisition(params, targets) + return twoq_rb_acquisition(params, targets, platform) def _fit(data: RB2QData) -> StandardRBResult: diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 432a37ecc..3c528b719 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -513,6 +513,7 @@ def rb_acquisition( def twoq_rb_acquisition( params: Parameters, targets: list[QubitPairId], + platform: Platform, add_inverse_layer: bool = True, interleave: str = None, ) -> RB2QData: @@ -530,6 +531,7 @@ def twoq_rb_acquisition( """ data, noise_model, backend = setup(params, single_qubit=False) + backend.platform = platform circuits, indexes, npulses_per_clifford = get_circuits( params, targets, add_inverse_layer, interleave, noise_model, single_qubit=False ) From ae7504fa43ca19dd0aa05953d6a94f3ae189c571 Mon Sep 17 00:00:00 2001 From: Edoardo Pedicillo Date: Fri, 19 Jul 2024 10:18:48 +0400 Subject: [PATCH 09/36] Apply suggestions from code review Co-authored-by: Andrea Pasquale --- src/qibocal/auto/transpile.py | 2 +- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index 2b2666b41..bddceb391 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -57,7 +57,7 @@ def execute_transpiled_circuit( This function returns the transpiled circuit and the execution results. """ # TODO: propagate the following lines in execute_transpiled_circuits - qubits = list(backend.platform.qubits.keys()) + qubits = list(backend.platform.qubits) if isinstance(qubit_map[0], str): qubit_map_copy = deepcopy(qubit_map) qubit_map = [qubits.index(i) for i in qubit_map_copy] diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 3c528b719..2f8a28e14 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -577,7 +577,7 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]: """ full_circuit = None random_indexes = [] - if isinstance(target, QubitId): + if isinstance(target, (str,int)): nqubits = 1 rb_gen_layer = rb_gen.layer_gen_single_qubit() elif isinstance(target, Tuple): # Tuple for qubit pair From 4fbfaf7d63e097f1a28023937462a9c784dac9bc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 06:20:00 +0000 Subject: [PATCH 10/36] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibocal/protocols/randomized_benchmarking/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/randomized_benchmarking/utils.py b/src/qibocal/protocols/randomized_benchmarking/utils.py index 2f8a28e14..35e17fb11 100644 --- a/src/qibocal/protocols/randomized_benchmarking/utils.py +++ b/src/qibocal/protocols/randomized_benchmarking/utils.py @@ -577,7 +577,7 @@ def layer_circuit(rb_gen: Callable, depth: int, target) -> tuple[Circuit, dict]: """ full_circuit = None random_indexes = [] - if isinstance(target, (str,int)): + if isinstance(target, (str, int)): nqubits = 1 rb_gen_layer = rb_gen.layer_gen_single_qubit() elif isinstance(target, Tuple): # Tuple for qubit pair From 5d5b7adc43a176cd23af92426dcf623c65152616 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 25 Jul 2024 12:44:42 +0400 Subject: [PATCH 11/36] refactor: solve code duplication --- src/qibocal/auto/transpile.py | 81 ++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index bddceb391..f24b3873e 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -1,4 +1,3 @@ -from copy import deepcopy from typing import Optional from qibo import Circuit @@ -8,13 +7,50 @@ from qibolab.qubits import QubitId +def transpile_circuits( + circuits: list[Circuit], + qubit_maps: list[list[QubitId]], + backend: Backend, + transpiler: Optional[Passes], +): + """ + Apply the `transpiler` to the `circuits` list and pad them in + circuits with the same number of qubits in the platform. + Before manipulating the circuits, this function check that the + `qubits_maps` contain string ids and in the positive case it + remap them in integers, following the ids order provided by the + platform. + + .. note:: + + In this function we are implicitly assume that the qubit ids + are all string or all integers. + """ + transpiled_circuits = [] + + qubits = list(backend.platform.qubits) + if isinstance(qubit_maps[0][0], str): + for i, qubit_map in enumerate(qubit_maps): + qubit_map = map(lambda x: qubits.index(x), qubit_map) + qubit_maps[i] = list(qubit_map) + if backend.name == "qibolab": + platform_nqubits = backend.platform.nqubits + for circuit, qubit_map in zip(circuits, qubit_maps): + new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map) + transpiled_circ, _ = transpiler(new_circuit) + transpiled_circuits.append(transpiled_circ) + else: + transpiled_circuits = circuits + return transpiled_circuits + + def execute_transpiled_circuits( circuits: list[Circuit], - qubit_maps: list[list[int]], + qubit_maps: list[list[QubitId]], backend: Backend, + transpiler: Optional[Passes], initial_states=None, nshots=1000, - transpiler: Optional[Passes] = None, ): """ If the `qibolab` backend is used, this function pads the `circuits` in new @@ -25,17 +61,14 @@ def execute_transpiled_circuits( For the qubit map look :func:`dummy_transpiler`. This function returns the list of transpiled circuits and the execution results. """ - new_circuits = [] - if backend.name == "qibolab": - platform_nqubits = backend.platform.nqubits - for circuit, qubit_map in zip(circuits, qubit_maps): - new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map) - transpiled_circ, _ = transpiler(new_circuit) - new_circuits.append(transpiled_circ) - else: - new_circuits = circuits - return new_circuits, backend.execute_circuits( - new_circuits, initial_states=initial_states, nshots=nshots + transpiled_circuits = transpile_circuits( + circuits, + qubit_maps, + backend, + transpiler, + ) + return transpiled_circuits, backend.execute_circuits( + transpiled_circuits, initial_states=initial_states, nshots=nshots ) @@ -43,9 +76,9 @@ def execute_transpiled_circuit( circuit: Circuit, qubit_map: list[QubitId], backend: Backend, + transpiler: Optional[Passes], initial_state=None, nshots=1000, - transpiler: Optional[Passes] = None, ): """ If the `qibolab` backend is used, this function pads the `circuit` in new a @@ -56,17 +89,13 @@ def execute_transpiled_circuit( For the qubit map look :func:`dummy_transpiler`. This function returns the transpiled circuit and the execution results. """ - # TODO: propagate the following lines in execute_transpiled_circuits - qubits = list(backend.platform.qubits) - if isinstance(qubit_map[0], str): - qubit_map_copy = deepcopy(qubit_map) - qubit_map = [qubits.index(i) for i in qubit_map_copy] - if backend.name == "qibolab": - platform_nqubits = backend.platform.nqubits - new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map) - transpiled_circ, _ = transpiler(new_circuit) - else: - transpiled_circ = circuit + + transpiled_circ = transpile_circuits( + [circuit], + [qubit_map], + backend, + transpiler, + )[0] return transpiled_circ, backend.execute_circuit( transpiled_circ, initial_state=initial_state, nshots=nshots ) From 2cfe6c6ebb16a48886b6b5945180ea8589b156d5 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 25 Jul 2024 13:05:35 +0400 Subject: [PATCH 12/36] doc: follow PEP 257 --- src/qibocal/auto/transpile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index f24b3873e..502b507e0 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -13,11 +13,12 @@ def transpile_circuits( backend: Backend, transpiler: Optional[Passes], ): - """ - Apply the `transpiler` to the `circuits` list and pad them in + """Transpile and pad `circuits` according to the platform. + + Apply the `transpiler` to `circuits` and pad them in circuits with the same number of qubits in the platform. Before manipulating the circuits, this function check that the - `qubits_maps` contain string ids and in the positive case it + `qubit_maps` contain string ids and in the positive case it remap them in integers, following the ids order provided by the platform. From 4bd39638dbbf481c711a47b694c6899708ecc071 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 25 Jul 2024 13:08:02 +0400 Subject: [PATCH 13/36] doc: propagate changes --- src/qibocal/auto/transpile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index 502b507e0..63f09464d 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -53,7 +53,8 @@ def execute_transpiled_circuits( initial_states=None, nshots=1000, ): - """ + """Transpile `circuits`. + If the `qibolab` backend is used, this function pads the `circuits` in new ones with a number of qubits equal to the one provided by the platform. At the end, the circuits are transpiled, executed and the results returned. @@ -81,7 +82,8 @@ def execute_transpiled_circuit( initial_state=None, nshots=1000, ): - """ + """Transpile `circuit`. + If the `qibolab` backend is used, this function pads the `circuit` in new a one with a number of qubits equal to the one provided by the platform. At the end, the circuit is transpiled, executed and the results returned. From 99df36b9fd472f0766af889089a8d18a018d4733 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 26 Jul 2024 09:20:35 +0400 Subject: [PATCH 14/36] fix: Dump using TaskId --- src/qibocal/auto/history.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qibocal/auto/history.py b/src/qibocal/auto/history.py index 1bf9bef80..ec570c0cd 100644 --- a/src/qibocal/auto/history.py +++ b/src/qibocal/auto/history.py @@ -83,13 +83,13 @@ def push(self, completed: Completed) -> TaskId: return task_id @staticmethod - def route(completed: Completed, folder: Path) -> Path: - """Determine the path related to a completed task. + def route(task_id: TaskId, folder: Path) -> Path: + """Determine the path related to a completed task given TaskId. - `folder` should be ussually the general output folder, used by Qibocal to store + `folder` should be usually the general output folder, used by Qibocal to store all the execution results. Cf. :cls:`qibocal.auto.output.Output`. """ - return folder / "data" / f"{completed.task.id}" + return folder / "data" / f"{task_id}" def flush(self, output: Optional[Path] = None): """Flush all content to disk. @@ -97,9 +97,9 @@ def flush(self, output: Optional[Path] = None): Specifying `output` is possible to select which folder should be considered as the general Qibocal output folder. Cf. :cls:`qibocal.auto.output.Output`. """ - for completed in self.values(): + for task_id, completed in self.items(): if output is not None: - completed.path = self.route(completed, output) + completed.path = self.route(task_id, output) completed.flush() # TODO: implement time_travel() From a2d5345572c60cc10b68feffbe9dc1d695b1ba2f Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 26 Jul 2024 09:46:11 +0400 Subject: [PATCH 15/36] test: Patch folder assertion --- tests/test_protocols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_protocols.py b/tests/test_protocols.py index c7a61b6c8..b5278a5a0 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -112,7 +112,7 @@ def test_acquire_command(runcard, tmp_path): **INVOKER_OPTIONS, ) - assert (outpath / "data" / protocol).is_dir() + assert (outpath / "data" / f"{protocol}-0").is_dir() # generate report from acquired data runner.invoke(command, ["report", str(outpath)], **INVOKER_OPTIONS) From 9cbd2f5b4ba963315f1e07c35d5a1c8e1227efc2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 17:53:31 +0200 Subject: [PATCH 16/36] build: Update flake to use all extras And to use flake-parts --- flake.lock | 96 ++++++++++++------------------------------------------ flake.nix | 57 +++++++++++++------------------- 2 files changed, 42 insertions(+), 111 deletions(-) diff --git a/flake.lock b/flake.lock index 8e1582716..ecb8ca90b 100644 --- a/flake.lock +++ b/flake.lock @@ -159,19 +159,21 @@ "type": "github" } }, - "flake-compat_6": { - "flake": false, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "type": "github" }, "original": { - "owner": "edolstra", - "repo": "flake-compat", + "owner": "hercules-ci", + "repo": "flake-parts", "type": "github" } }, @@ -229,23 +231,6 @@ "type": "github" } }, - "flake-utils_4": { - "inputs": { - "systems": "systems_4" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "id": "flake-utils", - "type": "indirect" - } - }, "gitignore": { "inputs": { "nixpkgs": [ @@ -381,26 +366,16 @@ "type": "github" } }, - "nixpkgs-python": { - "inputs": { - "flake-compat": "flake-compat_6", - "flake-utils": "flake-utils_4", - "nixpkgs": [ - "nixpkgs" - ] - }, + "nixpkgs-lib": { "locked": { - "lastModified": 1710929962, - "narHash": "sha256-CuPuUyX1TmxJDDZFOZMr7kHTzA8zoSJaVw0+jDVo2fw=", - "owner": "cachix", - "repo": "nixpkgs-python", - "rev": "a9e19aafbf75b8c7e5adf2d7319939309ebe0d77", - "type": "github" + "lastModified": 1719876945, + "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" }, "original": { - "owner": "cachix", - "repo": "nixpkgs-python", - "type": "github" + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" } }, "nixpkgs-regression": { @@ -565,9 +540,8 @@ "root": { "inputs": { "devenv": "devenv", - "nixpkgs": "nixpkgs_2", - "nixpkgs-python": "nixpkgs-python", - "systems": "systems_5" + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_2" } }, "systems": { @@ -614,36 +588,6 @@ "repo": "default", "type": "github" } - }, - "systems_4": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_5": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 5edf09270..52dac30b3 100644 --- a/flake.nix +++ b/flake.nix @@ -1,62 +1,49 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - systems.url = "github:nix-systems/default"; devenv = { url = "github:cachix/devenv"; inputs.nixpkgs.follows = "nixpkgs"; }; - nixpkgs-python = { - url = "github:cachix/nixpkgs-python"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + flake-parts.url = "github:hercules-ci/flake-parts"; }; outputs = { self, nixpkgs, devenv, - systems, + flake-parts, ... - } @ inputs: let - forEachSystem = nixpkgs.lib.genAttrs (import systems); - in { - packages = forEachSystem (system: { - default = - nixpkgs.legacyPackages.${system}.poetry2nix.mkPoetryApplication - { + } @ inputs: + flake-parts.lib.mkFlake {inherit inputs;} { + imports = [inputs.devenv.flakeModule]; + systems = ["x86_64-linux" "aarch64-darwin"]; + + perSystem = {pkgs, ...}: { + packages.default = pkgs.poetry2nix.mkPoetryApplication { projectDir = self; preferWheels = true; }; - }); - devShells = - forEachSystem - (system: let - pkgs = nixpkgs.legacyPackages.${system}; - in { - default = devenv.lib.mkShell { - inherit inputs pkgs; + devenv.shells.default = { + packages = with pkgs; [poethepoet pre-commit stdenv.cc.cc.lib]; - modules = [ - { - packages = with pkgs; [pre-commit poethepoet]; - - languages.python = { + languages = { + python = { + enable = true; + poetry = { enable = true; - libraries = with pkgs; [zlib]; - poetry = { + install = { enable = true; - install.enable = true; - install.groups = ["dev" "test"]; + allExtras = true; + groups = ["dev" "test"]; }; - version = "3.11"; }; - } - ]; + }; + }; }; - }); - }; + }; + }; nixConfig = { extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; From 5e983eb02a66f7d85ae1435c6ea521a915cc0749 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 17:57:01 +0200 Subject: [PATCH 17/36] build: Export qibolab platforms env var in flake --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 52dac30b3..a2388c9c9 100644 --- a/flake.nix +++ b/flake.nix @@ -25,9 +25,13 @@ preferWheels = true; }; - devenv.shells.default = { + devenv.shells.default = {config, ...}: { packages = with pkgs; [poethepoet pre-commit stdenv.cc.cc.lib]; + env = { + QIBOLAB_PLATFORMS = (dirOf config.env.DEVENV_ROOT) + "/qibolab_platforms_qrc"; + }; + languages = { python = { enable = true; From d42d357be303b9cfa381a35d4db016cc62d79919 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:04:44 +0200 Subject: [PATCH 18/36] test: Small fixes to use example for development --- runcards/.gitignore | 1 + runcards/single_shot.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 runcards/.gitignore diff --git a/runcards/.gitignore b/runcards/.gitignore new file mode 100644 index 000000000..578652c33 --- /dev/null +++ b/runcards/.gitignore @@ -0,0 +1 @@ +test*/ diff --git a/runcards/single_shot.py b/runcards/single_shot.py index 5314f8ea7..b3ad5ad1e 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -10,12 +10,12 @@ folder = Path("test_x") force = True -backend = construct_backend(backend="qibolab", platform="qw11q") +backend = construct_backend(backend="qibolab", platform="dummy") platform = backend.platform if platform is None: raise ValueError("Qibocal requires a Qibolab platform to run.") -executor = Executor(name="myexec", history=History(), platform=platform, targets=["D4"]) +executor = Executor(name="myexec", history=History(), platform=platform, targets=[0]) # generate output folder path = Output.mkdir(folder, force) From 9393f9d21971ddc85e379e77fd778857a10a2e39 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:09:18 +0200 Subject: [PATCH 19/36] fix: Move core executor methods at the beginning of the class definition --- src/qibocal/auto/execute.py | 82 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index 664538685..edc307021 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -92,6 +92,47 @@ def __post_init__(self): if self.name is not None: _register(self.name, self) + @classmethod + def create(cls, name: str, platform: Union[Platform, str, None] = None): + """Load list of protocols.""" + platform = ( + platform + if isinstance(platform, Platform) + else create_platform( + platform + if platform is not None + else os.environ.get("QIBO_PLATFORM", "dummy") + ) + ) + return cls( + name=name, + history=History(), + platform=platform, + targets=list(platform.qubits), + update=True, + ) + + def run_protocol( + self, + protocol: Routine, + parameters: Action, + mode: ExecutionMode = AUTOCALIBRATION, + ) -> Completed: + """Run single protocol in ExecutionMode mode.""" + task = Task(action=parameters, operation=protocol) + log.info(f"Executing mode {mode} on {task.action.id}.") + + completed = task.run(platform=self.platform, targets=self.targets, mode=mode) + self.history.push(completed) + + # TODO: drop, as the conditions won't be necessary any longer, and then it could + # be performed as part of `task.run` https://github.com/qiboteam/qibocal/issues/910 + if ExecutionMode.FIT in mode: + if self.update and task.update: + completed.update_platform(platform=self.platform) + + return completed + def __getattribute__(self, name: str): """Provide access to routines through the executor. @@ -176,47 +217,6 @@ def wrapper( return wrapper - @classmethod - def create(cls, name: str, platform: Union[Platform, str, None] = None): - """Load list of protocols.""" - platform = ( - platform - if isinstance(platform, Platform) - else create_platform( - platform - if platform is not None - else os.environ.get("QIBO_PLATFORM", "dummy") - ) - ) - return cls( - name=name, - history=History(), - platform=platform, - targets=list(platform.qubits), - update=True, - ) - - def run_protocol( - self, - protocol: Routine, - parameters: Action, - mode: ExecutionMode = AUTOCALIBRATION, - ) -> Completed: - """Run single protocol in ExecutionMode mode.""" - task = Task(action=parameters, operation=protocol) - log.info(f"Executing mode {mode} on {task.action.id}.") - - completed = task.run(platform=self.platform, targets=self.targets, mode=mode) - self.history.push(completed) - - # TODO: drop, as the conditions won't be necessary any longer, and then it could - # be performed as part of `task.run` https://github.com/qiboteam/qibocal/issues/910 - if ExecutionMode.FIT in mode: - if self.update and task.update: - completed.update_platform(platform=self.platform) - - return completed - def unload(self): """Unlist the executor from available modules.""" if self.name is not None: From 4151bab93296283dbb4dde99bf02bf9a6488ac67 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:24:50 +0200 Subject: [PATCH 20/36] build: Add back forgotten zlib library --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index a2388c9c9..4f49e7223 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,7 @@ languages = { python = { enable = true; + libraries = with pkgs; [zlib]; poetry = { enable = true; install = { From f299903edb299068e8822115d34a4e80afd2f914 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:25:31 +0200 Subject: [PATCH 21/36] feat: Move script incipt into an init method --- runcards/single_shot.py | 30 +++--------------------------- src/qibocal/auto/execute.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/runcards/single_shot.py b/runcards/single_shot.py index b3ad5ad1e..edc154bf1 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -1,37 +1,13 @@ from pathlib import Path -from qibo.backends import construct_backend - from qibocal.auto.execute import Executor -from qibocal.auto.history import History -from qibocal.auto.output import Metadata, Output from qibocal.cli.report import report -folder = Path("test_x") -force = True - -backend = construct_backend(backend="qibolab", platform="dummy") -platform = backend.platform -if platform is None: - raise ValueError("Qibocal requires a Qibolab platform to run.") - -executor = Executor(name="myexec", history=History(), platform=platform, targets=[0]) - -# generate output folder -path = Output.mkdir(folder, force) - -# generate meta -meta = Metadata.generate(path.name, backend) -output = Output(History(), meta, platform) -output.dump(path) - -from myexec import single_shot_classification +executor = Executor.create(name="myexec", platform="dummy") -# connect and initialize platform -platform.connect() +from myexec import init, single_shot_classification -# run -meta.start() +path, output, meta, platform = init(Path("test_x"), force=True) completed = single_shot_classification(nshots=1000) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index edc307021..733d0409d 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -6,8 +6,10 @@ import sys from copy import deepcopy from dataclasses import dataclass, fields +from pathlib import Path from typing import Optional, Union +from qibo.backends import construct_backend from qibolab import create_platform from qibolab.platform import Platform @@ -17,6 +19,7 @@ from .history import History from .mode import AUTOCALIBRATION, ExecutionMode from .operation import Routine +from .output import Metadata, Output from .task import Action, Completed, Targets, Task PLATFORM_DIR = "platform" @@ -237,3 +240,36 @@ def __del__(self): except KeyError: # it has been explicitly unloaded, no need to do it again pass + + def init( + self, + folder: Path, + force: bool = False, + platform: Optional[Platform] = None, + targets: Optional[Targets] = None, + ): + if platform is not None: + self.platform = platform + else: + platform = self.platform + + backend = construct_backend(backend="qibolab", platform=platform) + + if targets is not None: + self.targets = targets + + # generate output folder + path = Output.mkdir(folder, force) + + # generate meta + meta = Metadata.generate(path.name, backend) + output = Output(History(), meta, platform) + output.dump(path) + + # connect and initialize platform + platform.connect() + + # run + meta.start() + + return path, output, meta, platform From 5cf14a00bd303194dd26387c3822589ecb557683 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:28:53 +0200 Subject: [PATCH 22/36] feat: Add close method, to wrap up execution --- runcards/single_shot.py | 12 ++---------- src/qibocal/auto/execute.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/runcards/single_shot.py b/runcards/single_shot.py index edc154bf1..c33383545 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -5,20 +5,12 @@ executor = Executor.create(name="myexec", platform="dummy") -from myexec import init, single_shot_classification +from myexec import close, init, single_shot_classification path, output, meta, platform = init(Path("test_x"), force=True) completed = single_shot_classification(nshots=1000) -meta.end() - -# stop and disconnect platform -platform.disconnect() - -history = executor.history -# dump history, metadata, and updated platform -output.history = history -output.dump(path) +history = close(path, output, meta, platform) report(path, history) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index 733d0409d..afbdaace3 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -273,3 +273,15 @@ def init( meta.start() return path, output, meta, platform + + def close(self, path, output, meta, platform): + meta.end() + + # stop and disconnect platform + platform.disconnect() + + # dump history, metadata, and updated platform + output.history = self.history + output.dump(path) + + return self.history From a182fee04b000ee921c2e69126d4efac31eda1f0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:33:43 +0200 Subject: [PATCH 23/36] feat: Simplify init and close parameters and returned values --- runcards/single_shot.py | 8 ++++---- src/qibocal/auto/execute.py | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/runcards/single_shot.py b/runcards/single_shot.py index c33383545..602a58848 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -4,13 +4,13 @@ from qibocal.cli.report import report executor = Executor.create(name="myexec", platform="dummy") +path = Path("test_x") from myexec import close, init, single_shot_classification -path, output, meta, platform = init(Path("test_x"), force=True) +output, meta = init(path, force=True) completed = single_shot_classification(nshots=1000) -history = close(path, output, meta, platform) - -report(path, history) +close(path, output, meta) +report(path, executor.history) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index afbdaace3..915d603dd 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -272,16 +272,14 @@ def init( # run meta.start() - return path, output, meta, platform + return output, meta - def close(self, path, output, meta, platform): + def close(self, path, output, meta): meta.end() # stop and disconnect platform - platform.disconnect() + self.platform.disconnect() # dump history, metadata, and updated platform output.history = self.history output.dump(path) - - return self.history From 3d7c3f0c952e741526c744e4be131bb7370a6068 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:43:55 +0200 Subject: [PATCH 24/36] feat: Reduce init and close parameters to the minimal amount --- runcards/single_shot.py | 9 +++------ src/qibocal/auto/execute.py | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/runcards/single_shot.py b/runcards/single_shot.py index 602a58848..0025995fd 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -1,16 +1,13 @@ -from pathlib import Path - from qibocal.auto.execute import Executor from qibocal.cli.report import report executor = Executor.create(name="myexec", platform="dummy") -path = Path("test_x") from myexec import close, init, single_shot_classification -output, meta = init(path, force=True) +init("test_x", force=True) completed = single_shot_classification(nshots=1000) -close(path, output, meta) -report(path, executor.history) +close() +report(executor.path, executor.history) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index 915d603dd..ba2871976 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -89,6 +89,8 @@ class Executor: are also allowed, and they are interpreted relative to this package (in the top scope). """ + path: Optional[Path] = None + output: Optional[Output] = None def __post_init__(self): """Register as a module, if a name is specified.""" @@ -243,7 +245,7 @@ def __del__(self): def init( self, - folder: Path, + folder: os.PathLike, force: bool = False, platform: Optional[Platform] = None, targets: Optional[Targets] = None, @@ -259,7 +261,7 @@ def init( self.targets = targets # generate output folder - path = Output.mkdir(folder, force) + path = Output.mkdir(Path(folder), force) # generate meta meta = Metadata.generate(path.name, backend) @@ -272,14 +274,19 @@ def init( # run meta.start() - return output, meta + self.output = output + self.path = path - def close(self, path, output, meta): - meta.end() + def close(self, path: Optional[os.PathLike] = None): + assert self.output is not None and self.path is not None + + path = self.path if path is None else Path(path) + + self.output.meta.end() # stop and disconnect platform self.platform.disconnect() # dump history, metadata, and updated platform - output.history = self.history - output.dump(path) + self.output.history = self.history + self.output.dump(path) From c59df20ae08f758f96b0514cc78829cc71323344 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 18:57:17 +0200 Subject: [PATCH 25/36] test: Fix errors in example, switch to dummy --- runcards/rx_calibration.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/runcards/rx_calibration.py b/runcards/rx_calibration.py index ee67858b1..0bc19903c 100644 --- a/runcards/rx_calibration.py +++ b/runcards/rx_calibration.py @@ -7,11 +7,11 @@ from qibocal.auto.output import Metadata, Output from qibocal.cli.report import report -target = "D4" +target = 0 folder = Path("test_rx_calibration") force = True -backend = construct_backend(backend="qibolab", platform="qw11q") +backend = construct_backend(backend="qibolab", platform="dummy") platform = backend.platform if platform is None: raise ValueError("Qibocal requires a Qibolab platform to run.") @@ -47,11 +47,10 @@ # update only if chi2 is satisfied if rabi_output.results.chi2[target][0] > 2: - raise ( + raise RuntimeError( f"Rabi fit has chi2 {rabi_output.results.chi2[target][0]} greater than 2. Stopping." ) -else: - rabi_output.update_platform(platform) +rabi_output.update_platform(platform) ramsey_output = ramsey( delay_between_pulses_start=10, @@ -62,10 +61,11 @@ ) if ramsey_output.results.chi2[target][0] > 2: - raise ( + raise RuntimeError( f"Ramsey fit has chi2 {ramsey_output.results.chi2[target][0]} greater than 2. Stopping." ) -elif ramsey_output.results.delta_phys[target][0] < 1e4: + +if ramsey_output.results.delta_phys[target][0] < 1e4: print( f"Ramsey frequency not updated, correctio to small { ramsey_output.results.delta_phys[target][0]}" ) @@ -83,20 +83,18 @@ # update only if chi2 is satisfied if rabi_output_2.results.chi2[target][0] > 2: - raise ( + raise RuntimeError( f"Rabi fit has chi2 {rabi_output_2.results.chi2[target][0]} greater than 2. Stopping." ) -else: - rabi_output_2.update_platform(platform) +rabi_output_2.update_platform(platform) drag_output = drag_tuning(beta_start=-4, beta_end=4, beta_step=0.5) if drag_output.results.chi2[target][0] > 2: - raise ( + raise RuntimeError( f"Drag fit has chi2 {drag_output.results.chi2[target][0]} greater than 2. Stopping." ) -else: - drag_output.update_platform(platform) +drag_output.update_platform(platform) rabi_output_3 = rabi_amplitude( min_amp_factor=0.5, @@ -109,11 +107,10 @@ # update only if chi2 is satisfied if rabi_output_3.results.chi2[target][0] > 2: - raise ( + raise RuntimeError( f"Rabi fit has chi2 {rabi_output_3.results.chi2[target][0]} greater than 2. Stopping." ) -else: - rabi_output_3.update_platform(platform) +rabi_output_3.update_platform(platform) meta.end() From 468d96142d309377185a5c3e9532304206f4f05c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 19:10:28 +0200 Subject: [PATCH 26/36] test: Remove boilerplate from rx calibration as well --- runcards/rx_calibration.py | 59 +++++--------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/runcards/rx_calibration.py b/runcards/rx_calibration.py index 0bc19903c..114861d1f 100644 --- a/runcards/rx_calibration.py +++ b/runcards/rx_calibration.py @@ -1,41 +1,13 @@ -from pathlib import Path - -from qibo.backends import construct_backend - from qibocal.auto.execute import Executor -from qibocal.auto.history import History -from qibocal.auto.output import Metadata, Output from qibocal.cli.report import report -target = 0 -folder = Path("test_rx_calibration") -force = True - -backend = construct_backend(backend="qibolab", platform="dummy") -platform = backend.platform -if platform is None: - raise ValueError("Qibocal requires a Qibolab platform to run.") - -executor = Executor( - name="myexec", history=History(), platform=platform, targets=[target] -) +executor = Executor.create(name="myexec", platform="dummy") -# generate output folder -path = Output.mkdir(folder, force) - -# generate meta -meta = Metadata.generate(path.name, backend) -output = Output(History(), meta, platform) -output.dump(path) - -from myexec import drag_tuning, rabi_amplitude, ramsey - -# connect and initialize platform -platform.connect() - -# run -meta.start() +from myexec import init, close, drag_tuning, rabi_amplitude, ramsey +target = 0 +platform = executor.platform +init("test_rx_calibration", force=True, targets=[target]) rabi_output = rabi_amplitude( min_amp_factor=0.5, @@ -44,7 +16,6 @@ pulse_length=platform.qubits[target].native_gates.RX.duration, nshots=2048, ) - # update only if chi2 is satisfied if rabi_output.results.chi2[target][0] > 2: raise RuntimeError( @@ -59,12 +30,10 @@ detuning=1_000_000, nshots=2048, ) - if ramsey_output.results.chi2[target][0] > 2: raise RuntimeError( f"Ramsey fit has chi2 {ramsey_output.results.chi2[target][0]} greater than 2. Stopping." ) - if ramsey_output.results.delta_phys[target][0] < 1e4: print( f"Ramsey frequency not updated, correctio to small { ramsey_output.results.delta_phys[target][0]}" @@ -79,8 +48,6 @@ pulse_length=platform.qubits[target].native_gates.RX.duration, nshots=2048, ) - - # update only if chi2 is satisfied if rabi_output_2.results.chi2[target][0] > 2: raise RuntimeError( @@ -89,7 +56,6 @@ rabi_output_2.update_platform(platform) drag_output = drag_tuning(beta_start=-4, beta_end=4, beta_step=0.5) - if drag_output.results.chi2[target][0] > 2: raise RuntimeError( f"Drag fit has chi2 {drag_output.results.chi2[target][0]} greater than 2. Stopping." @@ -103,8 +69,6 @@ pulse_length=platform.qubits[target].native_gates.RX.duration, nshots=2048, ) - - # update only if chi2 is satisfied if rabi_output_3.results.chi2[target][0] > 2: raise RuntimeError( @@ -112,14 +76,5 @@ ) rabi_output_3.update_platform(platform) -meta.end() - -# stop and disconnect platform -platform.disconnect() - -history = executor.history -# dump history, metadata, and updated platform -output.history = history -output.dump(path) - -report(path, history) +close() +report(executor.path, executor.history) From bdd42c4269e455eb8d29b754cb324808e6c9a06a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 19:11:01 +0200 Subject: [PATCH 27/36] test: Set common number of shots just once --- runcards/rx_calibration.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/runcards/rx_calibration.py b/runcards/rx_calibration.py index 114861d1f..2c32ee983 100644 --- a/runcards/rx_calibration.py +++ b/runcards/rx_calibration.py @@ -3,10 +3,11 @@ executor = Executor.create(name="myexec", platform="dummy") -from myexec import init, close, drag_tuning, rabi_amplitude, ramsey +from myexec import close, drag_tuning, init, rabi_amplitude, ramsey target = 0 platform = executor.platform +platform.settings.nshots = 2048 init("test_rx_calibration", force=True, targets=[target]) rabi_output = rabi_amplitude( @@ -14,7 +15,6 @@ max_amp_factor=1.5, step_amp_factor=0.01, pulse_length=platform.qubits[target].native_gates.RX.duration, - nshots=2048, ) # update only if chi2 is satisfied if rabi_output.results.chi2[target][0] > 2: @@ -28,7 +28,6 @@ delay_between_pulses_end=5000, delay_between_pulses_step=100, detuning=1_000_000, - nshots=2048, ) if ramsey_output.results.chi2[target][0] > 2: raise RuntimeError( @@ -46,7 +45,6 @@ max_amp_factor=1.5, step_amp_factor=0.01, pulse_length=platform.qubits[target].native_gates.RX.duration, - nshots=2048, ) # update only if chi2 is satisfied if rabi_output_2.results.chi2[target][0] > 2: @@ -67,7 +65,6 @@ max_amp_factor=1.5, step_amp_factor=0.01, pulse_length=platform.qubits[target].native_gates.RX.duration, - nshots=2048, ) # update only if chi2 is satisfied if rabi_output_3.results.chi2[target][0] > 2: From 2b44c0f1aa57c677d3e3638952e529a73e9d75f0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 19:13:37 +0200 Subject: [PATCH 28/36] test: Fix typos in string --- runcards/rx_calibration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runcards/rx_calibration.py b/runcards/rx_calibration.py index 2c32ee983..ebb65e10b 100644 --- a/runcards/rx_calibration.py +++ b/runcards/rx_calibration.py @@ -35,7 +35,7 @@ ) if ramsey_output.results.delta_phys[target][0] < 1e4: print( - f"Ramsey frequency not updated, correctio to small { ramsey_output.results.delta_phys[target][0]}" + f"Ramsey frequency not updated, correction too small { ramsey_output.results.delta_phys[target][0]}" ) else: ramsey_output.update_platform(platform) From af68497518a1a260691778c233630295f6c74374 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 17 Jul 2024 20:14:08 +0200 Subject: [PATCH 29/36] fix: Store meta, not output --- src/qibocal/auto/execute.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index ba2871976..12e6aad5c 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -90,7 +90,7 @@ class Executor: scope). """ path: Optional[Path] = None - output: Optional[Output] = None + meta: Optional[Metadata] = None def __post_init__(self): """Register as a module, if a name is specified.""" @@ -245,48 +245,50 @@ def __del__(self): def init( self, - folder: os.PathLike, + path: os.PathLike, force: bool = False, - platform: Optional[Platform] = None, + platform: Union[Platform, str, None] = None, targets: Optional[Targets] = None, ): - if platform is not None: - self.platform = platform - else: + """Initialize execution.""" + if platform is None: platform = self.platform backend = construct_backend(backend="qibolab", platform=platform) + platform = self.platform = backend.platform + assert isinstance(platform, Platform) if targets is not None: self.targets = targets # generate output folder - path = Output.mkdir(Path(folder), force) + path = Output.mkdir(Path(path), force) # generate meta meta = Metadata.generate(path.name, backend) output = Output(History(), meta, platform) output.dump(path) - # connect and initialize platform - platform.connect() - # run meta.start() - self.output = output + # connect and initialize platform + platform.connect() + self.path = path + self.meta = meta def close(self, path: Optional[os.PathLike] = None): - assert self.output is not None and self.path is not None + """Close execution.""" + assert self.meta is not None and self.path is not None path = self.path if path is None else Path(path) - self.output.meta.end() - # stop and disconnect platform self.platform.disconnect() + self.meta.end() + # dump history, metadata, and updated platform - self.output.history = self.history - self.output.dump(path) + output = Output(self.history, self.meta) + output.dump(path) From 22ca90bd1e72c7656becb00da4a3dbaa8b9346fa Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 28 Jul 2024 19:27:57 +0200 Subject: [PATCH 30/36] test: Add init and close basic tests --- tests/test_executor.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_executor.py b/tests/test_executor.py index 51b4c0e4b..ebbd46991 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -106,8 +106,44 @@ def fake_protocols(request): return protocols +@pytest.fixture +def executor(): + executor = Executor.create("my-exec") + yield executor + executor.unload() + + @pytest.mark.protocols("ciao", "come") def test_simple(fake_protocols): globals_ = {} exec((SCRIPTS / "simple.py").read_text(), globals_) assert globals_["res"]._results.par[0] == 42 + + +def test_init(tmp_path: Path, executor: Executor): + path = tmp_path / "my-init-folder" + + init = executor.init + + init(path) + with pytest.raises(RuntimeError, match="Directory .* already exists"): + init(path) + + init(path, force=True) + + assert executor.meta is not None + assert executor.meta.start is not None + + +def test_close(tmp_path: Path, executor: Executor): + path = tmp_path / "my-close-folder" + + init = executor.init + close = executor.close + + init(path) + close() + + assert executor.meta is not None + assert executor.meta.start is not None + assert executor.meta.end is not None From 8959e0c97b956be1c12c5d4edb8a1f60ab7885cc Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 28 Jul 2024 19:44:57 +0200 Subject: [PATCH 31/36] fix: Attempt unloading while closing --- src/qibocal/auto/execute.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index 12e6aad5c..a33b34ced 100644 --- a/src/qibocal/auto/execute.py +++ b/src/qibocal/auto/execute.py @@ -292,3 +292,6 @@ def close(self, path: Optional[os.PathLike] = None): # dump history, metadata, and updated platform output = Output(self.history, self.meta) output.dump(path) + + # attempt unloading + self.__del__() From bf078977d25a02559052c277349a379e2f8b1b1d Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 28 Jul 2024 20:08:20 +0200 Subject: [PATCH 32/36] test: Convert executor unloading in a more humble attempt --- tests/test_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_executor.py b/tests/test_executor.py index ebbd46991..75e9313ff 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -110,7 +110,7 @@ def fake_protocols(request): def executor(): executor = Executor.create("my-exec") yield executor - executor.unload() + del executor @pytest.mark.protocols("ciao", "come") From 1efb11531fda58d73ca426828fad1e790dd45551 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 28 Jul 2024 21:48:27 +0200 Subject: [PATCH 33/36] test: Inline executor unloading attempt --- tests/test_executor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_executor.py b/tests/test_executor.py index 75e9313ff..84417978f 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -110,7 +110,11 @@ def fake_protocols(request): def executor(): executor = Executor.create("my-exec") yield executor - del executor + try: + executor.unload() + except KeyError: + # it has been explicitly unloaded, no need to do it again + pass @pytest.mark.protocols("ciao", "come") From 601b7863b7de6ceee13489bd3d1c7b1ab823414b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:42:15 +0000 Subject: [PATCH 34/36] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aecd05257..4e75b18e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - id: isort args: ["--profile", "black"] - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 + rev: v3.17.0 hooks: - id: pyupgrade - repo: https://github.com/hadialqattan/pycln From bcf44a4d943ed02c8b5288c2996fffbd3e17575e Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 31 Jul 2024 11:34:54 +0400 Subject: [PATCH 35/36] feat: Update correction for sweetspot --- .../flux_dependence/qubit_flux_dependence.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index f20b2597f..4bd8c1e6c 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from math import floor from typing import Optional import numpy as np @@ -218,10 +219,12 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - # just take maximum - sweetspot[qubit] = biases[ - np.argmax([fit_function(x, *popt) for x in biases]) - ] + # solution to x*popt[1] + popt[2] = k pi + # such that x is close to 0 + # to avoid errors due to periodicity + k = floor(popt[2] / np.pi + 0.5) + + sweetspot[qubit] = (k * np.pi + popt[2]) / popt[1] matrix_element[qubit] = popt[1] except ValueError as e: log.error( From 9ea76d4bed9417a99cb14f6514e749e36ec9a093 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 31 Jul 2024 13:08:42 +0400 Subject: [PATCH 36/36] feat: Use instead of floor --- src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index 4bd8c1e6c..d5a7db9fd 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from math import floor from typing import Optional import numpy as np @@ -222,7 +221,7 @@ def fit_function(x, w_max, normalization, offset): # solution to x*popt[1] + popt[2] = k pi # such that x is close to 0 # to avoid errors due to periodicity - k = floor(popt[2] / np.pi + 0.5) + k = np.round(popt[2] / np.pi) sweetspot[qubit] = (k * np.pi + popt[2]) / popt[1] matrix_element[qubit] = popt[1]