qat.experimental.conversion.pulse_to_q1.passes module

Pass and pipeline definitions for the Pulse-to-Q1 conversion.

class AcquireAnalysisStack(for_op_number_repeats, for_op_indexes)

Bases: object

Mutable stack state tracked while walking the enclosing loop nest.

Both lists behave as stacks that are pushed on entry to an scf.for and popped on exit, so at any acquire they describe exactly the loops surrounding it.

Variables:
  • for_op_number_repeats – A stack tracking the number of repetitions of each enclosing scf.for loop.

  • for_op_indexes – Induction-variable SSA values of the currently enclosing scf.for loops, aligned with for_op_number_repeats.

for_op_indexes: list[SSAValue[IndexType]]
for_op_number_repeats: list[int]
class BoundDeadFrameEliminationPass

Bases: OrderedPass, ModulePass

Erase frame metadata after binding and lowering have consumed it.

This deliberately runs after PulseToQ1LoweringPass rather than marking pulse.create_frame globally pure. Frames identify physical channels during outlining and binding, including channels with no timed instructions, so generic Pulse canonicalization must preserve them until that information has been captured.

apply(ctx, op)
Return type:

None

name: ClassVar[str] = 'bound-dead-frame-elimination'
required_predecessors()

Pass classes that must be present in the same pipeline and run before this pass.

Return type:

frozenset[type[ModulePass]]

class PulseToQ1LoweringPass(target_data=QbloxTargetData(max_acquisitions=10000, default_shots=1000, QUBIT_DATA=QubitDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000, passive_reset_time=0.001), RESONATOR_DATA=ResonatorDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000), driver_version=SemanticVersion(major=1, minor=3, patch=1, prerelease=None, build=None), fw_version=SemanticVersion(major=0, minor=13, patch=0, prerelease=None, build=None), Q1ASM_DATA=Q1asmDescription(min_gain=-32768, max_gain=32767, min_offset=-32768, max_offset=32767, max_wait_time=65532, register_size=4294967295, loop_unroll_threshold=4), CONTROL_SEQUENCER_DATA=ControlSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=16384, sample_rate=1000000000.0), READOUT_SEQUENCER_DATA=ReadoutSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=12288, sample_rate=1000000000.0), QCM_DATA=QcmDescription(number_of_sequencers=6, min_qcm_offset_v=-2.5, max_qcm_offset_v=2.5, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5], 2: [0, 1, 2, 3, 4, 5], 3: [0, 1, 2, 3, 4, 5]}), QCM_RF_DATA=QcmRfDescription(number_of_sequencers=6, min_qcm_rf_offset_mv=-84, max_qcm_rf_offset_mv=73, min_out_att_db=0, max_out_att_db=60, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_DATA=QrmDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_offset_v=-0.09, max_qrm_offset_v=0.09, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_RF_DATA=QrmRfDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_rf_offset_v=-0.09, max_qrm_rf_offset_v=0.09, min_out_att_db=0, max_out_att_db=60, min_in_att_db=0, max_in_att_db=30, output_connections={0: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5]}), QRC_DATA=QrcDescription(number_of_sequencers=12, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=7000000, number_of_readout_sequencers=8, number_of_control_sequencers=4, min_out_att_db=0.0, max_out_att_db=31.5, min_in_att_db=0.0, max_in_att_db=31.5, output_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7], 2: [0, 4, 8, 9, 10, 11], 3: [1, 5, 8, 9, 10, 11], 4: [2, 6, 8, 9, 10, 11], 5: [3, 7, 8, 9, 10, 11]}, input_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7]})))

Bases: OrderedPass, ModulePass

Apply the Pulse-to-Q1 rewrite stage inside outlined sequences.

Q1OutliningPass first isolates one logical sequence envelope for each frame partition. This pass then traverses those envelopes and applies the per-operation rewrite set that converts Pulse-level instructions into the flat Q1 instruction dialect.

apply(ctx, op)
Return type:

None

name: ClassVar[str] = 'pulse-to-q1-lowering'
required_predecessors()

Pass classes that must be present in the same pipeline and run before this pass.

Return type:

frozenset[type[ModulePass]]

runs_before()

Pass classes that, if present in the same pipeline, must run after this pass.

Return type:

frozenset[type[ModulePass]]

target_data: QbloxTargetData = QbloxTargetData(max_acquisitions=10000, default_shots=1000, QUBIT_DATA=QubitDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000, passive_reset_time=0.001), RESONATOR_DATA=ResonatorDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000), driver_version=SemanticVersion(major=1, minor=3, patch=1, prerelease=None, build=None), fw_version=SemanticVersion(major=0, minor=13, patch=0, prerelease=None, build=None), Q1ASM_DATA=Q1asmDescription(min_gain=-32768, max_gain=32767, min_offset=-32768, max_offset=32767, max_wait_time=65532, register_size=4294967295, loop_unroll_threshold=4), CONTROL_SEQUENCER_DATA=ControlSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=16384, sample_rate=1000000000.0), READOUT_SEQUENCER_DATA=ReadoutSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=12288, sample_rate=1000000000.0), QCM_DATA=QcmDescription(number_of_sequencers=6, min_qcm_offset_v=-2.5, max_qcm_offset_v=2.5, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5], 2: [0, 1, 2, 3, 4, 5], 3: [0, 1, 2, 3, 4, 5]}), QCM_RF_DATA=QcmRfDescription(number_of_sequencers=6, min_qcm_rf_offset_mv=-84, max_qcm_rf_offset_mv=73, min_out_att_db=0, max_out_att_db=60, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_DATA=QrmDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_offset_v=-0.09, max_qrm_offset_v=0.09, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_RF_DATA=QrmRfDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_rf_offset_v=-0.09, max_qrm_rf_offset_v=0.09, min_out_att_db=0, max_out_att_db=60, min_in_att_db=0, max_in_att_db=30, output_connections={0: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5]}), QRC_DATA=QrcDescription(number_of_sequencers=12, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=7000000, number_of_readout_sequencers=8, number_of_control_sequencers=4, min_out_att_db=0.0, max_out_att_db=31.5, min_in_att_db=0.0, max_in_att_db=31.5, output_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7], 2: [0, 4, 8, 9, 10, 11], 3: [1, 5, 8, 9, 10, 11], 4: [2, 6, 8, 9, 10, 11], 5: [3, 7, 8, 9, 10, 11]}, input_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7]}))
class Q1PreAcquireTransformationPass

Bases: OrderedPass, ModulePass

Lower pulse.acquire to PreQ1AcquireOp with QBlox acquisition context.

QBlox acquires need a result store index (bin) and a repetition count, neither of which is expressed by pulse.acquire itself. This pass walks the module, tracking the enclosing scf.for nest, and for each pulse.acquire computes:

  • a store_idx from the loop induction variables, so each iteration writes to a distinct bin, and

  • number_runs from the product of the enclosing loop trip counts.

It then replaces the pulse.acquire with an equivalent PreQ1AcquireOp carrying that context, ready for the context-free lowering in RewritePreQ1AcquireOp.

Assumes there are no scf.while loops and that all scf.for bounds are constant.

apply(ctx, op)

Run the transformation over op in place.

Parameters:
  • ctx (Context) – The xDSL context, unused but present for the pass interface.

  • op (ModuleOp) – The module to transform.

Return type:

None

name: ClassVar[str] = 'acquire-pre-q1-transformation'
required_predecessors()

Pass classes that must be present in the same pipeline and run before this pass.

Return type:

frozenset[type[ModulePass]]

runs_before()

Pass classes that, if present in the same pipeline, must run after this pass.

Return type:

frozenset[type[ModulePass]]

class Q1PulseLegalisationPass

Bases: OrderedPass, ModulePass

Apply Pulse phase legalisation before Pulse-to-Q1 lowering.

This stage applies the legalisation pattern set to Pulse-level operands after validation. The pattern set is expected to grow over time as more Pulse operations acquire legalisation support.

apply(ctx, op)
Return type:

None

name: ClassVar[str] = 'q1-pulse-legalisation'
required_predecessors()

Pass classes that must be present in the same pipeline and run before this pass.

Return type:

frozenset[type[ModulePass]]

runs_before()

Pass classes that, if present in the same pipeline, must run after this pass.

Return type:

frozenset[type[ModulePass]]

class Q1PulseValidationPass

Bases: OrderedPass, ModulePass

Validate QBlox-specific pre-conditions on constant Pulse operands.

Enforces hardware constraints that cannot be expressed as Pulse dialect invariants, ahead of legalisation and lowering. Dynamic waits, frame frequencies, continuous amplitudes, and phase operands are rejected because no Q1 lowering exists for them: Q1 phase instructions only accept an immediate, so a dynamic phase would require a real radians-to-NCO-steps runtime conversion that does not exist.

The following constraints are enforced:

  • pulse.wait constant duration: finite, non-negative, and an integer number of nanoseconds.

  • pulse.create_frame constant frequency: finite.

  • pulse.phase_set and pulse.phase_shift constant phase: finite.

apply(ctx, op)
Return type:

None

name: ClassVar[str] = 'q1-pulse-validation'
required_predecessors()

Pass classes that must be present in the same pipeline and run before this pass.

Return type:

frozenset[type[ModulePass]]

runs_before()

Pass classes that, if present in the same pipeline, must run after this pass.

Return type:

frozenset[type[ModulePass]]

create_qblox_configured_q1_pipeline(canonical_data, target_data=QbloxTargetData(max_acquisitions=10000, default_shots=1000, QUBIT_DATA=QubitDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000, passive_reset_time=0.001), RESONATOR_DATA=ResonatorDescription(sample_time=1e-09, samples_per_clock_cycle=1, instruction_memory_size=50000, waveform_memory_size=1500, pulse_duration_min=6.4e-08, pulse_duration_max=0.001, pulse_channel_lo_freq_min=1000000, pulse_channel_lo_freq_max=10000000000, pulse_channel_if_freq_min=0, pulse_channel_if_freq_max=10000000000), driver_version=SemanticVersion(major=1, minor=3, patch=1, prerelease=None, build=None), fw_version=SemanticVersion(major=0, minor=13, patch=0, prerelease=None, build=None), Q1ASM_DATA=Q1asmDescription(min_gain=-32768, max_gain=32767, min_offset=-32768, max_offset=32767, max_wait_time=65532, register_size=4294967295, loop_unroll_threshold=4), CONTROL_SEQUENCER_DATA=ControlSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=16384, sample_rate=1000000000.0), READOUT_SEQUENCER_DATA=ReadoutSequencerDescription(grid_time=4, nco_min_freq=-500000000.0, nco_max_freq=500000000.0, nco_max_phase_steps=1000000000, nco_phase_steps_per_deg=2777777.777777778, nco_freq_steps_per_hz=4, nco_freq_limit_steps=2000000000.0, number_of_registers=64, min_acq_integration_length=4, max_acq_integration_length=16777212, min_acq_threshold=-16777212, max_acq_threshold=16777212, max_sample_size_waveforms=16384, max_num_instructions=12288, sample_rate=1000000000.0), QCM_DATA=QcmDescription(number_of_sequencers=6, min_qcm_offset_v=-2.5, max_qcm_offset_v=2.5, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5], 2: [0, 1, 2, 3, 4, 5], 3: [0, 1, 2, 3, 4, 5]}), QCM_RF_DATA=QcmRfDescription(number_of_sequencers=6, min_qcm_rf_offset_mv=-84, max_qcm_rf_offset_mv=73, min_out_att_db=0, max_out_att_db=60, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_DATA=QrmDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_offset_v=-0.09, max_qrm_offset_v=0.09, output_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5], 1: [0, 1, 2, 3, 4, 5]}), QRM_RF_DATA=QrmRfDescription(number_of_sequencers=6, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=3000000, min_qrm_rf_offset_v=-0.09, max_qrm_rf_offset_v=0.09, min_out_att_db=0, max_out_att_db=60, min_in_att_db=0, max_in_att_db=30, output_connections={0: [0, 1, 2, 3, 4, 5]}, input_connections={0: [0, 1, 2, 3, 4, 5]}), QRC_DATA=QrcDescription(number_of_sequencers=12, min_sample_size_scope_acquisitions=4, max_sample_size_scope_acquisitions=16384, max_binned_acquisitions=7000000, number_of_readout_sequencers=8, number_of_control_sequencers=4, min_out_att_db=0.0, max_out_att_db=31.5, min_in_att_db=0.0, max_in_att_db=31.5, output_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7], 2: [0, 4, 8, 9, 10, 11], 3: [1, 5, 8, 9, 10, 11], 4: [2, 6, 8, 9, 10, 11], 5: [3, 7, 8, 9, 10, 11]}, input_connections={0: [0, 1, 2, 3, 4, 5, 6, 7], 1: [0, 1, 2, 3, 4, 5, 6, 7]})))

Create the Pulse-to-emission-ready configured Qblox Q1 conversion pipeline.

This is a Q1 conversion/configuration pipeline only: it assumes the input module has already been through Pulse-level preprocessing (see build_default_pipeline()). The experimental middleend runs Pulse preprocessing before the backend applies this pipeline.

Binding runs after Pulse validation, legalisation, and acquisition preparation while pulse.create_frame still carries generator-selection metadata. ReconcileUnrealizedCastsPass runs after all lowering (including the arith integer constant to q1.ir.move rewrite applied by PulseToQ1LoweringPass) has substituted the values that made the index-to-Q1-register bridging casts identity casts, and before register allocation, which requires the module to be free of builtin.unrealized_conversion_cast.

Parameters:
Return type:

PassPipeline

Returns:

Ordered pipeline ending in strict Qblox pre-emission verification.