Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.3.0] - 2026-07-02¶
Added¶
Chunked execution (max_circuits_spec)¶
max_circuitsbackend option —QMBackend,QMSamplerV2, andQMEstimatorV2accept amax_circuitsoption. When the circuit/PUB count exceeds it, execution is automatically split into consecutive QUA programs; results are stitched back transparently.compute_chunk_layout()inqua_programs.py— plans the per-program circuit/PUB grouping for a givenmax_circuitslimit.compute_locator()inqua_programs.py— inverts a chunk layout into aglobal_index → (chunk_idx, local_idx)mapping used during result assembly. Previously inlined as a dict comprehension in three separate places.stream_assembly.py— shared helpersbit_array_from_stream()andbit_array_from_measurement_stream()that assembleBitArrayresults from QUA streams, handling both standard QM (flat output) and IQCC cloud (may carry an extra leading dimension) via areshapenormalisation step.IQCCJobMixin.status()— IQCC-aware status check: sinceCloudJob.statusis unconditionally"completed"(IQCC execution is synchronous), this method inspects_run_data["stderr"]instead. ReturnsJobStatus.ERRORwhen the cloud stderr contains a Python traceback,JobStatus.DONEotherwise. Inherited by bothIQCCSamplerJobandIQCCEstimatorJob.aggregate_job_statuses()iniqcc_job_mixin.py— shared status-aggregation helper (worst-case across all chunk jobs) used by bothQMJob.status()andQMPrimitiveJob.status().QMJob.program— backward-compatible property alias forprograms[0].
Fixed¶
Chunked execution (max_circuits_spec)¶
IQCCJob.submit()— was executing onlyprograms[0], silently dropping all chunks beyond the first for IQCCbackend.run()calls with chunking; now loops over all programs.QMSamplerJob.submit()simulate path — was always submitting onlyprograms[0]; now iterates all chunks so multi-program simulation works correctly.IO1/IO2 parameter push on
QmPendingJob—wait_until_job_is_pausedrequires aRunningQmJob; the compile+add_compiled refactor passed a pending job. BothQMSamplerJob.submit()andQMEstimatorJob.submit()now callpending.wait_for_execution()before pushing parameters and store the resultingRunningQmJobin_qm_jobs.Result(results=…)single-circuit — was passing a bareExperimentResult(not a list) for single-circuit runs, causingTypeErroronresult.results[0]; always passes a list now.IQCC partial execution —
IQCCSamplerJobandIQCCEstimatorJobnow initializeself._qm_jobs = []before the execute loop and append incrementally; a mid-loop failure no longer leaves_qm_jobs = None, making partial state visible.IQCCJobMixin.status()/result()empty-list false DONE —self._qm_jobs = [](set before the loop, never appended to when the firstexecute()raises) previously passed theis Noneguard and returnedJobStatus.DONE; guard now uses a falsy check covering bothNoneand[].wait_for_execution()mid-loop failure — a failure on chunk k previously left a partial_qm_jobslist and caused a crypticIndexErrorin_result_function; now raisesRuntimeErrornaming the chunk index and the circuit/PUB indices in that chunk so the user knows which job to isolate.QMJob.submit()dead cloud kwargs — removed aCloudQuantumMachinekwargs block (timeout / terminal_output) that silently dropped the user-specified timeout on multi-program cloud jobs;IQCCJoboverridessubmit()entirely so the path was unreachable.Status mapping duplication —
QMJob.status()andQMPrimitiveJob.status()had identical status-string →JobStatusmapping dicts inlined separately; both now delegate to the sharedaggregate_job_statuses()helper.
Quarc hybrid integration (quarc-support)¶
Quarc integration for OPNIC stream assignment —
ParameterPool.prepare_opnic_quarc_hybrid_packets()deterministically attaches incoming/outgoing stream IDs toParameterTableand standaloneParameterobjects before QUA variable declaration, replacing the legacyopnic_wrapper-based path.default_quarc_struct_name()— helper to keep struct naming consistent between this provider and the Quarc build system.quarc_emit.py— translation helpers from QUA types to Quarc annotations (quarc_atomic_for,quarc_annotation_for,quarc_direction_for,build_quarc_struct) without prematurely consuming global stream IDs.Dual Quarc emission pipelines in
ParameterPool:Module-first —
ParameterPool.from_quarc_module(module)rebuilds OPNIC-backedParameterTable/Parameterobjects from an existing QuarcBaseModule.Parameter-first —
ParameterPool.to_quarc_module()lazily binds (or creates) a Quarc module and defers struct emission until explicitly requested.
Optional
[quarc]extra inpyproject.tomlfor installing Quarc as a peer dependency.Lazy Quarc import — top-level
import qiskit_qm_providerno longer requiresquarc;QiskitQMModuleis loaded via__getattr__andQUARC_AVAILABLEprobes availability at import time.
OPNIC host↔OPX transport (formerly DGX_Q)¶
InputType.OPNIC— rebranded fromInputType.DGX_Qfor classical host packet communication over the OP Network Interface Card.Quarc-managed struct handles —
ParameterTableandParameterdelegatepush_to_opx,fetch_from_opx, andstream_backto QuarcQuaStructHandleendpoints instead of manual packet assembly.Standalone OPNIC parameters — synthetic
ParameterTableadapter lets OPNICParameterinstances perform I/O without explicit table binding; lazy promotion on first transport call.Bidirectional stream resolution —
incoming_stream_idandoutgoing_stream_idproperties onParameterandParameterTable, resolved from Quarc handle metadata.Parameter.assign_from_io(io_index)— non-blocking assignment of global IO registers (IO1/IO2) to scalar variables without pausing QUA execution.
QiskitQMModule¶
QiskitQMModule—quarc.BaseModuleextension unifying OPNIC and non-OPNIC parameter declaration, persistence, and reconstruction:Automatic registration with
ParameterPoolat construction.Sweeps pre-existing OPNIC tables and pending standalone parameters onto the module.
parameter_specsfield for serialising non-OPNIC parameters.reconstruct_non_opnic()anditer_all_params()for round-trip module state.
QiskitQMModule.bind_connection(qmm)— serialises QMM host/port into module JSON; invoked automatically on firstQMBackend.qmaccess.QiskitQMModule.get_running_job()— retrieves the active QM job in both local backend mode and IQCC sync-hook mode.Default module allocation —
ParameterPool.quarc_module()/to_quarc_module()lazily allocate aQiskitQMModule(instead of a plainquarc.BaseModule) and sweep pending parameters on first access.
Parameter and ParameterTable API¶
Unified interface —
ParameterandParameterTableshare canonical method names (declare,rcv,declare_stream,reset_qua) so callers never branch on concrete type.1-field struct promotion —
ParameterPool.from_quarc_module()promotes Quarc structs with exactly one field to a standaloneParameter.QuaFieldTablemixin — shared field-access pattern for table-like QUA objects.QUA program scope guards —
is_inside_scope(),require_qua_program(), and@requires_qua_programenforce that QUA variable accessors are used insidewith program():.assign_struct_with_table()— QUA macro copying declaredParameterTablevalues into a QuarcQuaStructHandlewith runtime validation and shape checks.pack_register_to_int()— helper for bit-packing classical register values into a QUA integer scalar.Zero-argument
push_to_opx()—Parameter.push_to_opx()andParameterTable.push_to_opx()default to streaming current values when called without arguments.
Circuit compilation and measurement outputs¶
QuaCircuitCompilation— first-class wrapper for qm-qasm compilation results returned byQMBackend.quantum_circuit_to_qua().MeasurementOutcomeTable(comp.outputs) — compilation-local measurement handles wired from qm-qasmresult_program:comp.outputs["c"]/comp.outputs.get_parameter("c")for bool vars and field handles.comp.outputs.state_ints["c"]for lazy-packed integer scalars.comp.outputs.streams["c"]for per-fielddeclare_stream()handles.
MeasurementRegisterField— per-register handle exposing measurement outcome accessors without polluting the runtimeParameterPool.Loose QASM3 classical bits —
_LooseBitRegisterand extendedget_measurement_outcomes()surface unassigned classical bits under synthetic_bitNkeys.
Gates and backend utilities¶
FSimGate— parameterized fSim gate (θ, φ) withQuantumCircuit.fsim()monkey-patch.Expanded additional gates —
SYGate,SYdgGate, andCRGateretained and registered inget_extended_gate_name_mapping().add_basic_macros(backend)— exported utility to populate backends with standard single-qubit gate, measure, and reset macros; syncs the backend target automatically.quam_macrosmodule — dedicated home for superconducting qubit macros (MeasureMacro,ResetMacro,VirtualZMacro,DelayMacro,IdMacro), extracted fromiqcc_calibration_tools.Non-destructive macro injection —
add_basic_macrosonly fills emptyqubit.macrosdicts and absent pair-levelczentries, preserving user-defined macros.Improved flux-tunable channel mapping —
FluxTunableTransmonBackendqubit→element mapping returns variable-length channel tuples including TWPA pump channels when present.QMInstructionPropertiesquam_macrosupport — explicit QuAM macro input takes priority overqua_pulse_macrofor property inference.
Runtime and cross-environment job access¶
qiskit_qm_provider.runtime— helpers for hybrid classical/QUA workflows:Sync-hook job reconstruction from IQCC CLI args (
-j/-q/-i/-p).Running-job polling compatible with QOP 3.x (
get_jobs(status=["Running"])) and QOP 2.x (list_open_qms()fallback).
Self-contained IQCC sync hooks¶
Dependency-free sync-hook generation —
generate_sync_hook_sampler()andgenerate_sync_hook_estimator()now emit a sync hook that runs on any IQCC user’s cloud runtime: the generated code imports onlyfrom iqcc_cloud_client.runtime import get_qm_joband no longer requiresqiskit_qm_providerornumpyto be installed on the sync-hook side. Parameter tables and observable indices are serialised to plain-Python data; the push logic is rendered from Jinja templates (qiskit_qm_provider/job/templates/).INPUT_STREAMandIO1/IO2over IQCC — both delivery mechanisms are fully runnable from the sync hook.INPUT_STREAMpushes viajob.push_to_input_stream(name, value);IO1/IO2setjob.set_io_values(...)with an inlined pause/resume loop (an in-template equivalent ofqualang_tools.results.wait_until_job_is_paused), soqualang_toolsis not required in the cloud runtime either.Per-parameter type coercion — streamed values are coerced according to each parameter’s QUA type (
int/fixed/bool) instead of a blanketfloat()cast.jinja2added to the[iqcc]optional extra.
Documentation¶
Primitives guide — Running on IQCC Cloud — explains the self-contained sync-hook behavior, supported
input_typemechanisms (INPUT_STREAM,IO1/IO2), and theOPNIClimitation on IQCC jobs.Measurement outputs guide — locality model, accessor contract, RL reward and QEC worked examples.
Error-correction workflow guide — expanded hybrid QEC patterns (syndrome streaming, detection events, decoder integration).
Parameter table and backend API docs — updated Sphinx stubs, scope-guard reference, and OPNIC workflow documentation.
Changed¶
IQCC sync-hook generation rewritten — the previously emitted hook re-declared
ParameterTable/Parameterobjects and calledpush_to_opx()(requiringqiskit_qm_provider+numpyin the cloud runtime). It now injects plain data into a Jinja template that talks to the QM job directly.QMSamplerV2.run()/QMEstimatorV2.run()raiseNotImplementedErrorwheninput_type=InputType.OPNICis combined with an IQCC job (IQCCSamplerJob/IQCCEstimatorJob), since OPNIC transport is not yet available over the cloud sync hook — useINPUT_STREAMorIO1/IO2.QMBackend.quantum_circuit_to_qua()now returnsQuaCircuitCompilationinstead of a raw qm-qasmCompilationResult.InputType.DGX_Qrenamed toInputType.OPNICthroughout the codebase, primitives options, and documentation. OPNIC is presented as a QUARC-backed host↔OPX transport; the legacy “DGX Quantum” naming is fully retired from code and docs.Parameter.dgx_struct/opnic_structreplaced byParameter.struct_type(and equivalent onParameterTable); theopnic_structname was removed outright (see Removed).ParameterPool.prepare_dgx_quarc_hybrid_packets()renamed toprepare_opnic_quarc_hybrid_packets().Internal callers migrated to canonical names — library code (
qua_programs,qm_backend,pulse_support_utils) now callsdeclare()/rcv()instead of the deprecateddeclare_variable(s)/load_input_value(s), so building sampler/estimator programs no longer self-emitsDeprecationWarning.get_measurement_outcomes()sources every entry fromcomp.outputsand exposesis_array/lengthkeys (Parameterconvention). Loose clbits are returned under per-bit keys_bit0,_bit1, … (one entry each) instead of a single packed_bitregister — loose bits are independent and are no longer packed into one integer.state_intis always lazily available (no construction-time gate).MeasurementRegisterFieldnow follows theParametershape convention — it carrieslength(0for a scalar loose clbit, the register width otherwise) and exposesis_array(length > 0) andlengthas part of the sharedTableFieldProtocol. Dropped the bespokeregister_size/var_is_arraystate;pack_register_to_intremains the single place that adapts packing to the wired QUA variable.MeasurementRegisterField/MeasurementOutcomeTable.from_compilation/QuaCircuitCompilationdropped thecompute_state_intconstructor argument —state_intis a lazy property computed only on access.ParameterINPUT_STREAM declaration uses the current qm-qua API (declare_input_stream(type, name=..., value=...)); output streams still usedeclare_stream(). A QUA 1.3 client/stream_idpath is scaffolded inparameter.pybut remains commented until qm-qua ≥ 1.3 (note:declare_output_streamis not exported in qm-qua 1.2.x).ParameterTable.fetch_from_opx()uses bulkrecv(size, index)instead of per-element iteration.QMBackend.compile()param_tabletyping broadened fromList[...]toSequence[...].IQCCProvideralways defaults toFluxTunableQuamwhenquam_clsis not supplied (removed conditional fallback import).add_basic_macrosstores pulse references by name (string) in QuAM macro definitions.Deprecation aliases — historical method names (
declare_variable(s),load_input_value(s),reset_var(s),declare_streams) preserved via_DeprecatedAliasdescriptor; access emitsDeprecationWarning(scheduled removal in v1.2).
Deprecated¶
declare_variables/declare_variable→ usedeclare.load_input_values/load_input_value→ usercv.reset_vars/reset_var→ usereset_qua.declare_streams→ usedeclare_stream.
Removed¶
Legacy
opnic_wrapper-based OPNIC transport insideParameterPool.OpnicPacketBindingprotocol and internal OPNIC orchestration modules (quarc_naming,quarc_live_module).ParameterPoolmanual stream-ID rebinding methods (rebind_parameter_table_id, etc.) in favor of Quarc-managed handles.@opnic_checkdecorator (relaxed via standalone OPNIC adapter pattern).opnic_structalias onParameter/ParameterTable— removed outright (no deprecation cycle); OPNIC transport never shipped working, so it had no usable callers. Usestruct_typefor the Quarc struct type andvarfor the bound handle._LooseBitRegisterhelper class and the monotonic output-table name counter (reset_output_table_name_registry) — table names are now derived from currently-live tables, so they no longer grow unboundedly across a long-lived process.Dependency on
iqcc_calibration_toolsfor single-qubit macros (replaced byquam_macros).
Fixed¶
Input stream declarations — aligned with the installed qm-qua
declare_input_streamsignature (legacy(type, name=..., value=...)path active; QUA 1.3 client-target syntax prepared but not enabled).ResetMacro pi_12 pulse forwarding — corrected argument pass-through in single-qubit reset macro.
Quarc packet naming collision — synthetic table names avoid matching single-parameter field names that trigger Quarc complaints.
Ill-defined Quarc
recv()bypass — workaround for incomplete Quarc receive API.QUA array constructors —
QUAArrayandQUA2DArrayoverride__new__to preventqua_typedouble-binding during positional init.Backend utils docstring cleanup in
add_basic_macros.QuAM macros folder location corrected after module extraction.