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 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..4f49e7223 100644 --- a/flake.nix +++ b/flake.nix @@ -1,62 +1,54 @@ { 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 = {config, ...}: { + packages = with pkgs; [poethepoet pre-commit stdenv.cc.cc.lib]; - modules = [ - { - packages = with pkgs; [pre-commit poethepoet]; + env = { + QIBOLAB_PLATFORMS = (dirOf config.env.DEVENV_ROOT) + "/qibolab_platforms_qrc"; + }; - languages.python = { + languages = { + python = { + enable = true; + libraries = with pkgs; [zlib]; + 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="; 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/rx_calibration.py b/runcards/rx_calibration.py index ee67858b1..ebb65e10b 100644 --- a/runcards/rx_calibration.py +++ b/runcards/rx_calibration.py @@ -1,73 +1,41 @@ -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 = "D4" -folder = Path("test_rx_calibration") -force = True - -backend = construct_backend(backend="qibolab", platform="qw11q") -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] -) - -# generate output folder -path = Output.mkdir(folder, force) +executor = Executor.create(name="myexec", platform="dummy") -# 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 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( min_amp_factor=0.5, 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: - 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, 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 ( + 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]}" + f"Ramsey frequency not updated, correction too small { ramsey_output.results.delta_phys[target][0]}" ) else: ramsey_output.update_platform(platform) @@ -77,52 +45,33 @@ 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: - 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, 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: - 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) - -meta.end() - -# stop and disconnect platform -platform.disconnect() - -history = executor.history -# dump history, metadata, and updated platform -output.history = history -output.dump(path) +rabi_output_3.update_platform(platform) -report(path, history) +close() +report(executor.path, executor.history) diff --git a/runcards/single_shot.py b/runcards/single_shot.py index 5314f8ea7..0025995fd 100644 --- a/runcards/single_shot.py +++ b/runcards/single_shot.py @@ -1,48 +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="qw11q") -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"]) - -# generate output folder -path = Output.mkdir(folder, force) +executor = Executor.create(name="myexec", platform="dummy") -# generate meta -meta = Metadata.generate(path.name, backend) -output = Output(History(), meta, platform) -output.dump(path) +from myexec import close, init, single_shot_classification -from myexec import single_shot_classification - -# connect and initialize platform -platform.connect() - -# run -meta.start() +init("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) - -report(path, history) +close() +report(executor.path, executor.history) diff --git a/src/qibocal/auto/execute.py b/src/qibocal/auto/execute.py index 664538685..a33b34ced 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" @@ -86,12 +89,55 @@ class Executor: are also allowed, and they are interpreted relative to this package (in the top scope). """ + path: Optional[Path] = None + meta: Optional[Metadata] = None def __post_init__(self): """Register as a module, if a name is specified.""" 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 +222,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: @@ -237,3 +242,56 @@ def __del__(self): except KeyError: # it has been explicitly unloaded, no need to do it again pass + + def init( + self, + path: os.PathLike, + force: bool = False, + platform: Union[Platform, str, None] = None, + targets: Optional[Targets] = None, + ): + """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(path), force) + + # generate meta + meta = Metadata.generate(path.name, backend) + output = Output(History(), meta, platform) + output.dump(path) + + # run + meta.start() + + # connect and initialize platform + platform.connect() + + self.path = path + self.meta = meta + + def close(self, path: Optional[os.PathLike] = 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) + + # stop and disconnect platform + self.platform.disconnect() + + self.meta.end() + + # dump history, metadata, and updated platform + output = Output(self.history, self.meta) + output.dump(path) + + # attempt unloading + self.__del__() 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() diff --git a/src/qibocal/auto/transpile.py b/src/qibocal/auto/transpile.py index 72a7e1a48..63f09464d 100644 --- a/src/qibocal/auto/transpile.py +++ b/src/qibocal/auto/transpile.py @@ -4,17 +4,57 @@ 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 transpile_circuits( + circuits: list[Circuit], + qubit_maps: list[list[QubitId]], + backend: Backend, + transpiler: Optional[Passes], +): + """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 + `qubit_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, ): - """ + """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. @@ -23,29 +63,27 @@ 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 ) def execute_transpiled_circuit( circuit: Circuit, - qubit_map: list[int], + qubit_map: list[QubitId], backend: Backend, + transpiler: Optional[Passes], initial_state=None, nshots=1000, - transpiler: Optional[Passes] = None, ): - """ + """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. @@ -54,12 +92,13 @@ def execute_transpiled_circuit( For the qubit map look :func:`dummy_transpiler`. This function returns the transpiled circuit and the execution results. """ - 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 ) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index 303d19d54..d5a7db9fd 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -218,8 +218,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 - # sweetspot obtain by solving transmon_frequency == w_max - sweetspot[qubit] = -popt[2] / popt[1] + # solution to x*popt[1] + popt[2] = k pi + # such that x is close to 0 + # to avoid errors due to periodicity + k = np.round(popt[2] / np.pi) + + sweetspot[qubit] = (k * np.pi + popt[2]) / popt[1] matrix_element[qubit] = popt[1] except ValueError as e: log.error( 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: 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/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 6ec8b5186..35e17fb11 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 ) @@ -510,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: @@ -527,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 ) @@ -572,7 +577,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, (str, int)): nqubits = 1 rb_gen_layer = rb_gen.layer_gen_single_qubit() elif isinstance(target, Tuple): # Tuple for qubit pair 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) diff --git a/src/qibocal/protocols/utils.py b/src/qibocal/protocols/utils.py index 22833d302..6fa4ebd33 100644 --- a/src/qibocal/protocols/utils.py +++ b/src/qibocal/protocols/utils.py @@ -771,7 +771,7 @@ 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] diff --git a/tests/test_executor.py b/tests/test_executor.py index 51b4c0e4b..84417978f 100644 --- a/tests/test_executor.py +++ b/tests/test_executor.py @@ -106,8 +106,48 @@ def fake_protocols(request): return protocols +@pytest.fixture +def executor(): + executor = Executor.create("my-exec") + yield executor + try: + executor.unload() + except KeyError: + # it has been explicitly unloaded, no need to do it again + pass + + @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 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)