qibolab.instruments.zhinst package#


qibolab.instruments.zhinst.executor module#

Executing pulse sequences on a Zurich Instruments devices.


Translating to Zurich ExecutionParameters.

class qibolab.instruments.zhinst.executor.ZhPort(name: tuple[str, str], offset: float = 0.0, power_range: int = 0)[source]#

Bases: Port

name: tuple[str, str]#

Name of the port that acts as its identifier.

offset: float = 0.0#

DC offset that is applied to this port.

power_range: int = 0#

Similar to attenuation (negative) and gain (positive) for (Zurich instruments).

class qibolab.instruments.zhinst.executor.SubSequence(measurements: list[tuple[str, qibolab.instruments.zhinst.pulse.ZhPulse]], control_sequence: dict[str, list[qibolab.instruments.zhinst.pulse.ZhPulse]])[source]#

Bases: object

A subsequence is a slice (in time) of a sequence that contains at most one measurement per qubit.

When the driver is asked to execute a sequence, it will first split it into sub-sequences. This is needed so that we can create a separate laboneq section for each measurement (multiple measurements per section are not allowed). When splitting a sequence, it is assumed that 1. a measurement operation can be parallel (in time) to another measurement operation (i.e. measuring multiple qubits simultaneously), but other channels (e.g. drive) do not contain any pulses parallel to measurements, 2. ith measurement on some channel is in the same subsequence as the ith measurement (if any) on another measurement channel, 3. all measurements in one subsequence happen at the same time.

measurements: list[tuple[str, qibolab.instruments.zhinst.pulse.ZhPulse]]#
control_sequence: dict[str, list[qibolab.instruments.zhinst.pulse.ZhPulse]]#
class qibolab.instruments.zhinst.executor.Zurich(name, device_setup, time_of_flight=0.0, smearing=0.0)[source]#

Bases: Controller

Driver for a collection of ZI instruments that are automatically synchronized via ZSync protocol.


alias of ZhPort


Signals to lines mapping


Zurich calibration object)


Zurich device parameters for connection


Parameters read from the runcard not part of ExecutionParameters


Zurich experiment definitions


To store if the AcquisitionType.SPECTROSCOPY needs to be enabled by parsing the sequence


Zurich pulse sequence

sub_sequences: list[SubSequence]#

Sub sequences between each measurement

unsplit_channels: set[str]#

Names of channels that were not split into sub-sequences

property sampling_rate#

Sampling rate of control electronics in giga samples per second (GSps).


Establish connection to the physical instrument.


Close connection to the physical instrument.

calibration_step(qubits, couplers, options)[source]#

Zurich general pre experiment calibration definitions.

Change to get frequencies from sequence

register_readout_line(qubit, intermediate_frequency, options)[source]#

Registers qubit measure and acquire lines to calibration and signal map.


To allow debugging with and oscilloscope, just set the following:

self.calibration[f"/logical_signal_groups/q{q}/measure_line"] = lo.SignalCalibration(
register_drive_line(qubit, intermediate_frequency)[source]#

Registers qubit drive line to calibration and signal map.


Registers qubit flux line to calibration and signal map.


Registers qubit flux line to calibration and signal map.


Compilation settings, compilation step, execution step and data retrival - Save a experiment Python object: self.experiment.save(“saved_exp”) - Save a experiment compiled experiment (): self.exp.save(“saved_exp”) # saving compiled experiment

static frequency_from_pulses(qubits, sequence)[source]#

Gets the frequencies from the pulses to the qubits.

create_sub_sequences(qubits: list[qibolab.qubits.Qubit]) tuple[list[qibolab.instruments.zhinst.executor.SubSequence], set[str]][source]#

Create subsequences based on locations of measurements.

Returns list of subsequences and a set of channel names that were not split

experiment_flow(qubits: dict[str, qibolab.qubits.Qubit], couplers: dict[str, qibolab.couplers.Coupler], sequence: PulseSequence, options: ExecutionParameters)[source]#

Create the experiment object for the devices, following the steps separated one on each method:

Translation, Calibration, Experiment Definition.

  • qubits (dict[str, Qubit]) – qubits for the platform.

  • couplers (dict[str, Coupler]) – couplers for the platform.

  • sequence (PulseSequence) – sequence of pulses to be played in the experiment.

play(qubits, couplers, sequence, options)[source]#

Play pulse sequence.

sequence_zh(sequence: PulseSequence, qubits: dict[str, qibolab.qubits.Qubit]) dict[str, list[qibolab.instruments.zhinst.pulse.ZhPulse]][source]#

Convert Qibo sequence to a sequence where all pulses are replaced with ZhPulse instances.

The resulting object is a dictionary mapping from channel name to corresponding sequence of ZhPulse instances

create_exp(qubits, options)[source]#

Zurich experiment initialization using their Experiment class.

set_calibration_for_rt_sweep(exp: Experiment) None[source]#

Set laboneq calibration of parameters that are to be swept in real- time.

set_instrument_nodes_for_nt_sweep(exp: Experiment, sweeper: Sweeper) None[source]#

In some cases there is no straightforward way to sweep a parameter.

In these cases we achieve sweeping by directly manipulating the instrument nodes

get_channel_node_path(channel_name: str) str[source]#

Return the path of the instrument node corresponding to the given channel.

select_exp(exp, qubits, exp_options)[source]#

Build Zurich Experiment selecting the relevant sections.

static play_sweep(exp, channel_name, pulse)[source]#

Play Zurich pulse when a single sweeper is involved.

sweep(qubits, couplers, sequence: PulseSequence, options, *sweepers)[source]#

Play pulse and sweepers sequence.

qibolab.instruments.zhinst.pulse module#

Wrapper for qibolab and laboneq pulses and sweeps.

qibolab.instruments.zhinst.pulse.select_pulse(pulse: Pulse)[source]#

Return laboneq pulse object corresponding to the given qibolab pulse.

class qibolab.instruments.zhinst.pulse.ZhPulse(pulse)[source]#

Bases: object

Wrapper data type that holds a qibolab pulse, the corresponding laboneq pulse object, and any sweeps associated with this pulse.

pulse: Pulse#

Qibolab pulse.


Laboneq pulse.

zhsweepers: list[tuple[qibolab.sweeper.Parameter, laboneq.dsl.parameter.SweepParameter]]#

Parameters to be swept, along with their laboneq sweep parameter definitions.

delay_sweeper: SweepParameter | None#

Laboneq sweep parameter if the delay of the pulse should be swept.

add_sweeper(param: Parameter, sweeper: SweepParameter)[source]#

Add sweeper to list of sweepers associated with this pulse.

qibolab.instruments.zhinst.sweep module#

Pre-execution processing of sweeps.

qibolab.instruments.zhinst.sweep.classify_sweepers(sweepers: Iterable[Sweeper]) tuple[list[qibolab.sweeper.Sweeper], list[qibolab.sweeper.Sweeper]][source]#

Divide sweepers into two lists: 1. sweeps that can be done in the laboneq near-time sweep loop, 2. sweeps that can be done in real-time (i.e. on hardware)

class qibolab.instruments.zhinst.sweep.ProcessedSweeps(sweepers: Iterable[Sweeper], qubits: dict[str, qibolab.qubits.Qubit])[source]#

Bases: object

Data type that centralizes and allows extracting information about given sweeps.

In laboneq, sweeps are represented with the help of SweepParameter instances. When adding pulses to a laboneq experiment, some properties can be set to be an instance of SweepParameter instead of a fixed numeric value. In case of channel property sweeps, either the relevant calibration property or the instrument node directly can be set ot a SweepParameter instance. Parts of the laboneq experiment that define the sweep loops refer to SweepParameter instances as well. These should be linkable to instances that are either set to a pulse property, a channel calibration or instrument node. To achieve this, we use the exact same SweepParameter instance in both places. This class takes care of creating these SweepParameter instances and giving access to them in a consistent way (i.e. whenever they need to be the same instance they will be the same instance). When constructing sweep loops you may ask from this class to provide all the SweepParameter instances related to a given qibolab Sweeper (parallel sweeps). Later, when adding pulses or setting channel properties, you may ask from this class to provide all SweepParameter instances related to a given pulse or channel, and you will get parameters that are linkable to the ones in the sweep loop definition

sweeps_for_pulse(pulse: Pulse) list[tuple[qibolab.sweeper.Parameter, laboneq.dsl.parameter.SweepParameter]][source]#
sweeps_for_channel(ch: str) list[tuple[qibolab.sweeper.Parameter, laboneq.dsl.parameter.SweepParameter]][source]#
sweeps_for_sweeper(sweeper: Sweeper) list[laboneq.dsl.parameter.SweepParameter][source]#
channel_sweeps_for_sweeper(sweeper: Sweeper) list[tuple[str, qibolab.sweeper.Parameter, laboneq.dsl.parameter.SweepParameter]][source]#
channels_with_sweeps() set[str][source]#

qibolab.instruments.zhinst.util module#

Utility methods.

qibolab.instruments.zhinst.util.measure_channel_name(qubit: Qubit) str[source]#

Construct and return a name for qubit’s measure channel.

FIXME: We cannot use channel name directly, because currently channels are named after wires, and due to multiplexed readout multiple qubits have the same channel name for their readout. Should be fixed once channels are refactored.

qibolab.instruments.zhinst.util.acquire_channel_name(qubit: Qubit) str[source]#

Construct and return a name for qubit’s acquire channel.

FIXME: We cannot use acquire channel name, because qibolab does not have a concept of acquire channel. This function shall be removed once all channel refactoring is done.