diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index 32378f236..4066e5ab7 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -218,7 +218,6 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her from qibolab import ( AcquisitionType, AveragingMode, - ExecutionParameters, Parameter, PulseSequence, Sweeper, @@ -242,14 +241,14 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her ) # perform the experiment using specific options - options = ExecutionParameters( + results = platform.execute( + [sequence], + [[sweeper]], nshots=1000, relaxation_time=50, averaging_mode=AveragingMode.CYCLIC, acquisition_type=AcquisitionType.INTEGRATION, ) - - results = platform.execute([sequence], options, [[sweeper]]) _, acq = next(iter(sequence.acquisitions)) # plot the results diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index a91381454..74fdec314 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -84,17 +84,16 @@ Now we can execute the sequence on hardware: from qibolab import ( AcquisitionType, AveragingMode, - ExecutionParameters, ) - options = ExecutionParameters( + options = dict( nshots=1000, relaxation_time=10, fast_reset=False, acquisition_type=AcquisitionType.INTEGRATION, averaging_mode=AveragingMode.CYCLIC, ) - results = platform.execute([ps], options=options) + results = platform.execute([ps], **options) Finally, we can stop instruments and close connections. @@ -356,11 +355,7 @@ A typical resonator spectroscopy experiment could be defined with: for qubit in platform.qubits.values() ] - results = platform.execute([sequence], options, [sweepers]) - -.. note:: - - options is an :class:`qibolab.ExecutionParameters` object, detailed in a separate section. + results = platform.execute([sequence], [sweepers], **options) In this way, we first define three parallel sweepers with an interval of 400 MHz (-200 MHz --- 200 MHz). The resulting probed frequency will then be: - for qubit 0: [3.8 GHz, 4.2 GHz] @@ -390,7 +385,7 @@ For example: pulses=[rx_pulse], ) - results = platform.execute([sequence], options, [[sweeper_freq], [sweeper_amp]]) + results = platform.execute([sequence], [[sweeper_freq], [sweeper_amp]], **options) Let's say that the RX pulse has, from the runcard, a frequency of 4.5 GHz and an amplitude of 0.3, the parameter space probed will be: @@ -408,15 +403,16 @@ while different lists define nested loops, with the first list corresponding to Execution Parameters -------------------- -In the course of several examples, you've encountered the ``options`` argument in function calls like: +In the course of several examples, you've encountered the ``**options`` argument in function calls like: .. testcode:: python - res = platform.execute([sequence], options=options) + res = platform.execute([sequence], **options) -Let's now delve into the details of the ``options`` parameter and understand its components. +Let's now delve into the details of the ``options`` and understand its parts. -The ``options`` parameter, represented by the :class:`qibolab.ExecutionParameters` class, is a vital element for every hardware execution. It encompasses essential information that tailors the execution to specific requirements: +The ``options`` extra arguments, is a vital element for every hardware execution. +It encompasses essential information that tailors the execution to specific requirements: - ``nshots``: Specifies the number of experiment repetitions. - ``relaxation_time``: Introduces a wait time between repetitions, measured in nanoseconds (ns). @@ -461,7 +457,10 @@ For example in ro_sequence = natives.MZ() sequence = natives.RX() | ro_sequence - options = ExecutionParameters( + + ro_pulse = ro_sequence[0][1] + result = platform.execute( + [sequence], nshots=1000, relaxation_time=10, fast_reset=False, @@ -469,14 +468,11 @@ For example in averaging_mode=AveragingMode.CYCLIC, ) - ro_pulse = ro_sequence[0][1] - result = platform.execute([sequence], options=options) - ``result`` will be a dictionary with a single key ``ro_pulse.id`` and an array of two elements, the averaged I and Q components of the integrated signal. -If instead, ``(AcquisitionType.INTEGRATION, AveragingMode.SINGLESHOT)`` was used, the array would have shape ``(options.nshots, 2)``, -while for ``(AcquisitionType.DISCRIMINATION, AveragingMode.SINGLESHOT)`` the shape would be ``(options.nshots,)`` with values 0 or 1. +If instead, ``(AcquisitionType.INTEGRATION, AveragingMode.SINGLESHOT)`` was used, the array would have shape ``(options["nshots"], 2)``, +while for ``(AcquisitionType.DISCRIMINATION, AveragingMode.SINGLESHOT)`` the shape would be ``(options["nshots"],)`` with values 0 or 1. The shape of the values of an integrated acquisition with two sweepers will be: @@ -493,7 +489,7 @@ The shape of the values of an integrated acquisition with two sweepers will be: range=(f0 - 200_000, f0 + 200_000, 1), channels=[qubit.probe], ) - shape = (options.nshots, len(sweeper1.values), len(sweeper2.values), 2) + shape = (options["nshots"], len(sweeper1.values), len(sweeper2.values), 2) .. _main_doc_compiler: diff --git a/doc/source/tutorials/calibration.rst b/doc/source/tutorials/calibration.rst index 8980ce694..e180b5ae1 100644 --- a/doc/source/tutorials/calibration.rst +++ b/doc/source/tutorials/calibration.rst @@ -34,7 +34,6 @@ This is done in the following script: from qibolab import ( AcquisitionType, AveragingMode, - ExecutionParameters, Parameter, PulseSequence, Sweeper, @@ -56,15 +55,15 @@ This is done in the following script: channels=[qubit.probe], ) - options = ExecutionParameters( + results = platform.execute( + [sequence], + [[sweeper]], nshots=1000, relaxation_time=50, averaging_mode=AveragingMode.CYCLIC, acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.execute([sequence], options, [[sweeper]]) - acq = sequence.acquisitions[0][1] signal = results[acq.id] amplitudes = np.abs(signal[..., 0] + 1j * signal[..., 1]) @@ -106,7 +105,6 @@ complex pulse sequence. Therefore with start with that: from qibolab import ( AcquisitionType, AveragingMode, - ExecutionParameters, Parameter, PulseSequence, Sweeper, @@ -130,15 +128,15 @@ complex pulse sequence. Therefore with start with that: channels=[qubit.drive], ) - options = ExecutionParameters( + results = platform.execute( + [sequence], + [[sweeper]], nshots=1000, relaxation_time=50, averaging_mode=AveragingMode.CYCLIC, acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.execute([sequence], options, [[sweeper]]) - acq = sequence.acquisitions[0][1] signal = results[acq.id] amplitudes = np.abs(signal[..., 0] + 1j * signal[..., 1]) @@ -198,7 +196,6 @@ and its impact on qubit states in the IQ plane. from qibolab import ( AcquisitionType, AveragingMode, - ExecutionParameters, Parameter, Sweeper, create_platform, @@ -216,15 +213,14 @@ and its impact on qubit states in the IQ plane. # create pulse sequence 2 one_sequence = natives.RX() | natives.MZ() - options = ExecutionParameters( + results = platform.execute( + [zero_sequence, one_sequence], nshots=1000, relaxation_time=50_000, averaging_mode=AveragingMode.SINGLESHOT, acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.execute([zero_sequence, one_sequence], options) - acq0 = zero_sequence.acquisitions[0][1] acq1 = one_sequence.acquisitions[0][1] diff --git a/doc/source/tutorials/pulses.rst b/doc/source/tutorials/pulses.rst index 0a3e49868..94a17e92d 100644 --- a/doc/source/tutorials/pulses.rst +++ b/doc/source/tutorials/pulses.rst @@ -44,7 +44,7 @@ we can execute the previously defined sequence using the ``execute`` method: .. testcode:: python - from qibolab import ExecutionParameters, create_platform + from qibolab import create_platform # Define platform and load specific runcard platform = create_platform("dummy") @@ -53,8 +53,7 @@ we can execute the previously defined sequence using the ``execute`` method: platform.connect() # Executes a pulse sequence. - options = ExecutionParameters(nshots=1000, relaxation_time=100) - results = platform.execute([sequence], options=options) + results = platform.execute([sequence], nshots=1000, relaxation_time=100) # Disconnect from the instruments platform.disconnect() diff --git a/src/qibolab/_core/backends.py b/src/qibolab/_core/backends.py index 577b5cc5b..f500228ce 100644 --- a/src/qibolab/_core/backends.py +++ b/src/qibolab/_core/backends.py @@ -8,7 +8,6 @@ from qibolab._version import __version__ as qibolab_version from .compilers import Compiler -from .execution_parameters import ExecutionParameters from .platform import Platform, create_platform from .platform.load import available_platforms @@ -103,10 +102,7 @@ def execute_circuit(self, circuit, initial_state=None, nshots=1000): self.platform.connect() - readout_ = self.platform.execute( - [sequence], - ExecutionParameters(nshots=nshots), - ) + readout_ = self.platform.execute([sequence], nshots=nshots) readout = {k: v for k, v in readout_.items()} self.platform.disconnect() @@ -148,7 +144,7 @@ def execute_circuits(self, circuits, initial_states=None, nshots=1000): self.platform.connect() - readout = self.platform.execute(sequences, ExecutionParameters(nshots=nshots)) + readout = self.platform.execute(sequences, nshots=nshots) self.platform.disconnect() diff --git a/src/qibolab/_core/execution_parameters.py b/src/qibolab/_core/execution_parameters.py index 54c751852..3cfcf1931 100644 --- a/src/qibolab/_core/execution_parameters.py +++ b/src/qibolab/_core/execution_parameters.py @@ -4,7 +4,7 @@ from .serialize import Model from .sweeper import ParallelSweepers -__all__ = ["AcquisitionType", "AveragingMode", "ExecutionParameters"] +__all__ = ["AcquisitionType", "AveragingMode"] class AcquisitionType(Enum): diff --git a/src/qibolab/_core/platform/platform.py b/src/qibolab/_core/platform/platform.py index d39031c7e..b20b0595d 100644 --- a/src/qibolab/_core/platform/platform.py +++ b/src/qibolab/_core/platform/platform.py @@ -227,8 +227,8 @@ def _execute( def execute( self, sequences: list[PulseSequence], - options: Optional[ExecutionParameters] = None, sweepers: Optional[list[ParallelSweepers]] = None, + **options, ) -> dict[PulseId, Result]: """Execute pulse sequences. @@ -241,7 +241,7 @@ def execute( .. testcode:: import numpy as np - from qibolab import ExecutionParameters, Parameter, PulseSequence, Sweeper, create_dummy + from qibolab import Parameter, PulseSequence, Sweeper, create_dummy platform = create_dummy() @@ -256,7 +256,7 @@ def execute( channels=[qubit.probe], ) ] - platform.execute([sequence], ExecutionParameters(), [sweeper]) + platform.execute([sequence], [sweeper]) """ if sweepers is None: sweepers = [] @@ -265,9 +265,7 @@ def execute( "The acquisitions' identifiers have to be unique across all sequences." ) - if options is None: - options = ExecutionParameters() - options = self.settings.fill(options) + options = self.settings.fill(ExecutionParameters(**options)) time = estimate_duration(sequences, options, sweepers) log.info(f"Minimal execution time: {time}") diff --git a/src/qibolab/_core/sweeper.py b/src/qibolab/_core/sweeper.py index a60b6e257..82780a196 100644 --- a/src/qibolab/_core/sweeper.py +++ b/src/qibolab/_core/sweeper.py @@ -54,7 +54,7 @@ class Sweeper(Model): .. testcode:: import numpy as np - from qibolab import ExecutionParameters, Parameter, PulseSequence, Sweeper, create_dummy + from qibolab import Parameter, PulseSequence, Sweeper, create_dummy platform = create_dummy() @@ -65,7 +65,7 @@ class Sweeper(Model): sweeper = Sweeper( parameter=Parameter.frequency, values=parameter_range, channels=[qubit.probe] ) - platform.execute([sequence], ExecutionParameters(), [[sweeper]]) + platform.execute([sequence], [[sweeper]]) Args: parameter: parameter to be swept, possible choices are frequency, attenuation, amplitude, current and gain. diff --git a/tests/conftest.py b/tests/conftest.py index c4e855162..6996d50fa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,13 +6,7 @@ import numpy.typing as npt import pytest -from qibolab import ( - AcquisitionType, - AveragingMode, - ExecutionParameters, - Platform, - create_platform, -) +from qibolab import AcquisitionType, AveragingMode, Platform, create_platform from qibolab._core.platform.load import PLATFORMS from qibolab._core.sequence import PulseSequence from qibolab._core.sweeper import ParallelSweepers, Parameter, Sweeper @@ -114,7 +108,7 @@ def wrapped( sequence: Optional[PulseSequence] = None, target: Optional[int] = None, ) -> npt.NDArray: - options = ExecutionParameters( + options = dict( nshots=nshots, acquisition_type=acquisition_type, averaging_mode=averaging_mode, @@ -124,8 +118,8 @@ def wrapped( natives = connected_platform.natives.single_qubit[0] if sequence is None: - qd_seq = natives.RX.create_sequence() - probe_seq = natives.MZ.create_sequence() + qd_seq = natives.RX() + probe_seq = natives.MZ() probe_pulse = probe_seq[0][1].probe acq = probe_seq[0][1].acquisition wrapped.acquisition_duration = acq.duration @@ -151,7 +145,7 @@ def wrapped( assert target is not None assert sweepers is not None - results = connected_platform.execute([sequence], options, sweepers) + results = connected_platform.execute([sequence], sweepers, **options) return results[target] return wrapped diff --git a/tests/integration/test_sequence.py b/tests/integration/test_sequence.py index 4bb709a00..6f216f125 100644 --- a/tests/integration/test_sequence.py +++ b/tests/integration/test_sequence.py @@ -1,5 +1,4 @@ from qibolab import create_platform -from qibolab._core.execution_parameters import ExecutionParameters from qibolab._core.pulses import Delay @@ -33,7 +32,7 @@ def test_sequence_creation(): # ---------------------------------------------------- nshots = 17 - res = platform.execute([seq], ExecutionParameters(nshots=nshots)) + res = platform.execute([seq], nshots=nshots) for r in res.values(): assert r.shape == (nshots,) diff --git a/tests/test_dummy.py b/tests/test_dummy.py index 02ac4fcb5..75e533bba 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -1,6 +1,6 @@ import pytest -from qibolab import AcquisitionType, ExecutionParameters, create_platform +from qibolab import AcquisitionType, create_platform from qibolab._core.platform.platform import Platform from qibolab._core.pulses import Delay, GaussianSquare, Pulse from qibolab._core.sequence import PulseSequence @@ -29,8 +29,7 @@ def test_dummy_execute_coupler_pulse(platform: Platform): ) sequence.append((channel, pulse)) - options = ExecutionParameters(nshots=None) - _ = platform.execute([sequence], options) + _ = platform.execute([sequence], nshots=None) def test_dummy_execute_pulse_sequence_couplers(): @@ -38,15 +37,14 @@ def test_dummy_execute_pulse_sequence_couplers(): sequence = PulseSequence() natives = platform.natives - cz = natives.two_qubit[(1, 2)].CZ.create_sequence() + cz = natives.two_qubit[(1, 2)].CZ() sequence.concatenate(cz) sequence.append((platform.qubits[0].probe, Delay(duration=40))) sequence.append((platform.qubits[2].probe, Delay(duration=40))) - sequence.concatenate(natives.single_qubit[0].MZ.create_sequence()) - sequence.concatenate(natives.single_qubit[2].MZ.create_sequence()) - options = ExecutionParameters(nshots=None) - _ = platform.execute([sequence], options) + sequence.concatenate(natives.single_qubit[0].MZ()) + sequence.concatenate(natives.single_qubit[2].MZ()) + _ = platform.execute([sequence], nshots=None) @pytest.mark.parametrize( @@ -61,9 +59,8 @@ def test_dummy_execute_pulse_sequence_unrolling( natives = platform.natives sequences = [] for _ in range(nsequences): - sequences.append(natives.single_qubit[0].MZ.create_sequence()) - options = ExecutionParameters(nshots=nshots, acquisition_type=acquisition) - result = platform.execute(sequences, options) + sequences.append(natives.single_qubit[0].MZ()) + result = platform.execute(sequences, nshots=nshots, acquisition_type=acquisition) assert len(next(iter(result.values()))) == nshots for r in result.values(): if acquisition is AcquisitionType.INTEGRATION: diff --git a/tests/test_platform.py b/tests/test_platform.py index dbcd149bb..b6bb417e9 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -17,7 +17,6 @@ from qibolab._core.components import AcquisitionConfig, IqConfig, OscillatorConfig from qibolab._core.dummy import create_dummy from qibolab._core.dummy.platform import FOLDER -from qibolab._core.execution_parameters import ExecutionParameters from qibolab._core.native import SingleQubitNatives, TwoQubitNatives from qibolab._core.parameters import NativeGates, Parameters, update_configs from qibolab._core.platform import Platform @@ -135,9 +134,8 @@ def test_duplicated_acquisition(): platform = create_platform("dummy") sequence = platform.natives.single_qubit[0].MZ.create_sequence() - options = ExecutionParameters(nshots=None) with pytest.raises(ValueError, match="unique"): - _ = platform.execute([sequence, sequence.copy()], options) + _ = platform.execute([sequence, sequence.copy()]) def test_update_configs(platform): @@ -246,9 +244,7 @@ def test_platform_execute_empty(qpu_platform): # an empty pulse sequence platform = qpu_platform sequence = PulseSequence() - result = platform.execute_pulse_sequence( - sequence, ExecutionParameters(nshots=nshots) - ) + result = platform.execute([sequence], nshots=nshots) assert result is not None @@ -265,9 +261,7 @@ def test_platform_execute_one_drive_pulse(qpu_platform): ) ] ) - result = platform.execute_pulse_sequence( - sequence, ExecutionParameters(nshots=nshots) - ) + result = platform.execute([sequence], nshots=nshots) assert result is not None @@ -286,9 +280,7 @@ def test_platform_execute_one_coupler_pulse(qpu_platform): ) ] ) - result = platform.execute_pulse_sequence( - sequence, ExecutionParameters(nshots=nshots) - ) + result = platform.execute([sequence], nshots=nshots) assert result is not None @@ -305,9 +297,7 @@ def test_platform_execute_one_flux_pulse(qpu_platform): ) ] ) - result = platform.execute_pulse_sequence( - sequence, ExecutionParameters(nshots=nshots) - ) + result = platform.execute([sequence], nshots=nshots) assert result is not None @@ -318,8 +308,7 @@ def test_platform_execute_one_long_drive_pulse(qpu_platform): qubit = next(iter(platform.qubits.values())) pulse = Pulse(duration=8192 + 200, amplitude=0.12, envelope=Gaussian(5)) sequence = PulseSequence([(qubit.drive, pulse)]) - options = ExecutionParameters(nshots=nshots) - platform.execute_pulse_sequence(sequence, options) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -329,8 +318,7 @@ def test_platform_execute_one_extralong_drive_pulse(qpu_platform): qubit = next(iter(platform.qubits.values())) pulse = Pulse(duration=2 * 8192 + 200, amplitude=0.12, envelope=Gaussian(0.2)) sequence = PulseSequence([(qubit.drive, pulse)]) - options = ExecutionParameters(nshots=nshots) - platform.execute_pulse_sequence(sequence, options) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -342,7 +330,7 @@ def test_platform_execute_one_drive_one_readout(qpu_platform): sequence.concatenate(platform.create_RX_pulse(qubit_id)) sequence.append((qubit.probe, Delay(duration=200))) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots)) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -358,7 +346,7 @@ def test_platform_execute_multiple_drive_pulses_one_readout(qpu_platform): sequence.concatenate(platform.create_RX_pulse(qubit_id)) sequence.append((qubit.probe, Delay(duration=808))) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots)) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -375,7 +363,7 @@ def test_platform_execute_multiple_drive_pulses_one_readout_no_spacing( sequence.concatenate(platform.create_RX_pulse(qubit_id)) sequence.append((qubit.probe, Delay(duration=800))) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots)) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -394,7 +382,7 @@ def test_platform_execute_multiple_overlaping_drive_pulses_one_readout( ] ) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots)) + platform.execute([sequence], nshots=nshots) @pytest.mark.qpu @@ -414,7 +402,7 @@ def test_platform_execute_multiple_readout_pulses(qpu_platform): sequence.concatenate(qd_seq2) sequence.append((qubit.probe, Delay(duration=qd_seq2.duration))) sequence.concatenate(ro_seq2) - platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=nshots)) + platform.execute([sequence], nshots=nshots) @pytest.mark.skip(reason="no way of currently testing this") @@ -430,7 +418,7 @@ def test_excited_state_probabilities_pulses(qpu_platform): sequence.concatenate(platform.create_RX_pulse(qubit_id)) sequence.append((qubit.probe, Delay(duration=sequence.duration))) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - result = platform.execute([sequence], ExecutionParameters(nshots=5000)) + result = platform.execute([sequence], nshots=5000) nqubits = len(platform.qubits) cr = CircuitResult(backend, Circuit(nqubits), result, nshots=5000) @@ -465,7 +453,7 @@ def test_ground_state_probabilities_pulses(qpu_platform, start_zero): ) ) sequence.concatenate(platform.create_MZ_pulse(qubit_id)) - result = platform.execute_pulse_sequence(sequence, ExecutionParameters(nshots=5000)) + result = platform.execute([sequence], nshots=5000) nqubits = len(platform.qubits) cr = CircuitResult(backend, Circuit(nqubits), result, nshots=5000)