diff --git a/docs/releases/changelog-dev.md b/docs/releases/changelog-dev.md index 9445c5b68..e90265bb7 100644 --- a/docs/releases/changelog-dev.md +++ b/docs/releases/changelog-dev.md @@ -300,6 +300,9 @@ This document contains the changes of the current release. nodes: [qubit_2, resonator_q0, drive_line_q0, flux_line_q0] ``` +- `ql.execute` now accepts a list of circuits! + [#549](https://github.com/qilimanjaro-tech/qililab/pull/549) + ### Breaking changes - Old scripts using `Experiment` with circuits should be changed and use `CircuitExperiment` instead. diff --git a/src/qililab/execute_circuit.py b/src/qililab/execute_circuit.py index 20ca5d65a..541e094e5 100644 --- a/src/qililab/execute_circuit.py +++ b/src/qililab/execute_circuit.py @@ -14,23 +14,26 @@ """Execute function used to execute a qibo Circuit using the given runcard.""" from qibo.models import Circuit +from tqdm.auto import tqdm + +from qililab.result import Result from .data_management import build_platform -from .experiment.experiment import Experiment from .transpiler import translate_circuit -from .typings import ExperimentOptions, ExperimentSettings -def execute(circuit: Circuit, platform_path: str, nshots: int = 1): - """Execute a qibo with qililab and native gates +def execute(program: Circuit | list[Circuit], runcard: str | dict, nshots: int = 1) -> Result | list[Result]: + """Executes a Qibo circuit (or a list of circuits) with qililab and returns the results. Args: - circuit (Circuit): Qibo Circuit. - platform_path (str): Path to the YAML file containing the serialization of the Platform to be used. + circuit (Circuit | list[Circuit]): Qibo Circuit. + runcard (str | dict): If a string, path to the YAML file containing the serialization of the Platform to be + used. If a dictionary, the serialized platform to be used. nshots (int, optional): Number of shots to execute. Defaults to 1. Returns: - Results: ``Results`` class containing the experiment results + Result | list[Result]: :class:`Result` class (or list of :class:`Result` classes) containing the results of the + execution. Example Usage: @@ -54,20 +57,26 @@ def execute(circuit: Circuit, platform_path: str, nshots: int = 1): c.add(gates.SWAP(4,2)) c.add(gates.RX(1, 3*np.pi/2)) - probabilities = ql.execute(c, platform_path="./runcards/galadriel.yml") - - + probabilities = ql.execute(c, runcard="./runcards/galadriel.yml") """ - # transpile and optimize circuit - circuit = translate_circuit(circuit, optimize=True) - - # create platform - platform = build_platform(runcard=platform_path) - - settings = ExperimentSettings(hardware_average=1, repetition_duration=200000, software_average=1, num_bins=nshots) - options = ExperimentOptions(settings=settings) - - # create experiment with options - sample_experiment = Experiment(platform=platform, circuits=[circuit], options=options) - - return sample_experiment.execute(save_experiment=False, save_results=False) + if isinstance(program, Circuit): + program = [program] + + # Initialize platform and connect to the instruments + platform = build_platform(runcard=runcard) + platform.connect() + + try: + platform.initial_setup() + platform.turn_on_instruments() + results = [] + for circuit in tqdm(program, total=len(program)): + # Transpile and optimize circuit + native_circuit = translate_circuit(circuit, optimize=True) + # Execute circuit + results.append(platform.execute(native_circuit, num_avg=1, repetition_duration=200_000, num_bins=nshots)) + platform.disconnect() + return results[0] if len(results) == 1 else results + except Exception as e: + platform.disconnect() + raise e diff --git a/tests/test_execute_circuit.py b/tests/test_execute_circuit.py index bb65c809e..189ce9a73 100644 --- a/tests/test_execute_circuit.py +++ b/tests/test_execute_circuit.py @@ -1,6 +1,7 @@ """Test execute script""" from unittest.mock import MagicMock, patch +import pytest from qibo.gates import CNOT, RY, H, M, X from qibo.models import Circuit @@ -10,13 +11,52 @@ class TestExecute: """Test execute method in the `execute.py` script""" - @patch("qililab.experiment.experiment.Experiment.execute") - @patch("qililab.execute_circuit.build_platform") - def test_execute(self, mock_build: MagicMock, mock_execute: MagicMock): + def test_execute(self): n_qubits = 5 circuit = Circuit(n_qubits) circuit.add([X(1), H(2), RY(0, 2), CNOT(4, 1), X(4), H(3), M(*range(5))]) runcard = "galadriel.yml" - ql.execute(circuit, runcard) - mock_build.assert_called_with(runcard="galadriel.yml") - mock_execute.assert_called_once_with(save_experiment=False, save_results=False) + mock_platform = MagicMock() + with patch("qililab.execute_circuit.build_platform", return_value=mock_platform) as mock_build: + results = ql.execute(circuit, runcard) + assert isinstance(results, MagicMock) + mock_build.assert_called_with(runcard="galadriel.yml") + mock_platform.connect.assert_called_once_with() + mock_platform.initial_setup.assert_called_once_with() + mock_platform.turn_on_instruments.assert_called_once_with() + mock_platform.execute.assert_called_once() + mock_platform.disconnect.assert_called_once_with() + + def test_execute_calls_disconnect_after_error(self): + n_qubits = 5 + circuit = Circuit(n_qubits) + circuit.add([X(1), H(2), RY(0, 2), CNOT(4, 1), X(4), H(3), M(*range(5))]) + runcard = "galadriel.yml" + with pytest.raises(Exception): + mock_platform = MagicMock() + mock_platform.turn_on_instruments.side_effect = Exception() + with patch("qililab.execute_circuit.build_platform", return_value=mock_platform) as mock_build: + ql.execute(circuit, runcard) + mock_build.assert_called_with(runcard="galadriel.yml") + mock_platform.connect.assert_called_once_with() + mock_platform.initial_setup.assert_called_once_with() + mock_platform.turn_on_instruments.assert_called_once_with() + mock_platform.execute.assert_not_called() + mock_platform.disconnect.assert_called_once_with() + + def test_execute_circuit_with_multiple_circuits(self): + """Test that executing a list of circuits returns a list of results.""" + n_qubits = 5 + circuit = Circuit(n_qubits) + circuit.add([X(1), H(2), RY(0, 2), CNOT(4, 1), X(4), H(3), M(*range(5))]) + runcard = "galadriel.yml" + mock_platform = MagicMock() + with patch("qililab.execute_circuit.build_platform", return_value=mock_platform) as mock_build: + results = ql.execute([circuit, circuit], runcard) + assert isinstance(results, list) + mock_build.assert_called_with(runcard="galadriel.yml") + mock_platform.connect.assert_called_once_with() + mock_platform.initial_setup.assert_called_once_with() + mock_platform.turn_on_instruments.assert_called_once_with() + assert mock_platform.execute.call_count == 2 + mock_platform.disconnect.assert_called_once_with()