diff --git a/README.md b/README.md index c2e1bcf88..df04418f4 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ platform.connect() # Execute a pulse sequence options = ExecutionParameters(nshots=1000) -results = platform.execute_pulse_sequence(sequence, options) +results = platform.execute([sequence], options) # Print the acquired shots print(results.samples) diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index 51b7e24f7..6d2751751 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -208,10 +208,10 @@ We leave to the dedicated tutorial a full explanation of the experiment, but her acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) # plot the results - amplitudes = results[ro_pulse.id].magnitude + amplitudes = results[ro_pulse.id][0].magnitude frequencies = np.arange(-2e8, +2e8, 1e6) + ro_pulse.frequency plt.title("Resonator Spectroscopy") diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index 3903108da..50536e71e 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -15,7 +15,7 @@ In the platform, the main methods can be divided in different sections: - functions save and change qubit parameters (``dump``, ``update``) - functions to coordinate the instruments (``connect``, ``setup``, ``disconnect``) -- functions to execute experiments (``execute_pulse_sequence``, ``execute_pulse_sequences``, ``sweep``) +- a unique interface to execute experiments (``execute``) - functions to initialize gates (``create_RX90_pulse``, ``create_RX_pulse``, ``create_CZ_pulse``, ``create_MZ_pulse``, ``create_qubit_drive_pulse``, ``create_qubit_readout_pulse``, ``create_RX90_drag_pulse``, ``create_RX_drag_pulse``) - setters and getters of channel/qubit parameters (local oscillator parameters, attenuations, gain and biases) @@ -86,7 +86,7 @@ Now we can execute the sequence on hardware: acquisition_type=AcquisitionType.INTEGRATION, averaging_mode=AveragingMode.CYCLIC, ) - results = platform.execute_pulse_sequence(ps, options=options) + results = platform.execute([ps], options=options) Finally, we can stop instruments and close connections. @@ -390,7 +390,7 @@ When conducting experiments on quantum hardware, pulse sequences are vital. Assu .. testcode:: python - result = platform.execute_pulse_sequence(sequence, options=options) + result = platform.execute([sequence], options=options) Lastly, when conducting an experiment, it is not always required to define a pulse from scratch. Usual pulses, such as pi-pulses or measurements, are already defined in the platform runcard and can be easily initialized with platform methods. @@ -415,7 +415,7 @@ Typical experiments may include both pre-defined pulses and new ones: ) sequence.append(platform.create_MZ_pulse(0)) - results = platform.execute_pulse_sequence(sequence, options=options) + results = platform.execute([sequence], options=options) .. note:: @@ -496,7 +496,7 @@ A tipical resonator spectroscopy experiment could be defined with: type=SweeperType.OFFSET, ) - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) .. note:: @@ -543,7 +543,7 @@ For example: type=SweeperType.FACTOR, ) - results = platform.sweep(sequence, options, sweeper_freq, sweeper_amp) + results = platform.execute([sequence], options, sweeper_freq, sweeper_amp) 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: @@ -562,8 +562,7 @@ In the course of several examples, you've encountered the ``options`` argument i .. testcode:: python - res = platform.execute_pulse_sequence(sequence, options=options) - res = platform.sweep(sequence, options=options) + res = platform.execute([sequence], options=options) Let's now delve into the details of the ``options`` parameter and understand its components. @@ -636,7 +635,7 @@ Let's now delve into a typical use case for result objects within the qibolab fr averaging_mode=AveragingMode.CYCLIC, ) - res = platform.execute_pulse_sequence(sequence, options=options) + res = platform.execute([sequence], options=options) The ``res`` object will manifest as a dictionary, mapping the measurement pulse serial to its corresponding results. @@ -734,10 +733,8 @@ Instruments all implement a set of methods: - setup - disconnect -While the controllers, the main instruments in a typical setup, add other two methods: - -- execute_pulse_sequence -- sweep +While the controllers, the main instruments in a typical setup, add another, i.e. +execute. Some more detail on the interal functionalities of instruments is given in :doc:`/tutorials/instrument` diff --git a/doc/source/tutorials/calibration.rst b/doc/source/tutorials/calibration.rst index ba8a2fee6..1588ad654 100644 --- a/doc/source/tutorials/calibration.rst +++ b/doc/source/tutorials/calibration.rst @@ -65,7 +65,7 @@ We then define the execution parameters and launch the experiment. acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) In few seconds, the experiment will be finished and we can proceed to plot it. @@ -73,7 +73,7 @@ In few seconds, the experiment will be finished and we can proceed to plot it. import matplotlib.pyplot as plt - amplitudes = results[readout_pulse.id].magnitude + amplitudes = results[readout_pulse.id][0].magnitude frequencies = np.arange(-2e8, +2e8, 1e6) + readout_pulse.frequency plt.title("Resonator Spectroscopy") @@ -153,9 +153,9 @@ We can now proceed to launch on hardware: acquisition_type=AcquisitionType.INTEGRATION, ) - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) - amplitudes = results[readout_pulse.id].magnitude + amplitudes = results[readout_pulse.id][0].magnitude frequencies = np.arange(-2e8, +2e8, 1e6) + drive_pulse.frequency plt.title("Resonator Spectroscopy") @@ -239,20 +239,20 @@ and its impact on qubit states in the IQ plane. acquisition_type=AcquisitionType.INTEGRATION, ) - results_one = platform.execute_pulse_sequence(one_sequence, options) - results_zero = platform.execute_pulse_sequence(zero_sequence, options) + results_one = platform.execute([one_sequence], options) + results_zero = platform.execute([zero_sequence], options) plt.title("Single shot classification") plt.xlabel("I [a.u.]") plt.ylabel("Q [a.u.]") plt.scatter( - results_one[readout_pulse1.id].voltage_i, - results_one[readout_pulse1.id].voltage_q, + results_one[readout_pulse1.id][0].voltage_i, + results_one[readout_pulse1.id][0].voltage_q, label="One state", ) plt.scatter( - results_zero[readout_pulse2.id].voltage_i, - results_zero[readout_pulse2.id].voltage_q, + results_zero[readout_pulse2.id][0].voltage_i, + results_zero[readout_pulse2.id][0].voltage_q, label="Zero state", ) plt.show() diff --git a/doc/source/tutorials/pulses.rst b/doc/source/tutorials/pulses.rst index b3b0b7233..0b336addf 100644 --- a/doc/source/tutorials/pulses.rst +++ b/doc/source/tutorials/pulses.rst @@ -65,7 +65,7 @@ pulse sequence according to the number of shots ``nshots`` specified. # Executes a pulse sequence. options = ExecutionParameters(nshots=1000, relaxation_time=100) - results = platform.execute_pulse_sequence(sequence, options=options) + results = platform.execute([sequence], options=options) # Disconnect from the instruments platform.disconnect() diff --git a/examples/minimum_working_example.py b/examples/minimum_working_example.py index 40ced664c..58c8f367a 100644 --- a/examples/minimum_working_example.py +++ b/examples/minimum_working_example.py @@ -38,7 +38,7 @@ # Connects to lab instruments using the details specified in the calibration settings. platform.connect() # Executes a pulse sequence. -results = platform.execute_pulse_sequence(sequence, nshots=3000) +results = platform.execute([sequence], nshots=3000) print(f"results (amplitude, phase, i, q): {results}") # Disconnect from the instruments platform.disconnect() diff --git a/examples/qibolab_v017_1Q_emulator_test_QuTiP.ipynb b/examples/qibolab_v017_1Q_emulator_test_QuTiP.ipynb index fda85d07b..d72c27c56 100644 --- a/examples/qibolab_v017_1Q_emulator_test_QuTiP.ipynb +++ b/examples/qibolab_v017_1Q_emulator_test_QuTiP.ipynb @@ -108,7 +108,7 @@ "\n", "# Execute the pulse sequence and save the output\n", "options = ExecutionParameters(nshots=1000)\n", - "results = emulator_platform.execute_pulse_sequence(sequence, options=options)" + "results = emulator_platform.execute([sequence], options=options)" ] }, { @@ -454,7 +454,7 @@ "id": "08e1c8ee-8076-47ee-a05a-bcc068d681d0", "metadata": {}, "source": [ - "The simulation results generated by the simulation engine are returned together with the usual outputs of `execute_pulse_sequence` for device platforms and are grouped under 'simulation'. \n", + "The simulation results generated by the simulation engine are returned together with the usual outputs of `execute` for device platforms and are grouped under 'simulation'. \n", "\n", "Let us retrieve the simulation results obtained previously:" ] diff --git a/src/qibolab/backends.py b/src/qibolab/backends.py index fc288fb6a..e3016e7b5 100644 --- a/src/qibolab/backends.py +++ b/src/qibolab/backends.py @@ -102,10 +102,11 @@ def execute_circuit(self, circuit, initial_state=None, nshots=1000): if not self.platform.is_connected: self.platform.connect() - readout = self.platform.execute_pulse_sequence( - sequence, + readout_ = self.platform.execute( + [sequence], ExecutionParameters(nshots=nshots), ) + readout = {k: v[0] for k, v in readout_.items()} self.platform.disconnect() @@ -147,10 +148,7 @@ def execute_circuits(self, circuits, initial_states=None, nshots=1000): if not self.platform.is_connected: self.platform.connect() - readout = self.platform.execute_pulse_sequences( - sequences, - ExecutionParameters(nshots=nshots), - ) + readout = self.platform.execute(sequences, ExecutionParameters(nshots=nshots)) self.platform.disconnect() diff --git a/src/qibolab/instruments/abstract.py b/src/qibolab/instruments/abstract.py index bdd44e66c..c42bbdd49 100644 --- a/src/qibolab/instruments/abstract.py +++ b/src/qibolab/instruments/abstract.py @@ -97,17 +97,11 @@ def ports(self, port_name, *args, **kwargs): def play(self, *args, **kwargs): """Play a pulse sequence and retrieve feedback. - Returns: - (Dict[ResultType]) mapping the serial of the readout pulses used to - the acquired :class:`qibolab.result.ExecutionResults` object. - """ - - @abstractmethod - def sweep(self, *args, **kwargs): - """Play a pulse sequence while sweeping one or more parameters. + If :cls:`qibolab.sweeper.Sweeper` objects are passed as arguments, they are + executed in real-time. If not possible, an error is raised. Returns: - (dict) mapping the serial of the readout pulses used to + (Dict[ResultType]) mapping the serial of the readout pulses used to the acquired :class:`qibolab.result.ExecutionResults` object. """ diff --git a/src/qibolab/instruments/dummy.py b/src/qibolab/instruments/dummy.py index bdbdda8e5..cc67b56d8 100644 --- a/src/qibolab/instruments/dummy.py +++ b/src/qibolab/instruments/dummy.py @@ -120,27 +120,6 @@ def play( couplers: Dict[QubitId, Coupler], sequence: PulseSequence, options: ExecutionParameters, - ): - exp_points = ( - 1 if options.averaging_mode is AveragingMode.CYCLIC else options.nshots - ) - shape = (exp_points,) - results = {} - - for ro_pulse in sequence.ro_pulses: - values = np.squeeze(self.get_values(options, ro_pulse, shape)) - results[ro_pulse.qubit] = results[ro_pulse.id] = options.results_type( - values - ) - - return results - - def sweep( - self, - qubits: Dict[QubitId, Qubit], - couplers: Dict[QubitId, Coupler], - sequence: PulseSequence, - options: ExecutionParameters, *sweepers: List[Sweeper], ): results = {} diff --git a/src/qibolab/platform/platform.py b/src/qibolab/platform/platform.py index 597c9632a..3dd122847 100644 --- a/src/qibolab/platform/platform.py +++ b/src/qibolab/platform/platform.py @@ -2,7 +2,7 @@ from collections import defaultdict from dataclasses import dataclass, field, fields -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple import networkx as nx from qibo.config import log, raise_error @@ -214,60 +214,66 @@ def disconnect(self): instrument.disconnect() self.is_connected = False - def _execute(self, sequence, options, **kwargs): + @property + def _controller(self): + """Identify controller instrument. + + Used for splitting the unrolled sequences to batches. + + This method does not support platforms with more than one + controller instruments. + """ + controllers = [ + instr + for instr in self.instruments.values() + if isinstance(instr, Controller) + ] + assert len(controllers) == 1 + return controllers[0] + + def _execute(self, sequence, options, *sweepers): """Executes sequence on the controllers.""" result = {} for instrument in self.instruments.values(): if isinstance(instrument, Controller): new_result = instrument.play( - self.qubits, self.couplers, sequence, options + self.qubits, self.couplers, sequence, options, *sweepers ) if isinstance(new_result, dict): result.update(new_result) return result - def execute_pulse_sequence( - self, sequence: PulseSequence, options: ExecutionParameters, **kwargs - ): - """ - Args: - sequence (:class:`qibolab.pulses.PulseSequence`): Pulse sequences to execute. - options (:class:`qibolab.platforms.platform.ExecutionParameters`): Object holding the execution options. - **kwargs: May need them for something - Returns: - Readout results acquired by after execution. - """ - options = self.settings.fill(options) + def execute( + self, + sequences: List[PulseSequence], + options: ExecutionParameters, + *sweepers: Sweeper, + ) -> dict[Any, list]: + """Execute a pulse sequences. - time = ( - (sequence.duration + options.relaxation_time) * options.nshots * NS_TO_SEC - ) - log.info(f"Minimal execution time (sequence): {time}") + If any sweeper is passed, the execution is performed for the different values of sweeped parameters. - return self._execute(sequence, options, **kwargs) + Example: + .. testcode:: - @property - def _controller(self): - """Controller instrument used for splitting the unrolled sequences to - batches. + import numpy as np + from qibolab.dummy import create_dummy + from qibolab.sweeper import Sweeper, Parameter + from qibolab.pulses import PulseSequence + from qibolab.execution_parameters import ExecutionParameters - Used only by :meth:`qibolab.platform.Platform.execute_pulse_sequences` (unrolling). - This method does not support platforms with more than one controller instruments. - """ - controllers = [ - instr - for instr in self.instruments.values() - if isinstance(instr, Controller) - ] - assert len(controllers) == 1 - return controllers[0] - def execute_pulse_sequences( - self, sequences: List[PulseSequence], options: ExecutionParameters, **kwargs - ): - """ + platform = create_dummy() + sequence = PulseSequence() + parameter = Parameter.frequency + pulse = platform.create_qubit_readout_pulse(qubit=0) + sequence.append(pulse) + parameter_range = np.random.randint(10, size=10) + sweeper = Sweeper(parameter, parameter_range, [pulse]) + platform.execute([sequence], ExecutionParameters(), sweeper) + Args: sequence (List[:class:`qibolab.pulses.PulseSequence`]): Pulse sequences to execute. options (:class:`qibolab.platforms.platform.ExecutionParameters`): Object holding the execution options. @@ -283,7 +289,9 @@ def execute_pulse_sequences( * options.nshots * NS_TO_SEC ) - log.info(f"Minimal execution time (unrolling): {time}") + for sweep in sweepers: + time *= len(sweep.values) + log.info(f"Minimal execution time: {time}") # find readout pulses ro_pulses = { @@ -293,10 +301,9 @@ def execute_pulse_sequences( } results = defaultdict(list) - bounds = kwargs.get("bounds", self._controller.bounds) - for b in batch(sequences, bounds): + for b in batch(sequences, self._controller.bounds): sequence, readouts = unroll_sequences(b, options.relaxation_time) - result = self._execute(sequence, options, **kwargs) + result = self._execute(sequence, options, *sweepers) for serial, new_serials in readouts.items(): results[serial].extend(result[ser] for ser in new_serials) @@ -305,62 +312,6 @@ def execute_pulse_sequences( return results - def sweep( - self, sequence: PulseSequence, options: ExecutionParameters, *sweepers: Sweeper - ): - """Executes a pulse sequence for different values of sweeped - parameters. - - Useful for performing chip characterization. - - Example: - .. testcode:: - - import numpy as np - from qibolab.dummy import create_dummy - from qibolab.sweeper import Sweeper, Parameter - from qibolab.pulses import PulseSequence - from qibolab.execution_parameters import ExecutionParameters - - - platform = create_dummy() - sequence = PulseSequence() - parameter = Parameter.frequency - pulse = platform.create_qubit_readout_pulse(qubit=0) - sequence.append(pulse) - parameter_range = np.random.randint(10, size=10) - sweeper = Sweeper(parameter, parameter_range, [pulse]) - platform.sweep(sequence, ExecutionParameters(), sweeper) - - Returns: - Readout results acquired by after execution. - """ - if options.nshots is None: - options = replace(options, nshots=self.settings.nshots) - - if options.relaxation_time is None: - options = replace(options, relaxation_time=self.settings.relaxation_time) - - time = ( - (sequence.duration + options.relaxation_time) * options.nshots * NS_TO_SEC - ) - for sweep in sweepers: - time *= len(sweep.values) - log.info(f"Minimal execution time (sweep): {time}") - - result = {} - for instrument in self.instruments.values(): - if isinstance(instrument, Controller): - new_result = instrument.sweep( - self.qubits, self.couplers, sequence, options, *sweepers - ) - if isinstance(new_result, dict): - result.update(new_result) - return result - - def __call__(self, sequence, options): - return self.execute_pulse_sequence(sequence, options) - def get_qubit(self, qubit): """Return the name of the physical qubit corresponding to a logical qubit. diff --git a/src/qibolab/result.py b/src/qibolab/result.py index 837d7fba1..494b74bf5 100644 --- a/src/qibolab/result.py +++ b/src/qibolab/result.py @@ -6,9 +6,7 @@ class IntegratedResults: - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.INTEGRATION and AveragingMode.SINGLESHOT @@ -65,9 +63,7 @@ def average(self): class AveragedIntegratedResults(IntegratedResults): - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.INTEGRATION and AveragingMode.CYCLIC or the averages of ``IntegratedResults`` @@ -99,9 +95,7 @@ def phase(self): class RawWaveformResults(IntegratedResults): - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.RAW and AveragingMode.SINGLESHOT may also be used to store the integration weights ? @@ -109,9 +103,7 @@ class RawWaveformResults(IntegratedResults): class AveragedRawWaveformResults(AveragedIntegratedResults): - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.RAW and AveragingMode.CYCLIC or the averages of ``RawWaveformResults`` @@ -119,9 +111,7 @@ class AveragedRawWaveformResults(AveragedIntegratedResults): class SampleResults: - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.DISCRIMINATION and AveragingMode.SINGLESHOT @@ -156,9 +146,7 @@ def average(self): class AveragedSampleResults(SampleResults): - """Data structure to deal with the output of :func:`qibolab.platforms.abstr - act.AbstractPlatform.execute_pulse_sequence` - :func:`qibolab.platforms.abstract.AbstractPlatform.sweep` + """Data structure to deal with the execution output. Associated with AcquisitionType.DISCRIMINATION and AveragingMode.CYCLIC or the averages of ``SampleResults`` diff --git a/tests/test_dummy.py b/tests/test_dummy.py index 6ff899f78..bff9e914e 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -30,11 +30,11 @@ def test_dummy_execute_pulse_sequence(name, acquisition): sequence.append(platform.create_MZ_pulse(0)) sequence.append(platform.create_RX12_pulse(0)) options = ExecutionParameters(nshots=100, acquisition_type=acquisition) - result = platform.execute_pulse_sequence(sequence, options) + result = platform.execute([sequence], options) if acquisition is AcquisitionType.INTEGRATION: - assert result[0].magnitude.shape == (nshots,) + assert result[0][0].magnitude.shape == (nshots,) elif acquisition is AcquisitionType.RAW: - assert result[0].magnitude.shape == (nshots * ro_pulse.duration,) + assert result[0][0].magnitude.shape == (nshots * ro_pulse.duration,) def test_dummy_execute_coupler_pulse(): @@ -45,7 +45,7 @@ def test_dummy_execute_coupler_pulse(): sequence.append(pulse) options = ExecutionParameters(nshots=None) - result = platform.execute_pulse_sequence(sequence, options) + result = platform.execute([sequence], options) def test_dummy_execute_pulse_sequence_couplers(): @@ -66,7 +66,7 @@ def test_dummy_execute_pulse_sequence_couplers(): sequence.append(platform.create_MZ_pulse(0)) sequence.append(platform.create_MZ_pulse(2)) options = ExecutionParameters(nshots=None) - result = platform.execute_pulse_sequence(sequence, options) + result = platform.execute([sequence], options) @pytest.mark.parametrize("name", PLATFORM_NAMES) @@ -75,7 +75,7 @@ def test_dummy_execute_pulse_sequence_fast_reset(name): sequence = PulseSequence() sequence.append(platform.create_MZ_pulse(0)) options = ExecutionParameters(nshots=None, fast_reset=True) - result = platform.execute_pulse_sequence(sequence, options) + result = platform.execute([sequence], options) @pytest.mark.parametrize("name", PLATFORM_NAMES) @@ -94,7 +94,7 @@ def test_dummy_execute_pulse_sequence_unrolling(name, acquisition, batch_size): for _ in range(nsequences): sequences.append(sequence) options = ExecutionParameters(nshots=nshots, acquisition_type=acquisition) - result = platform.execute_pulse_sequences(sequences, options) + result = platform.execute(sequences, options) assert len(result[0]) == nsequences for r in result[0]: if acquisition is AcquisitionType.INTEGRATION: @@ -117,9 +117,9 @@ def test_dummy_single_sweep_raw(name): averaging_mode=AveragingMode.CYCLIC, acquisition_type=AcquisitionType.RAW, ) - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) assert pulse.id and pulse.qubit in results - shape = results[pulse.qubit].magnitude.shape + shape = results[pulse.qubit][0].magnitude.shape assert shape == (pulse.duration * SWEPT_POINTS,) @@ -162,20 +162,20 @@ def test_dummy_single_sweep_coupler( fast_reset=fast_reset, ) average = not options.averaging_mode is AveragingMode.SINGLESHOT - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) assert ro_pulse.id and ro_pulse.qubit in results if average: results_shape = ( - results[ro_pulse.qubit].magnitude.shape + results[ro_pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[ro_pulse.qubit].statistical_frequency.shape + else results[ro_pulse.qubit][0].statistical_frequency.shape ) else: results_shape = ( - results[ro_pulse.qubit].magnitude.shape + results[ro_pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[ro_pulse.qubit].samples.shape + else results[ro_pulse.qubit][0].samples.shape ) assert results_shape == (SWEPT_POINTS,) if average else (nshots, SWEPT_POINTS) @@ -208,20 +208,20 @@ def test_dummy_single_sweep(name, fast_reset, parameter, average, acquisition, n fast_reset=fast_reset, ) average = not options.averaging_mode is AveragingMode.SINGLESHOT - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) assert pulse.id and pulse.qubit in results if average: results_shape = ( - results[pulse.qubit].magnitude.shape + results[pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[pulse.qubit].statistical_frequency.shape + else results[pulse.qubit][0].statistical_frequency.shape ) else: results_shape = ( - results[pulse.qubit].magnitude.shape + results[pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[pulse.qubit].samples.shape + else results[pulse.qubit][0].samples.shape ) assert results_shape == (SWEPT_POINTS,) if average else (nshots, SWEPT_POINTS) @@ -270,21 +270,21 @@ def test_dummy_double_sweep(name, parameter1, parameter2, average, acquisition, acquisition_type=acquisition, ) average = not options.averaging_mode is AveragingMode.SINGLESHOT - results = platform.sweep(sequence, options, sweeper1, sweeper2) + results = platform.execute([sequence], options, sweeper1, sweeper2) assert ro_pulse.id and ro_pulse.qubit in results if average: results_shape = ( - results[pulse.qubit].magnitude.shape + results[pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[pulse.qubit].statistical_frequency.shape + else results[pulse.qubit][0].statistical_frequency.shape ) else: results_shape = ( - results[pulse.qubit].magnitude.shape + results[pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[pulse.qubit].samples.shape + else results[pulse.qubit][0].samples.shape ) assert ( @@ -333,21 +333,21 @@ def test_dummy_single_sweep_multiplex(name, parameter, average, acquisition, nsh acquisition_type=acquisition, ) average = not options.averaging_mode is AveragingMode.SINGLESHOT - results = platform.sweep(sequence, options, sweeper1) + results = platform.execute([sequence], options, sweeper1) for ro_pulse in ro_pulses.values(): assert ro_pulse.id and ro_pulse.qubit in results if average: results_shape = ( - results[ro_pulse.qubit].magnitude.shape + results[ro_pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[ro_pulse.qubit].statistical_frequency.shape + else results[ro_pulse.qubit][0].statistical_frequency.shape ) else: results_shape = ( - results[ro_pulse.qubit].magnitude.shape + results[ro_pulse.qubit][0].magnitude.shape if acquisition is AcquisitionType.INTEGRATION - else results[ro_pulse.qubit].samples.shape + else results[ro_pulse.qubit][0].samples.shape ) assert results_shape == (SWEPT_POINTS,) if average else (nshots, SWEPT_POINTS) diff --git a/tests/test_emulator.py b/tests/test_emulator.py index f87a6e014..78612bfba 100644 --- a/tests/test_emulator.py +++ b/tests/test_emulator.py @@ -40,9 +40,7 @@ def test_emulator_initialization(emulators, emulator): "acquisition", [AcquisitionType.DISCRIMINATION, AcquisitionType.INTEGRATION, AcquisitionType.RAW], ) -def test_emulator_execute_pulse_sequence_compute_overlaps( - emulators, emulator, acquisition -): +def test_emulator_execute_compute_overlaps(emulators, emulator, acquisition): nshots = 10 # 100 platform = create_platform(emulator) pulse_simulator = platform.instruments["pulse_simulator"] @@ -54,30 +52,30 @@ def test_emulator_execute_pulse_sequence_compute_overlaps( acquisition is AcquisitionType.DISCRIMINATION or acquisition is AcquisitionType.INTEGRATION ): - results = platform.execute_pulse_sequence(sequence, options) + results = platform.execute([sequence], options) simulated_states = results["simulation"]["output_states"] overlaps = pulse_simulator.simulation_engine.compute_overlaps(simulated_states) if acquisition is AcquisitionType.DISCRIMINATION: - assert results[0].samples.shape == (nshots,) + assert results[0][0].samples.shape == (nshots,) else: - assert results[0].voltage.shape == (nshots,) + assert results[0][0].voltage.shape == (nshots,) else: with pytest.raises(ValueError) as excinfo: - platform.execute_pulse_sequence(sequence, options) + platform.execute(sequence, options) assert "Current emulator does not support requested AcquisitionType" in str( excinfo.value ) @pytest.mark.parametrize("emulator", EMULATORS) -def test_emulator_execute_pulse_sequence_fast_reset(emulators, emulator): +def test_emulator_execute_fast_reset(emulators, emulator): platform = create_platform(emulator) sequence = PulseSequence() sequence.add(platform.create_qubit_readout_pulse(0, 0)) options = ExecutionParameters( nshots=None, fast_reset=True ) # fast_reset does nothing in emulator - result = platform.execute_pulse_sequence(sequence, options) + result = platform.execute([sequence], options) @pytest.mark.parametrize("emulator", EMULATORS) @@ -110,17 +108,17 @@ def test_emulator_single_sweep( ) average = not options.averaging_mode is AveragingMode.SINGLESHOT if parameter in AVAILABLE_SWEEP_PARAMETERS: - results = platform.sweep(sequence, options, sweeper) + results = platform.execute([sequence], options, sweeper) assert pulse.serial and pulse.qubit in results if average: - results_shape = results[pulse.qubit].statistical_frequency.shape + results_shape = results[pulse.qubit][0].statistical_frequency.shape else: - results_shape = results[pulse.qubit].samples.shape + results_shape = results[pulse.qubit][0].samples.shape assert results_shape == (SWEPT_POINTS,) if average else (nshots, SWEPT_POINTS) else: with pytest.raises(NotImplementedError) as excinfo: - platform.sweep(sequence, options, sweeper) + platform.execute([sequence], options, sweeper) assert "Sweep parameter requested not available" in str(excinfo.value) @@ -171,14 +169,14 @@ def test_emulator_double_sweep_false_history( parameter1 in AVAILABLE_SWEEP_PARAMETERS and parameter2 in AVAILABLE_SWEEP_PARAMETERS ): - results = platform.sweep(sequence, options, sweeper1, sweeper2) + results = platform.execute([sequence], options, sweeper1, sweeper2) assert ro_pulse.serial and ro_pulse.qubit in results if average: - results_shape = results[pulse.qubit].statistical_frequency.shape + results_shape = results[pulse.qubit][0].statistical_frequency.shape else: - results_shape = results[pulse.qubit].samples.shape + results_shape = results[pulse.qubit][0].samples.shape assert ( results_shape == (SWEPT_POINTS, SWEPT_POINTS) @@ -227,14 +225,14 @@ def test_emulator_single_sweep_multiplex( ) average = not options.averaging_mode is AveragingMode.SINGLESHOT if parameter in AVAILABLE_SWEEP_PARAMETERS: - results = platform.sweep(sequence, options, sweeper1) + results = platform.execute([sequence], options, sweeper1) for ro_pulse in ro_pulses.values(): assert ro_pulse.serial and ro_pulse.qubit in results if average: - results_shape = results[ro_pulse.qubit].statistical_frequency.shape + results_shape = results[ro_pulse.qubit][0].statistical_frequency.shape else: - results_shape = results[ro_pulse.qubit].samples.shape + results_shape = results[ro_pulse.qubit][0].samples.shape assert ( results_shape == (SWEPT_POINTS,) if average else (nshots, SWEPT_POINTS) ) diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index ea89655dd..b41be56e6 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -892,7 +892,7 @@ def test_connections(instrument): @pytest.mark.qpu -def test_experiment_execute_pulse_sequence_qpu(connected_platform, instrument): +def test_experiment_execute_qpu(connected_platform, instrument): platform = connected_platform sequence = PulseSequence() qubits = {0: platform.qubits[0], "c0": platform.qubits["c0"]} @@ -922,12 +922,9 @@ def test_experiment_execute_pulse_sequence_qpu(connected_platform, instrument): averaging_mode=AveragingMode.CYCLIC, ) - results = platform.execute_pulse_sequence( - sequence, - options, - ) + results = platform.execute([sequence], options) - assert len(results[ro_pulses[q].id]) > 0 + assert len(results[ro_pulses[q][0].id]) > 0 @pytest.mark.qpu diff --git a/tests/test_result_shapes.py b/tests/test_result_shapes.py index d4317689e..487da3091 100644 --- a/tests/test_result_shapes.py +++ b/tests/test_result_shapes.py @@ -2,6 +2,7 @@ import pytest from qibolab import AcquisitionType, AveragingMode, ExecutionParameters +from qibolab.platform.platform import Platform from qibolab.pulses import PulseSequence from qibolab.result import ( AveragedIntegratedResults, @@ -16,7 +17,7 @@ NSWEEP2 = 8 -def execute(platform, acquisition_type, averaging_mode, sweep=False): +def execute(platform: Platform, acquisition_type, averaging_mode, sweep=False): qubit = next(iter(platform.qubits)) qd_pulse = platform.create_RX_pulse(qubit, start=0) @@ -34,10 +35,10 @@ def execute(platform, acquisition_type, averaging_mode, sweep=False): sweeper1 = Sweeper(Parameter.bias, amp_values, qubits=[platform.qubits[qubit]]) # sweeper1 = Sweeper(Parameter.amplitude, amp_values, pulses=[qd_pulse]) sweeper2 = Sweeper(Parameter.frequency, freq_values, pulses=[ro_pulse]) - results = platform.sweep(sequence, options, sweeper1, sweeper2) + results = platform.execute([sequence], options, sweeper1, sweeper2) else: - results = platform.execute_pulse_sequence(sequence, options) - return results[qubit] + results = platform.execute([sequence], options) + return results[qubit][0] @pytest.mark.qpu