Skip to content

Commit

Permalink
Merge pull request #861 from qiboteam/unify-execution
Browse files Browse the repository at this point in the history
Unify execution interface
  • Loading branch information
alecandido authored Jul 23, 2024
2 parents 673425d + 79b5c14 commit e95b2e8
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 240 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions doc/source/getting-started/experiment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
23 changes: 10 additions & 13 deletions doc/source/main-documentation/qibolab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.
Expand All @@ -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::

Expand Down Expand Up @@ -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::

Expand Down Expand Up @@ -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:

Expand All @@ -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.

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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`

Expand Down
20 changes: 10 additions & 10 deletions doc/source/tutorials/calibration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ 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.

.. testcode:: python

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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion doc/source/tutorials/pulses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion examples/minimum_working_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
4 changes: 2 additions & 2 deletions examples/qibolab_v017_1Q_emulator_test_QuTiP.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
]
},
{
Expand Down Expand Up @@ -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:"
]
Expand Down
10 changes: 4 additions & 6 deletions src/qibolab/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

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

Expand Down
12 changes: 3 additions & 9 deletions src/qibolab/instruments/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand Down
21 changes: 0 additions & 21 deletions src/qibolab/instruments/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand Down
Loading

0 comments on commit e95b2e8

Please sign in to comment.