Skip to content

Commit

Permalink
Improve the execute function. (#549)
Browse files Browse the repository at this point in the history
* Improve the execute function.

* Improve the execute function.

* style: 🎨 Remove unused imports

* docs: 📝 Improve comments.

* docs: 📝 Improve docstring.

* docs: 📝 Add changelog entry.

* test: 🧪 Fix test.

* test: 🧪 Add test.

* test: 🧪 Fix tests.

* test: 🧪 Add tests.

* fix: 🐛 Small fix.
  • Loading branch information
AlbertMitjans authored Oct 3, 2023
1 parent 698d509 commit a019cc7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 29 deletions.
3 changes: 3 additions & 0 deletions docs/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
55 changes: 32 additions & 23 deletions src/qililab/execute_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
52 changes: 46 additions & 6 deletions tests/test_execute_circuit.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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()

0 comments on commit a019cc7

Please sign in to comment.