ScheduleBuilder#

Module: iqm.pulse.builder

class iqm.pulse.builder.ScheduleBuilder(op_table, calibration, chip_topology, channels, component_channels)#

Bases: object

Builds instruction schedules out of quantum circuits or individual quantum operations.

Encapsulates known quantum ops, the calibration information for them, QPU components and their topology, and controller properties.

Parameters:
  • op_table (QuantumOpTable) – definitions of known quantum ops

  • calibration (OpCalibrationDataTree) – calibration data tree for the quantum ops

  • chip_topology (ChipTopology) – Chip topology derived from the CHAD.

  • channels (dict[str, ChannelProperties]) – mapping of controller names to the configurations of their channels

  • component_channels (dict[str, dict[str, str]]) – Mapping from QPU component name to a mapping of ('drive', 'flux', 'readout') to the name of the control channel responsible for that function of the component.

Attributes

barrier

prx

prx_12

cz

move

measure

measure_fidelity

cc_prx

composite_cache

Cache for the CompositeGate TimeBoxes.

Methods

build_playlist

Build a playlist from a number of instruction schedules.

circuit_to_timebox

Convert a quantum circuit to a TimeBox.

get_cache

Get a copy of the gate implementation cache.

get_calibration

Calibration data for the given quantum operation, implementation and locus.

get_control_channels

Control channels that directly affect quantum operations at the given locus.

get_drive_channel

Drive channel for the given QPU component.

get_flux_channel

Flux channel for the given QPU component.

get_implementation

Provide an implementation for a quantum operation at a given locus.

get_implementation_class

Implementation class for the given operation.

get_probe_channel

Probe line channel for the probe line component belongs to.

get_virtual_feedback_channel_for

Get virtual feedback channel for feedback to a given AWG from a given probe line.

get_virtual_feedback_channels

All virtual feedback signal channels for the given QPU component.

has_calibration

Is there calibration data for the given quantum operation, implementation and locus?

inject_calibration

Inject new calibration data, changing calibration after initialisation.

inject_gate_definitions

Inject gate definition updates, changing op_table after initialisation.

resolve_timebox

Resolve a TimeBox.

timebox_to_schedule

Convert a TimeBox to a finished instruction schedule, ready for execution.

timeboxes_to_front_padded_playlist

Temporary helper function, for converting a sequence of TimeBoxes to a Playlist.

timeboxes_to_playlist

Convert a sequence of TimeBoxes to a Playlist.

validate_calibration

Check that the calibration data matches the known quantum operations.

validate_quantum_circuit

Validate a sequence of circuit operations constituting a quantum circuit.

wait

Utility method for applying Block instructions on every channel of the given locus.

composite_cache#

Cache for the CompositeGate TimeBoxes. Flushed whenever ANY calibration data is injected into the builder.

inject_gate_definitions(partial_gate_definitions)#

Inject gate definition updates, changing op_table after initialisation.

Currently only supports updating the default implementation for quantum operations.

Parameters:

partial_gate_definitions (dict[str, Any]) – gate definition data to be injected.

Raises:

ValueError – if attempting to inject unsupported QuantumOp attributes.

Return type:

None

inject_calibration(partial_calibration, cache=None)#

Inject new calibration data, changing calibration after initialisation.

Invalidates the GateImplementation caches for the affected operations/implementations/loci. Also invalidates the cache for any factorizable gate implementation, if any of its locus components was affected.

Parameters:
  • partial_calibration (dict[str, dict[str, dict[tuple[str, ...], dict[str, Any]]]]) – data to be injected. Must have the same structure as calibration but does not have to contain all operations/implementations/loci/values. Only the parts of the data that are found will be merged into calibration (including any None values). _cache will be invalidated for the found operations/implementations/loci and only if the new calibration data actually differs from the previous.

  • cache (dict[str, dict[str, dict[tuple[str, ...], GateImplementation]]] | None) – if an implementation cache is given, we do not invalidate the cache for affected loci, but instead fetch the implementations from the provided cache (if they exist there).

Return type:

None

get_cache(ops=None, implementations=None, loci=None)#

Get a copy of the gate implementation cache.

Parameters:
  • ops (Iterable[str] | None) – limit to these QuantumOps.

  • implementations (Iterable[str] | None) – limit to these GateImplementations.

  • loci (Iterable[tuple[str, ...]] | None) – limit to these loci.

Returns:

The gate implementation cache.

Return type:

dict[str, dict[str, dict[tuple[str, …], GateImplementation]]]

validate_calibration()#

Check that the calibration data matches the known quantum operations.

Raises:

ValueError – there is something wrong with the calibration data

Return type:

None

get_drive_channel(component)#

Drive channel for the given QPU component.

Parameters:

component (str) – name of a QPU component

Returns:

Name of the drive channel for component, if it exists.

Raises:

KeyError – if component does not exist or does not have a drive channel

Return type:

str

get_flux_channel(component)#

Flux channel for the given QPU component.

See get_drive_channel().

Parameters:

component (str)

Return type:

str

get_probe_channel(component)#

Probe line channel for the probe line component belongs to.

See get_drive_channel().

Parameters:

component (str) – name of a QPU component (typically qubit) to probe

Return type:

str

get_virtual_feedback_channels(component)#

All virtual feedback signal channels for the given QPU component.

A virtual feedback channel between a source and a destination exists if the station configuration allows it. component can be either the source or the destination of the signal.

Parameters:

component (str) – name of a QPU component

Returns:

Names of the virtual channels.

Return type:

list[str]

get_virtual_feedback_channel_for(awg_name, feedback_qubit)#

Get virtual feedback channel for feedback to a given AWG from a given probe line.

Parameters:
  • awg_name (str) – Name of the AWG controller that receives the feedback bit.

  • feedback_qubit (str) – which qubit’s measurement resulted in the feedback bit.

Returns:

The virtual feedback channel name.

Raises:

ValueError – if the given AWG does not support fast feedback from the given probe line.

Return type:

str

has_calibration(op_name, impl_name, locus)#

Is there calibration data for the given quantum operation, implementation and locus?

Parameters:
  • op_name (str) – name of the quantum operation

  • impl_name (str) – name of the implementation

  • locus (tuple[str, ...]) – locus of the operation

Returns:

True iff requested calibration data was found

Return type:

bool

get_calibration(op_name, impl_name, locus, *, strict=True)#

Calibration data for the given quantum operation, implementation and locus.

Parameters:
  • op_name (str) – name of the quantum operation

  • impl_name (str) – name of the implementation

  • locus (tuple[str, ...]) – locus of the operation

  • strict (bool) – Iff True, raise an error if the calibration data is missing.

Returns:

Requested calibration data, or an empty dict if strict == False and not found.

Raises:

ValueError – Requested calibration data was not found and strict == True.

Return type:

dict[str, Any]

get_control_channels(locus)#

Control channels that directly affect quantum operations at the given locus.

Includes the probe, drive and flux channels of the locus QPU components. Does not include e.g. any neighboring coupler channels, these will have to be added separately in the TimeBox resolution phase.

Will only return channels that are known to exist, i.e. are found in ScheduleBuilder.channels.

Parameters:

locus (Iterable[str]) – locus on which the operation acts

Returns:

names of the control channels that directly affect the operation

Return type:

tuple[str, …]

wait(locus, duration, *, rounding=False)#

Utility method for applying Block instructions on every channel of the given locus.

The Block instructions guarantee the locus components to idle for the given duration, and cannot e.g. be replaced with e.g. dynamical decoupling sequences. They are treated the same as any other TimeBox contents:

  1. Blocks on different channels remain aligned in time during scheduling.

  2. The actual waiting time on a particular channel may thus be >= duration, if the other channels have less non-blocking space on either side.

Note

TODO For now, this method can round duration to the nearest value allowed by each channel if requested. This is for the benefit of EXA sweeping over waiting durations. In the future, EXA sweep generation should be responsible for doing the rounding.

Parameters:
  • locus (Iterable[str]) – locus components that should experience the wait

  • duration (float) – how long to wait (in seconds)

  • rounding (bool) – Iff True, for each channel separately, duration will be rounded to the nearest value allowed by the granularity of that channel. The Waits will start simultaneously.

Returns:

box containing Block instructions on every control channel of locus

Return type:

TimeBox

get_implementation(op_name: Literal['prx', 'prx_12'], locus: Sequence[str], impl_name: str | None = None, *, strict_locus: bool = False, priority_calibration: dict[str, Any] | None = None, priority_calibration_factorizable: dict[tuple[str, ...], dict[str, Any]] | None = None) PrxGateImplementation#
get_implementation(op_name: Literal['cz', 'move'], locus: Sequence[str], impl_name: str | None = None, *, strict_locus: bool = False, priority_calibration: dict[str, Any] | None = None, priority_calibration_factorizable: dict[tuple[str, ...], dict[str, Any]] | None = None) FluxPulseGate
get_implementation(op_name: Literal['measure', 'measure_fidelity'], locus: Sequence[str], impl_name: str | None = None, *, strict_locus: bool = False, priority_calibration: dict[str, Any] | None = None, priority_calibration_factorizable: dict[tuple[str, ...], dict[str, Any]] | None = None) Measure_CustomWaveforms
get_implementation(op_name: Literal['cc_prx'], locus: Sequence[str], impl_name: str | None = None, *, strict_locus: bool = False, priority_calibration: dict[str, Any] | None = None, priority_calibration_factorizable: dict[tuple[str, ...], dict[str, Any]] | None = None) CCPRX_Composite
get_implementation(op_name: str, locus: Sequence[str], impl_name: str | None = None, *, strict_locus: bool = False, priority_calibration: dict[str, Any] | None = None, priority_calibration_factorizable: dict[tuple[str, ...], dict[str, Any]] | None = None) GateImplementation

Provide an implementation for a quantum operation at a given locus.

The GateImplementations are built when they are first requested, and cached for later use.

The returned implementation may use a permuted locus (and the calibration data associated with it) based on the following rules:

  • If the operation is not symmetric (e.g. CX), different locus orders correspond to different operations, and the locus order many not be changed.

  • If the operation is symmetric (e.g. CZ), all locus orders correspond to the same logical operation. However, symmetric operations may still have non-symmetric implementations.

    • If the chosen implementation is symmetric, we order locus numerically (not lexicographically!), so that we only need to have calibration data for one locus order.

    • Otherwise we check all permutations of locus, and pick the first one that has calibration data available. The original order is checked first.

The attributes QuantumOp.factorizable, GateImplementation.needs_calibration and whether the implementation is a CompositeGate interact in a nontrivial way. Examples of their currently used combinations:

composite / not composite

factorizable

not factorizable

needs_calibration

measure_fidelity.shelved_constant / measure.constant

cc_prx.prx_composite / prx.drag_crf

not needs_calibration

reset.conditional / not meaningful

rz.prx_composite / rz.virtual

Parameters:
  • op_name (str | Literal['measure', 'measure_fidelity', 'prx', 'prx_12', 'cz', 'move', 'cc_prx']) – Name of the quantum operation.

  • locus (Sequence[str]) – Locus of the operation.

  • impl_name (str | None) – Name of the implementation. None means the implementation is chosen automatically using QuantumOp.get_default_implementation_for_locus().

  • strict_locus (bool) – Only has an effect if the operation is symmetric (e.g. CZ). Iff False and the chosen implementation is non-symmetric, the locus order may be changed if no calibration data is available for the requested locus order.

  • priority_calibration (dict[str, Any] | None) – Single-locus calibration data to override the common calibration data in calibration. Any non-None values found in priority_calibration will be merged to the common calibration. If used, impl_name must not be None since priority_calibration is implementation-specific. Note: using priority_calibration will prevent caching.

  • priority_calibration_factorizable (dict[tuple[str, ...], dict[str, Any]] | None) – Like priority_calibration, but for factorizable QuantumOps. Mapping from single-qubit loci to overrides of their calibration data.

Returns:

Requested implementation.

Raises:

ValueError – Requested implementation could not be provided.

Return type:

GateImplementation

get_implementation_class(op_name, impl_name=None)#

Implementation class for the given operation.

Parameters:
  • op_name (str) – Name of the quantum operation.

  • impl_name (str | None) – Name of the implementation (None means use the global default implementation).

Returns:

requested implementation class

Return type:

type[GateImplementation]

validate_quantum_circuit(operations, *, require_measurements=False)#

Validate a sequence of circuit operations constituting a quantum circuit.

Parameters:
  • operations (Iterable[CircuitOperation]) – quantum circuit to be validated

  • require_measurements (bool) – iff True the circuit must include at least one measurement operation

Raises:

ValueErroroperations do not constitute a valid quantum circuit

Return type:

None

circuit_to_timebox(circuit, *, name='', scheduling=SchedulingStrategy.ASAP, scheduling_algorithm=SchedulingAlgorithm.HARD_BOUNDARY, locus_mapping=None)#

Convert a quantum circuit to a TimeBox.

Parameters:
  • circuit (Iterable[CircuitOperation]) – quantum circuit

  • name (str) – name of the circuit

  • scheduling (SchedulingStrategy) – scheduling strategy (e.g. ASAP or ALAP) to be used in resolving the TimeBoxes.

  • scheduling_algorithm (SchedulingAlgorithm) – scheduling algorithm to be used in resolving the TimeBoxes.

  • locus_mapping (dict[str, str] | None) – optional mapping of placeholder component names to the physical component names used while resolving the circuit into a TimeBox.

Returns:

unresolved TimeBox that implements circuit

Raises:

ValueError – failed to convert circuit to a TimeBox

Return type:

TimeBox

timeboxes_to_front_padded_playlist(boxes, *, neighborhood=0)#

Temporary helper function, for converting a sequence of TimeBoxes to a Playlist.

Each individual TimeBox in boxes is resolved into a Schedule, and then each schedules is front-padded with Wait instructions on each channel such that the resulting Schedules have equal durations. This is required since for now in Station Control the delay before the final measurement is the same for all the Schedules in a Playlist, and we do not wish to lose coherence waiting for the measurement after each Schedule is done.

TODO Once Station Control can handle measurements better, this method should be removed, and timeboxes_to_playlist() be used instead.

Parameters:
  • boxes (Iterable[TimeBox]) – TimeBoxes to include in the playlist

  • neighborhood (int) – During scheduling, block neighboring channels of the used components this far. By default, blocks only the defined locus components and any other components which have occupied channels.

Returns:

playlist that implements boxes and the readout metrics for that playlist.

Return type:

tuple[Playlist, ReadoutMetrics]

timeboxes_to_playlist(boxes, *, neighborhood=1)#

Convert a sequence of TimeBoxes to a Playlist.

Resolves the boxes, converts them to Schedules, removes unnecessary channels, and then packs the Schedules into a Playlist. Assumes all the TimeBoxes refer to the same QPU and its control channels.

Parameters:
  • boxes (Iterable[TimeBox]) – TimeBoxes to include in the playlist

  • neighborhood (int) – During scheduling, block neighboring channels of the used components this far. The default value ensures that quantum operations work as intended, assuming the station is properly calibrated. Higher values may help defend against crosstalk, at the expense of a longer instruction schedule and thus more decoherence.

Returns:

playlist that implements boxes

Return type:

Playlist

timebox_to_schedule(box, *, neighborhood=1)#

Convert a TimeBox to a finished instruction schedule, ready for execution.

Resolves the box, then converts the durations of the instructions in the schedule to samples at the channel sample_rate.

Parameters:
  • box (TimeBox) – TimeBox to resolve

  • neighborhood (int) – During scheduling, block neighboring channels of the used components this far. The default value ensures that quantum operations work as intended, assuming the station is properly calibrated. Higher values may help defend against crosstalk, at the expense of a longer instruction schedule and thus more decoherence.

Returns:

finished schedule that implements box

Return type:

Schedule

resolve_timebox(box, *, neighborhood, compute_neighborhood_hard_boundary=False)#

Resolve a TimeBox.

Resolves recursively each of the children of the box, and then concatenates the resulting Schedules into a new one using a specific scheduling strategy and algorithm.

The supported algorithms are HARD_BOUNDARY, which treats each composite TimeBox as a solid rectangle (the longest channel within defines the duration) and TETRIS, which packs the schedule as tightly as possible (solid instructions still cannot overlap) regardless of the TimeBox boundaries.

Modifies box so that it becomes atomic, if it isn’t already.

Parameters:
  • box (TimeBox) – TimeBox to resolve

  • neighborhood (int) – During scheduling, block control channels of neighboring QPU components this far from the locus. Values higher than 0 may help defend against crosstalk, at the expense of a longer instruction schedule and thus more decoherence.

  • compute_neighborhood_hard_boundary (bool) – Whether to precompute the neighborhood components while resolving a composite TimeBox in the HARD_BOUNDARY algorithm. Typically one does not want to do this on the top layer composite TimeBox, since it would be unused. The algorithm sets this True on lower layers, where it improves the performance as the neighborhood components are needed in scheduling.

Returns:

instruction schedule that implements box

Return type:

Schedule

build_playlist(schedules, finish_schedules=True)#

Build a playlist from a number of instruction schedules.

This involves compressing the schedules so that no duplicate information needs to be transferred to Station Control.

All virtual channels are dropped at this point.

Parameters:
  • schedules (Sequence[Schedule]) – finished instruction schedules to include in the playlist

  • finish_schedules (bool) – whether to finalise the schedules before building the playlist. Should be set True unless some process has already finalised them before calling this function.

Returns:

playlist containing the schedules and the readout metrics for this playlist.

Raises:

ValueError – if the schedules contain channels with non-uniform sampling rates

Return type:

tuple[Playlist, ReadoutMetrics]

Inheritance

Inheritance diagram of iqm.pulse.builder.ScheduleBuilder