Stim circuit import

Install the optional Stim integration before importing this module:

uv add "graphqomb[stim]"

The circuit importer converts supported Stim circuits into GraphQOMB measurement patterns. It accepts initial Pauli resets, Clifford unitary blocks, and Pauli measurement blocks separated by TICK. The supported Pauli measurement instructions are M/MZ, MX, MY, MXX, MYY, MZZ, and MPP.

Every unitary block is normalized by graphqomb.stim_glue.transpiler to the four Clifford J(angle) gates (H = J(0), HS = J(pi/2), HZ = J(pi), HS_DAG = J(-pi/2), corresponding to X+/Y-/X-/Y+ measurements) plus CZ before graph conversion. This accepts all fixed one- and two-qubit Clifford gates exposed by Stim as well as SPP and SPP_DAG. Each basis gate becomes one GraphQOMB J primitive.

Here, circuit foliation means constructing an MBQC pattern from the operations of a Clifford circuit, including gates, resets, measurements, and feedback. We distinguish this circuit construction from code foliation for memory operations such as CSS-code memories. YFoliation selects the graph construction for products containing Y factors.

For closed syndrome-extraction windows, first apply Round-preserving syndrome extraction and pass its result.circuit to this importer. Record references already include any readout permutation; Pauli feedback follows the ordinary xflow/zflow import path.

Initial reset instructions

Without an explicit reset, an input uses the Z+ state (|0>), matching Stim’s default initialization. Both M 0 and R 0; M 0 therefore return zero noiselessly. To prepare |+>, specify RX before the qubit’s first quantum use. This default applies to Stim circuit import; GraphQOMB’s general input initialization default remains X+.

Leading reset instructions determine the positive Pauli eigenstate used for an input: R/RZ initializes Z+, RX initializes X+, and RY initializes Y+. A reset is initial when its target qubit has not previously participated in a unitary or measurement operation. Operations on other qubits do not prevent a later initial reset. Repeated leading resets are accepted, and the last reset on each qubit determines its initialization state.

Leading resets are normalized together with the Clifford gates in their TICK block. When the resulting state is a positive Pauli eigenstate, the gates are absorbed into R, RX, or RY before graph construction. For example, R followed by H becomes RX and does not add a gate node or shift that input to an earlier z layer.

Stim canonicalizes the Z-axis aliases internally, so the importer accepts both M and MZ for Z measurement and both R and RZ for Z reset. Stim export uses MZ for Z measurement and R for Z reset.

A mid-circuit reset starts a new wire on a fresh internal qubit index: the reset and every later operation on that Stim qubit continue on the new wire, which is initialized in the positive eigenstate of the reset axis at the qubit’s XY coordinates on the next z layer. The reset discards the previous state, so the new wire carries no outcome conditioning; when several resets act on the qubit before its next quantum use, the last reset determines the axis. A replaced wire that was not consumed by a direct measurement remains a pattern output, which is channel-equivalent to the trace-out reset on every recorded measurement. Combined measurement-reset instructions (MR/MRZ, MRX, and MRY) are imported as a direct measurement whose continuation re-prepares the measured axis eigenstate unconditionally.

StimImportResult.qubit_to_stim maps every fresh index back to the original Stim qubit id. Because that mapping is many-to-one for a reset qubit, stim_to_final_qubit gives the index of its last lifetime; the earlier indices are discarded history. Equivalence to the source circuit is equivalence of the recorded measurements, not of the pattern’s output channel: a replaced live wire is a purification of Stim’s trace-out reset, so a caller that reads every pattern output must trace out the outputs that are not named by stim_to_final_qubit.

Internally each reset lifetime also receives its own Stim qubit id, and the StimMppExtraction metadata of an MPP block (supports, stim_to_column, and column_to_stim) reports the lifetime that each product measured. A qubit measured before and after a reset therefore occupies a different column in each block, and the same physical qubit may carry different ids across blocks. StimImportResult.wire_to_stim maps every such id back to the original circuit id; it is the identity for qubits that are never reset mid-circuit.

Within one optimization block, single-qubit Cliffords preceding a terminal measurement are absorbed into its signed Pauli basis when its post-state is not reused. For example, H 0; M 0 becomes MX 0, and S 0; MX 0 becomes MY !0. These readouts need no extra gate node. TICK barriers and measurements whose qubits are reused limit this folding; the latter must preserve the post-measurement state.

Single-qubit measurements assign an AxisMeasBasis directly to the measured data-lane endpoint without replacing that node or its coordinate. They do not create an MPP extraction or an ancillary parity measurement node. Inverted single-qubit measurement targets select the minus sign of that node’s basis. A direct single-qubit measurement ends that wire: a later quantum operation on the same qubit continues on a fresh internal qubit index initialized in the positive eigenstate of the measurement axis and conditioned on the outcome through the correction flows. Reuse after an inverted plain target (M !q) is rejected because negative-eigenstate initialization cannot be represented, while an inverted measure-reset target may be reused because the reset discards the outcome.

The first two components of QUBIT_COORDS are used as the fixed spatial (x, y) position of each data lane. The importer supplies the temporal z component. Wires split from a mid-circuit reset inherit the original qubit’s (x, y). Fully coordinated and fully uncoordinated circuits both import silently; when only some wires carry QUBIT_COORDS the importer emits a UserWarning, because a mixture usually means coordinate metadata was lost upstream — for example reset lifetimes split onto fresh qubit ids by another tool, which cannot be mapped back to an original qubit. Every unitary TICK block is transpiled before placement. Its input layer starts at the preceding block’s output z, and all of its output nodes share the maximum transpiled depth of the block. A shorter data-wire chain is spread across that same interval. A live qubit with no operation in the block remains a single input/output node and is relocated directly to the common output layer; this adds no graph node or edge and does not change the circuit semantics.

Two-qubit measurements are parity measurements and are lowered to equivalent unsigned MPP products. Inverted targets in MXX, MYY, and MZZ are rejected by the direct importer; the rewriter can normalize them to signed MPP products. Signed MPP is supported: its product sign changes the ancilla measurement basis. The Type-I Y construction also contributes its own phase correction. Extraction supports and check matrices describe unsigned support; the pattern retains the measurement sign.

Classical Pauli feedback

Stim measurement-record controls are imported as explicit Pauli-frame corrections. CX rec[-k] q/CNOT rec[-k] q adds q to the controlling measurement node’s X-correction flow, CZ rec[-k] q adds it to the Z-correction flow, and CY rec[-k] q adds it to both. The equivalent reverse-target spellings XCZ q rec[-k] and YCZ q rec[-k] are also supported, as is the symmetric CZ q rec[-k] form. Batched target pairs in one Stim instruction are supported, including instructions mixing quantum and feedback pairs. The importer splits those pairs in source order; this also handles Stim’s automatic fusion of adjacent instructions.

A feedback instruction is a graph-fragment boundary, so its correction targets the data-lane node at that exact circuit position. Repeating the same record-controlled Pauli correction cancels by parity. A feedback record must refer to an earlier imported measurement; records idealized to zero produce no correction.

Frame corrections are applied at their target’s measurement, after every graph edge on the target exists. An X or Y feedback therefore also XORs Z corrections onto the neighbors the target becomes entangled with after the feedback position, which is what pushing the deferred X through those later CZ edges leaves behind. Neighbors entangled before the feedback position receive no compensation, and Z feedback commutes with CZ so it needs none.

A feedback record may come from an MPP ancilla or from a direct single-qubit measurement. Direct measurements are imported as measured output nodes, which are scheduled like ordinary measurements, so the controlling readout is always measured before any node that depends on its correction.

All MPP instructions within each feedback-free portion of one TICK block are represented by one combined extraction and are validated to commute. Anticommuting products in the same portion are rejected. Within a combined block, local stabilizer interactions are ordered Z -> Y -> X on each shared data qubit. If an odd number of shared-data-qubit pairs reverse the order of the same two stabilizers, the graph-state builder adds the required CZ edge between their ancillas. This rule is applied automatically for both Type I and Type II circuit foliation.

Only data-wire nodes contribute to the graph-generated X-correction flow; MPP ancilla nodes do not produce those corrections. After composing all graph fragments, the importer first derives the Z-correction flow from the odd neighborhood of this graph-generated X flow. It then XORs Stim’s explicit classical X/Y/Z feedback into the two correction maps, including the deferred-X compensation described above; a feedback entry never adopts the full odd-neighbor rule, only the neighbors entangled after its position. Both completed maps are passed directly to qompile() without Pauli simplification or an importer-specific fallback. Non-commuting measurements must be separated by TICK in the source circuit. Each unit has a distinct unmeasured output layer, which is composed with the next unitary or measurement fragment by qubit index. Pass y_foliation=YFoliation.TYPE_II to any of the three import entry points to use the three-layer Y-measurement construction; Type I is the default.

An MPP block starts at the preceding gate or MPP output layer and ends two z units later. Live lanes not used by that MPP block are relocated to the same output layer without adding nodes. Consequently, a composed output and the next active fragment input have the same z coordinate, and imported patterns do not mix 2D spatial coordinates with 3D spacetime coordinates.

The flattened ideal circuit is normalized before measurement-record analysis. The original qubit inventory is retained even when a unitary block optimizes to the identity. Records from single-qubit measurements, pair measurements, MPP, and ideal-zero MPAD replacements share one absolute index space. DETECTOR and OBSERVABLE_INCLUDE targets are resolved against that whole-circuit index space, and each StimMppExtraction retains the corresponding absolute record sets even when an annotation also references records outside that MPP unit.

Noise policy

Circuit-level Stim noise is intentionally not imported. GraphQOMB applies noise to the compiled MBQC pattern through its own noise-model API, where preparation, entanglement, measurement, and idle events differ from the source circuit operations.

Pure noise instructions are omitted during import. Error probabilities attached to Pauli measurements are also omitted while retaining the ideal measurement. Heralded noise records are retained as ideal zero-valued record positions so that later DETECTOR and OBSERVABLE_INCLUDE references remain aligned.

from graphqomb.qeccode import YFoliation
from graphqomb.stim_glue.importer import stim_text_to_pattern

result = stim_text_to_pattern(
    """
    RY 0
    R 1 2
    MX 0
    MYY 1 2
    DETECTOR rec[-2] rec[-1]
    """,
    y_foliation=YFoliation.TYPE_II,
)
pattern = result.pattern

API reference

Import supported Stim circuits into GraphQOMB patterns.

This module provides:

class graphqomb.stim_glue.importer.StimImportResult[source]

Bases: object

Result of importing a supported Stim circuit.

stim_to_qubit maps each Stim qubit id to the qubit index of its initial (input-side) wire. When a Stim qubit is used again after a direct single-qubit measurement or re-initialized by a mid-circuit reset, the importer issues a fresh internal qubit index for the new wire; qubit_to_stim maps every internal qubit index, including those continuation indices, back to its Stim qubit id. That mapping is many-to-one, so stim_to_final_qubit names the index of the last lifetime of each Stim qubit; the earlier indices of a reset qubit are its discarded history, whose wires may remain pattern outputs.

A mid-circuit reset also issues a fresh Stim qubit id for the reset lifetime, and that id is what mpp_extractions reports in supports, stim_to_column, and column_to_stim. wire_to_stim maps every such id back to the original circuit’s Stim qubit id; it is the identity on qubits that are never reset mid-circuit.

pattern

Imported measurement pattern.

Type:

Pattern

stim_to_qubit

Original Stim qubit id to the internal qubit index of its initial wire.

Type:

dict[int, int]

qubit_to_stim

Every internal qubit index to its original Stim qubit id.

Type:

dict[int, int]

mpp_extractions

Stabilizer data of each imported MPP block, keyed by wire id.

Type:

tuple[StimMppExtraction, …]

wire_to_stim

Wire (post-split Stim) id to original Stim qubit id.

Type:

dict[int, int]

stim_to_final_qubit

Original Stim qubit id to the internal qubit index of its last lifetime.

Type:

dict[int, int]

__init__(pattern, stim_to_qubit, qubit_to_stim, mpp_extractions, wire_to_stim, stim_to_final_qubit)
graphqomb.stim_glue.importer.stim_file_to_pattern(path, *, coord_dims=2, schedule_strategy=CircuitScheduleStrategy.PARALLEL, y_foliation=YFoliation.TYPE_I, merge_safe_ticks=False, schedule_config=None, schedule_timeout=60)[source]

Import a supported Stim file into a GraphQOMB pattern.

When merge_safe_ticks is true, TICK boundaries whose removal cannot change the imported semantics are dropped before import; see stim_circuit_to_pattern. schedule_config and schedule_timeout select the pattern-level scheduler; see stim_circuit_to_pattern.

Returns:

Imported pattern, qubit mapping, and MPP extraction metadata.

Return type:

StimImportResult

graphqomb.stim_glue.importer.stim_text_to_pattern(text, *, coord_dims=2, schedule_strategy=CircuitScheduleStrategy.PARALLEL, y_foliation=YFoliation.TYPE_I, merge_safe_ticks=False, schedule_config=None, schedule_timeout=60)[source]

Import supported Stim text into a GraphQOMB pattern.

When merge_safe_ticks is true, TICK boundaries whose removal cannot change the imported semantics are dropped before import; see stim_circuit_to_pattern. schedule_config and schedule_timeout select the pattern-level scheduler; see stim_circuit_to_pattern.

Returns:

Imported pattern, qubit mapping, and MPP extraction metadata.

Return type:

StimImportResult

graphqomb.stim_glue.importer.stim_circuit_to_pattern(circuit, *, coord_dims=2, schedule_strategy=CircuitScheduleStrategy.PARALLEL, y_foliation=YFoliation.TYPE_I, merge_safe_ticks=False, schedule_config=None, schedule_timeout=60)[source]

Import a supported Stim circuit into a GraphQOMB pattern.

schedule_config controls how the imported pattern is scheduled: when given, a graphqomb.scheduler.core.Scheduler is built from the imported graph and flows, solved with that configuration (schedule_timeout bounds the CP-SAT solve time in seconds), and passed to graphqomb.qompiler.qompile; when None, qompile schedules with its default greedy strategy.

The importer supports initial Pauli resets, Clifford unitary blocks, and Pauli measurement blocks. Inputs without an explicit reset start in the positive Z eigenstate (|0>), matching Stim’s default initialization. Stim R, RX, and RY instructions are imported as positive Z-, X-, and Y-eigenstate input initialization, respectively, when they occur before any other quantum operation on the target qubit. Adjacent Clifford gates are folded into those initial Pauli states when possible. Stim noise instructions and measurement-error probabilities are omitted because circuit-level noise is outside the GraphQOMB import model. Measurement-record-controlled Pauli gates are imported as X/Z correction-flow entries at their circuit positions; an X or Y feedback additionally contributes Z corrections on the neighbors its target becomes entangled with after the feedback position, so deferred frame-based application matches the circuit-position semantics.

Unitary gates may share a TICK block with Pauli measurements. Single-qubit Cliffords ahead of a measurement are folded into the measurement basis when the measured qubit is never used again; any remaining mixed block is split into internal unitary, measurement, and feedback layers that preserve the original instruction order, each advancing the z coordinate by one layer.

A Stim qubit may be used again after a direct single-qubit measurement. The importer issues a fresh internal qubit index for the post-measurement wire: a new node is placed at the qubit’s XY coordinates on the next z layer, initialized in the positive eigenstate of the measurement axis, and the measurement outcome conditions the continuation through the pattern’s correction flows. Measure-reset gates (MR, MRX, MRY) work the same way except that the reset re-prepares the positive eigenstate unconditionally, so the continuation carries no outcome correction. Reuse after an inverted plain measurement target (M !q) is not supported because negative-eigenstate initialization cannot be represented; an inverted measure-reset target may be reused since the reset discards the outcome. Reuse is decided on the normalized circuit: single-qubit Cliffords after a measurement that cancel to the identity during per-block optimization do not count as later use, so the measured wire ends and the pattern has one output fewer than the raw instruction stream suggests.

A qubit may also be re-initialized with an explicit mid-circuit R, RX, or RY. The importer splits each reset-started lifetime onto a fresh internal qubit index: the new wire starts at the qubit’s XY coordinates on the next z layer in the positive eigenstate of the reset axis, with no outcome conditioning because the reset discards the previous state. A wire that was not consumed by a direct measurement before the reset remains a pattern output; leaving it coherent and untouched is channel-equivalent to Stim’s trace-out reset on every recorded measurement, provided that discarded output is traced out rather than read. qubit_to_stim maps the fresh indices back to the original Stim qubit id, and stim_to_final_qubit selects the live one among them.

DETECTOR instruction tags are preserved: each imported detector’s tag is carried into pattern.frame.parity_check_tags (aligned with parity_check_group) and re-emitted by graphqomb.stim_glue.compiler.stim_compile, so post-selection flag detectors (DETECTOR[type=flag]) survive the import/export round trip.

TICK boundaries are optimization barriers: gates in different TICK blocks are never cancelled against each other or folded into an adjacent reset or measurement, so a finely ticked circuit can import to a much larger graph than the same instructions in fewer blocks. When merge_safe_ticks is true, the importer drops every TICK whose removal cannot change the imported semantics before analyzing the circuit. Because per-qubit instruction order and measurement-record order survive block normalization, the only TICKs kept are those separating two blocks that both contain Pauli-product measurements, preserving the per-block product grouping (one StimMppExtraction per block, block-level commutation validation, and the block’s foliation structure). Node coordinates come out z-compressed because merged blocks share layers.

Each reset lifetime also gets its own Stim qubit id internally, and the mpp_extractions metadata (supports, stim_to_column, and column_to_stim) reports the lifetime that an MPP product actually measured, so a qubit measured before and after a reset occupies a distinct column in each block. wire_to_stim maps those ids back to the original circuit ids.

Returns:

Imported pattern, qubit mapping, and MPP extraction metadata.

Return type:

StimImportResult

Raises:

ValueError – If the circuit uses unsupported instructions or invalid coordinates, or if schedule_config is given and no schedule is found.