# circuit
Source: https://bloqade.quera.com/tsim/api-reference/circuit
Quantum circuit wrapper around stim.Circuit with non-Clifford gate support.
## class `Circuit`
```python theme={null}
Circuit(stim_program_text: str = '')
```
Quantum circuit as a thin wrapper around stim.Circuit.
Circuits are constructed like stim circuits:
\>>> circuit = Circuit('''
... H 0
... T 0
... CNOT 0 1
... M 0 1
... ''')
### `append`
```python theme={null}
append(name: str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit, targets: int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString] = (), arg: float | Iterable[float] | None = None, tag: str = '') -> None
```
Append an operation into the circuit.
**Parameters:**
* `name` (`str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit`) — The name of the operation's gate (e.g. "H" or "M" or "CNOT"). This argument can also be set to a `stim.CircuitInstruction` or `stim.CircuitInstructionBlock`, which results in the instruction or block being appended to the circuit. The other arguments (targets and arg) can't be specified when doing so.
* `targets` (`int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString]`) — The objects operated on by the gate. This can be either a single target or an iterable of multiple targets. Each target can be: An int: The index of a targeted qubit. A `stim.GateTarget`: Could be a variety of things. Methods like `stim.target_rec`, `stim.target_sweet`, `stim.target_x`, and `stim.CircuitInstruction.__getitem__` all return this type. A `stim.PauliString`: This will automatically be expanded into a product of pauli targets like `X1*Y2*Z3`.
* `arg` (`float | Iterable[float] | None`) — The "parens arguments" for the gate, such as the probability for a noise operation. A double or list of doubles parameterizing the gate. Different gates take different parens arguments. For example, X\_ERROR takes a probability, OBSERVABLE\_INCLUDE takes an observable index, and PAULI\_CHANNEL\_1 takes three disjoint probabilities.
* `tag` (`str`) — A customizable string attached to the instruction.
### `append_from_stim_program_text`
```python theme={null}
append_from_stim_program_text(stim_program_text: str) -> None
```
Append operations described by a Stim format program into the circuit.
Supports the same shorthand syntax as the constructor.
### `approx_equals`
```python theme={null}
approx_equals(other: object, atol: float) -> bool
```
Check if a circuit is approximately equal to another circuit.
Two circuits are approximately equal if they are equal up to slight
perturbations of instruction arguments such as probabilities. For example,
`X_ERROR(0.100) 0` is approximately equal to `X_ERROR(0.099)` within an absolute
tolerance of 0.002. All other details of the circuits (such as the ordering of
instructions and targets) must be exactly the same.
**Parameters:**
* `other` (`object`) — The circuit, or other object, to compare to this one.
* `atol` (`float`) — The absolute error tolerance. The maximum amount each probability may have been perturbed by.
**Returns:**
* `bool` — True if the given object is a circuit approximately equal up to the
* `bool` — receiving circuit up to the given tolerance, otherwise False.
### `cast_to_stim`
```python theme={null}
cast_to_stim() -> stim.Circuit
```
Return self with type cast to `stim.Circuit`.
This is useful for passing the circuit to functions that expect a
`stim.Circuit`.
### `compile_detector_sampler`
```python theme={null}
compile_detector_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledDetectorSampler
```
Compile circuit into a detector sampler.
Connected components whose single output is deterministically given by
one f-variable are handled via a fast direct path (no compilation or
autoregressive sampling). Remaining components go through the full
compilation pipeline.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledDetectorSampler` — A CompiledDetectorSampler that can be used to sample detectors and observables.
### `compile_m2d_converter`
```python theme={null}
compile_m2d_converter(skip_reference_sample: bool = False) -> stim.CompiledMeasurementsToDetectionEventsConverter
```
Create a measurement-to-detection-event converter for the given circuit.
The converter can efficiently compute detection events and observable flips
from raw measurement data.
The converter uses a noiseless reference sample, collected from the circuit
using stim's Tableau simulator during initialization of the converter, as a
baseline for determining what the expected value of a detector is.
Note that the expected behavior of gauge detectors (detectors that are not
actually deterministic under noiseless execution) can vary depending on the
reference sample. Stim mitigates this by always generating the same reference
sample for a given circuit.
**Parameters:**
* `skip_reference_sample` (`bool`) — Defaults to False. When set to True, the reference sample used by the converter is initialized to all-zeroes instead of being collected from the circuit. This should only be used if it's known that the all-zeroes sample is actually a possible result from the circuit (under noiseless execution).
**Returns:**
* `stim.CompiledMeasurementsToDetectionEventsConverter` — An initialized stim.CompiledMeasurementsToDetectionEventsConverter.
### `compile_sampler`
```python theme={null}
compile_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledMeasurementSampler
```
Compile circuit into a measurement sampler.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledMeasurementSampler` — A CompiledMeasurementSampler that can be used to sample measurements.
### `copy`
```python theme={null}
copy() -> Circuit
```
Create a copy of this circuit.
### `detector_error_model`
```python theme={null}
detector_error_model(decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true.
**Parameters:**
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. This is not supported by tsim and setting it to true will raise an error. The argument is present for compatibility with stim.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_gauge\_detectors=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. Note that, unlike in stim, logical observables are also allowed to be non-deterministic.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
### `diagram`
```python theme={null}
diagram(type: Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg'] = 'timeline-svg', tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, height: float | None = None, width: float | None = None, zoomable: bool = True, kwargs: Any = {}) -> Any
```
Return a diagram of the circuit, from a variety of options.
**Parameters:**
* `type` (`Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg']`) — The type of diagram. Available types are: "pyzx": A pyzx SVG of the ZX diagram of the circuit. "pyzx-dets": A pyzx SVG of the ZX diagram that is used to compute probabilities of detectors and observables. "pyzx-meas": A pyzx SVG of the ZX diagram that is used to compute probabilities of measurements. "timeline-svg": An SVG image of the operations applied by the circuit over time. Includes annotations showing the measurement record index that each measurement writes to, and the measurements used by detectors. "timeslice-svg": An SVG image of the operations applied between two TICK instructions in the circuit, with the operations laid out in 2d.
* `tick` (`int | range | None`) — Required for time slice diagrams. Specifies which TICK instruction, or range of TICK instructions, to slice at. Note that the first TICK instruction in the circuit corresponds tick=1. The value tick=0 refers to the very start of the circuit. Passing `range(A, B)` for a detector slice will show the slices for ticks A through B including A but excluding B. Passing `range(A, B)` for a time slice will show the operations between tick A and tick B.
* `rows` (`int | None`) — In diagrams that have multiple separate pieces, such as timeslice diagrams and detslice diagrams, this controls how many rows of pieces there will be. If not specified, a number of rows that creates a roughly square layout will be chosen.
* `filter_coords` (`Iterable[Iterable[float] | stim.DemTarget]`) — A list of things to include in the diagram. Different effects depending on the diagram. For detslice diagrams, the filter defaults to showing all detectors and no observables. When specified, each list entry can be a collection of floats (detectors whose coordinates start with the same numbers will be included), a stim.DemTarget (specifying a detector or observable to include), a string like "D5" or "L0" specifying a detector or observable to include.
* `height` (`float | None`) — Optional height for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types. For timeline-svg, when both height and width are None, the height is automatically determined based on the number of qubits. When only one dimension is given, the other is computed from the SVG aspect ratio.
* `width` (`float | None`) — Optional width for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types.
* `zoomable` (`bool`) — If True (default), wraps SVG diagrams in an interactive container with pan and Ctrl/Cmd+wheel zoom. Only applies to timeline-svg and timeslice-svg diagram types.
* `**kwargs` (`Any`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `Any` — An object whose `__str__` method returns the diagram, so that
* `Any` — writing the diagram to a file works correctly. The returned
* `Any` — object may also define methods such as `_repr_html_`, so that
* `Any` — ipython notebooks recognize it can be shown using a specialized
* `Any` — viewer instead of as raw text.
### `flattened`
```python theme={null}
flattened() -> Circuit
```
Return a copy of the circuit with all repeat blocks expanded.
### `from_file`
```python theme={null}
from_file(cls, filename: str) -> Circuit
```
Create a Circuit from a file.
**Parameters:**
* `filename` (`str`) — The filename to read the circuit from.
**Returns:**
* `Circuit` — A new Circuit instance.
### `from_stim_program`
```python theme={null}
from_stim_program(cls, stim_circuit: stim.Circuit) -> Circuit
```
Create a Circuit from a stim.Circuit object.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to wrap.
**Returns:**
* `Circuit` — A new Circuit instance.
### `get_graph`
```python theme={null}
get_graph() -> BaseGraph
```
Construct the ZX graph.
### `get_sampling_graph`
```python theme={null}
get_sampling_graph(sample_detectors: bool = False) -> BaseGraph
```
Get a ZX graph that can be used to compute probabilities.
This graph will be constructed as follows:
1. Double the ZX-diagram by composing it with its adjoint.
2. Connect all rec\[i] vertices to their corresponding adjoint rec\[i] vertices.
3. Add outputs:
(a) When sampling measurements (i.e. `sample_detectors` is False),
add output nodes for each measurement. Detectors and observables are
removed since they are ignored when sampling measurements.
(b) When sampling detectors and observables (i.e. `sample_detectors` is True),
add output nodes for each detector and observable. Only one set of detector
and observable nodes is kept, i.e., detectors and observables are not
composed with their adjoints.
**Parameters:**
* `sample_detectors` (`bool`) — If True, sample detectors and observables instead of measurements.
**Returns:**
* `BaseGraph` — A ZX graph for sampling.
### `inverse`
```python theme={null}
inverse() -> Circuit
```
Return the inverse of the circuit.
### `pop`
```python theme={null}
pop(index: int = -1) -> stim.CircuitInstruction | stim.CircuitRepeatBlock
```
Pops an operation from the end of the circuit, or at the given index.
**Parameters:**
* `index` (`int`) — Defaults to -1 (end of circuit). The index to pop from.
**Returns:**
* `stim.CircuitInstruction | stim.CircuitRepeatBlock` — The popped instruction or repeat block.
**Raises:**
* `IndexError` — The given index is outside the bounds of the circuit.
### `tcount`
```python theme={null}
tcount() -> int
```
Count the number of T gates in the circuit.
### `to_matrix`
```python theme={null}
to_matrix() -> Any
```
Convert circuit to matrix representation.
### `to_tensor`
```python theme={null}
to_tensor() -> Any
```
Convert circuit to tensor representation.
### `without_annotations`
```python theme={null}
without_annotations() -> Circuit
```
Return a copy of the circuit with all detector and observable annotations removed.
### `without_noise`
```python theme={null}
without_noise() -> Circuit
```
Return a copy of the circuit with all noise removed.
## class `CompiledDetectorSampler`
```python theme={null}
CompiledDetectorSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples detector and observable outcomes from a quantum circuit.
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None, prepend_observables: bool = False, append_observables: bool = False, separate_observables: bool = False, bit_packed: bool = False, use_detector_reference_sample: bool = False, use_observable_reference_sample: bool = False, postselection_mask: np.ndarray | None = None) -> np.ndarray | tuple[np.ndarray, np.ndarray]
```
Return detector samples from the circuit.
The circuit must define the detectors using DETECTOR instructions. Observables
defined by OBSERVABLE\_INCLUDE instructions can also be included in the results
as honorary detectors.
**Parameters:**
* `shots` (`int`) — The number of times to sample every detector in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
* `separate_observables` (`bool`) — Defaults to False. When set to True, the return value is a (detection\_events, observable\_flips) tuple instead of a flat detection\_events array.
* `prepend_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the start of the results.
* `append_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the end of the results.
* `bit_packed` (`bool`) — Defaults to false. When set, results are bit-packed.
* `use_detector_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with detector outcomes so that results represent deviations from the noiseless baseline. This should only be used when detectors are deterministic. Otherwise, it can unpredictably change the results.
* `use_observable_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with observable outcomes so that results represent deviations from the noiseless baseline. This should only be used when observables are deterministic. Otherwise, it can unpredictably change the results.
* `postselection_mask` (`np.ndarray | None`) — Optional boolean array of length `num_detectors`. When set, shots where any masked direct detector fires skip the JAX sampling loop. All `shots` rows are still returned: survivors contain the full sample, while discarded rows retain direct detector columns and fill component columns with `False`. Re-apply the mask to the returned detector columns to recover surviving shots.
**Returns:**
* `np.ndarray | tuple[np.ndarray, np.ndarray]` — A numpy array or tuple of numpy arrays containing the samples.
**Raises:**
* `ValueError` — If `separate_observables` is combined with `prepend_observables` or `append_observables`.
## class `CompiledMeasurementSampler`
```python theme={null}
CompiledMeasurementSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples measurement outcomes from a quantum circuit.
Uses sequential decomposition \[0, 1, 2, ..., n] where:
* compiled\_scalar\_graphs\[0]: normalization (0 outputs plugged)
* compiled\_scalar\_graphs\[i]: cumulative probability up to bit i
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None) -> np.ndarray
```
Sample measurement outcomes from the circuit.
**Parameters:**
* `shots` (`int`) — The number of times to sample every measurement in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
**Returns:**
* `np.ndarray` — A numpy array containing the measurement samples.
## `build_sampling_graph`
```python theme={null}
build_sampling_graph(built: GraphRepresentation, sample_detectors: bool = False) -> BaseGraph
```
Build a ZX graph for sampling from a GraphRepresentation.
This is the internal implementation of get\_sampling\_graph.
## `controlled_gate_decomposition_lines`
```python theme={null}
controlled_gate_decomposition_lines(gate: str, control1: int | str, control2: int | str, target: int | str, tag: str = '') -> list[str]
```
Return a Clifford+T decomposition for a controlled-controlled gate.
## `encode_t_tag`
```python theme={null}
encode_t_tag(user_tag: str = '') -> str
```
Encode a T-family gate tag while preserving an optional user tag.
## `enriched_stim_error`
```python theme={null}
enriched_stim_error(exc: ValueError, converted_text: str) -> ValueError
```
Improve stim parse errors for tsim-specific gates.
When stim raises a 'Gate not found' error for a gate that should have been
converted by shorthand\_to\_stim, this searches the converted text for the
unconverted usage and returns a more helpful error message.
## `expand_clifford_rotations`
```python theme={null}
expand_clifford_rotations(source: stim.Circuit) -> stim.Circuit
```
Return `source` with half-π parametric rotations expanded to Clifford gates.
`REPEAT` blocks are preserved structurally and expanded recursively.
## `get_detector_error_model`
```python theme={null}
get_detector_error_model(stim_circuit: stim.Circuit, allow_non_deterministic_observables: bool = True, decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true. This is achieved by converting logical
observables into detectors, calling the stim.Circuit.detector\_error\_model(allow\_gauge\_detectors=True), and then
reconverting the detectors back into observables in the detector error model.
WARNING: If the circuit has distance one, i.e. if there are errors that only flip logical observables,
this method will return an incorrect detector error model.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to compute the detector error model for.
* `allow_non_deterministic_observables` (`bool`) — Defaults to true. When set to true, the detector error model allows for non-deterministic observables. This is achieved by converting logical observables into detectors, calling the stim.Circuit.detector\_error\_model(allow\_gauge\_detectors=True), and then reconverting the detectors back into observables in the detector error model.
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. When allow\_non\_deterministic\_observables is set to true, decomposition is not supported.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_non\_deterministic\_observables=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. When set to true, these detectors are instead considered to be part of degrees freedom that can be removed from the error model. For example, if detectors D1 and D3 both anti-commute with a reset, then the error model has a gauge `error(0.5) D1 D3`. When gauges are identified, one of the involved detectors is removed from the system using Gaussian elimination. Note that logical observables are still verified to be deterministic, even if this option is set.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
## `is_clifford`
```python theme={null}
is_clifford(source: stim.Circuit) -> bool
```
Return True iff every instruction in `source` is Clifford.
Recurses into `REPEAT` block bodies.
## `parse_parametric_tag`
```python theme={null}
parse_parametric_tag(instruction: stim.CircuitInstruction) -> tuple[str, dict[str, Fraction]] | None
```
Parse the parametric tag on an instruction (e.g. `I[R_Z(theta=0.3*pi)]`).
Supports gates: R\_Z, R\_X, R\_Y, U3.
**Parameters:**
* `instruction` (`stim.CircuitInstruction`) — The stim instruction whose tag will be parsed.
**Returns:**
* `tuple[str, dict[str, Fraction]] | None` — Tuple of (gate\_name, params\_dict) when the instruction's tag is a
* `tuple[str, dict[str, Fraction]] | None` — well-formed parametric tag, or `None` when the tag is not
* `tuple[str, dict[str, Fraction]] | None` — parametric-looking (no `name(...)` shape, or empty).
**Raises:**
* `ValueError` — When the tag looks parametric (matches `name(...)`) but is malformed: a parameter value does not parse, the gate name is unknown, or the parameter keys do not match the expected set for the gate.
## `parse_stim_circuit`
```python theme={null}
parse_stim_circuit(stim_circuit: stim.Circuit, track_classical_wires: bool = False) -> GraphRepresentation
```
Parse a stim circuit into a GraphRepresentation.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to convert.
* `track_classical_wires` (`bool`) — Whether to track classical wires.
**Returns:**
* `GraphRepresentation` — A GraphRepresentation containing the ZX graph and all auxiliary data.
## `render_pyzx_d3`
```python theme={null}
render_pyzx_d3(stim_circ: stim.Circuit, kwargs: dict[str, Any]) -> GraphS
```
Render a stim circuit as a pyzx ZX diagram using d3.js.
**Parameters:**
* `stim_circ` (`stim.Circuit`) — The stim circuit to render.
* `kwargs` (`dict[str, Any]`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `GraphS` — A pyzx ZX diagram.
## `render_svg`
```python theme={null}
render_svg(c: stim.Circuit, type: str, tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, width: float | None = None, height: float | None = None, zoomable: bool = True) -> Diagram
```
Render a stim circuit timeline/timeslice diagram with custom labels.
## `shorthand_to_stim`
```python theme={null}
shorthand_to_stim(text: str) -> str
```
Convert tsim shorthand syntax to valid stim instructions.
**Converts:**
CCZ 0 1 2 → Clifford+T decomposition of CCZ
CCX 0 1 2 → Clifford+T decomposition of CCX/Toffoli
T 0 1 → S\[T] 0 1
T\_DAG 0 1 → S\_DAG\[T] 0 1
TPP X0*Y1 → SPP\[T] X0*Y1
TPP\_DAG X0*Y1 → SPP\_DAG\[T] X0*Y1
R\_Z(0.3) 0 → I\[R\_Z(theta=0.3*pi)] 0
R\_X(0.25) 0 → I\[R\_X(theta=0.25*pi)] 0
R\_Y(-0.5) 0 → I\[R\_Y(theta=-0.5*pi)] 0
R\_XX(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] X0*X1
R\_YY(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] Y0*Y1
R\_ZZ(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] Z0*Z1
R\_PAULI(0.3) X0*Y1 → SPP\[R\_PAULI(theta=0.3*pi)] X0*Y1
U3(0.3, 0.24, 0.49) 0 → I\[U3(theta=0.3*pi, phi=0.24*pi, lambda=0.49\*pi)] 0
## `stim_to_shorthand`
```python theme={null}
stim_to_shorthand(text: str) -> str
```
Convert expanded stim annotations back to tsim shorthand.
Rewrites:
* I\[U3(theta=θ*pi, phi=φ*pi, lambda=λ\*pi)] → U3(θ, φ, λ)
* I\[R\_X(theta=α\*pi)] / I\[R\_Y(...)] / I\[R\_Z(...)] → R\_X(α) / R\_Y(α) / R\_Z(α)
* SPP\[R\_PAULI(theta=α*pi)] P0*P1 → R\_PP(α) 0 1 (P ∈ \{X, Y, Z})
* SPP\[R\_PAULI(theta=α*pi)] X0*Y1 → R\_PAULI(α) X0\*Y1
* SPP\[T] → TPP
* SPP\_DAG\[T] → TPP\_DAG
* S\[T] → T
* S\_DAG\[T] → T\_DAG
# compile
Source: https://bloqade.quera.com/tsim/api-reference/compile/compile
Compilation of ZX graphs into JAX-compatible data structures.
## class `CompiledScalarGraphs`
```python theme={null}
CompiledScalarGraphs()
```
JAX-compatible compiled representation of a list of scalar ZX graphs.
The scalar for each graph is a product of four term families, multiplied by
a per-graph `ScalarPrefactor` (global phase, floatfactor, `2^power2`,
optional approximate complex floatfactor). All arrays are static-shaped so
the whole struct can be traced under `jax.jit`.
## class `HalfPiPhases`
```python theme={null}
HalfPiPhases()
```
Sum of `exp(i·j·π·⊕params / 2)` terms with `j ∈ \{1, 3\}`.
Terms sharing a parameter bitstring have been combined to a single stored
coefficient `j' ∈ \{1, 2, 3\}` (see `_compile_halfpi_phases`). Coefficients
are stored in eighth-turn units — i.e. as `2·j'` — so the evaluator can
reuse the `ω = e^(iπ/4)` phase table.
Padded slots use `0` (the additive identity for phase sums), so padded
entries contribute nothing to the summed exponent.
Shapes are `(num_graphs, max_terms)` except `params` which is
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate ω^(Σ coeffs · parity) per graph, batched over param\_vals.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `NodePhases`
```python theme={null}
NodePhases()
```
Product of `1 + exp(i·(α + ⊕params)·π)` terms, one factor per stored term.
Padded slots use `0` for both `phases` and `params`; the evaluator masks
padded slots to the multiplicative identity using `counts`.
Shapes are `(num_graphs, max_terms)` except `params` which is
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (1 + ω^(4·parity + phase)) per graph, batched over param\_vals.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `PhasePairs`
```python theme={null}
PhasePairs()
```
Product of `1 + e^(iα) + e^(iβ) − e^(i(α+β))` terms.
Each of `α` and `β` combines a constant phase with a parameter parity.
Padded slots use `0` and are masked to the multiplicative identity using
`counts`.
Shapes are `(num_graphs, max_terms)` except `*_params` which are
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (1 + ω^α + ω^β - ω^(α+β)) per graph, batched.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `PiProducts`
```python theme={null}
PiProducts()
```
Product of `(-1)^(ψ · φ)` terms, with ψ and φ each a parity expression.
Each side is encoded as a constant bit plus a parameter bitmask. Padded slots
use `0` everywhere; a padded term contributes `(-1)^0 = 1` to the product.
Shapes are `(num_graphs, max_terms)` except `*_params` which are
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (-1)^(ψ·φ) per graph as a real ±1 exact scalar.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`, with values
* `ExactScalarArray` — in \{+1, -1} represented exactly.
## class `ScalarPrefactor`
```python theme={null}
ScalarPrefactor()
```
Per-graph static scalar prefactor — independent of parameter values.
Each graph's amplitude is the product of the four term families times the
prefactor `ω^phase_index · floatfactor · 2^power2`, with an additional
complex `approximate_floatfactor` multiplied in when any graph's phase
had a denominator outside `\{1, 2, 4\}` and was folded into float form.
This class is pure data; the final fold-in with the term-family product
(which requires branching on `has_approximate_floatfactors`) lives in
`evaluate.py`.
## `compile_scalar_graphs`
```python theme={null}
compile_scalar_graphs(g_list: list[BaseGraph], params: list[str]) -> CompiledScalarGraphs
```
Compile ZX-graph list into JAX-compatible structure for fast evaluation.
**Parameters:**
* `g_list` (`list[BaseGraph]`) — List of ZX-graphs to compile (must be scalar graphs with no vertices)
* `params` (`list[str]`) — List of parameter names used by this circuit. Each parameter will correspond to columns in the jax.Arrays of the compiled circuit.
**Returns:**
* `CompiledScalarGraphs` — CompiledScalarGraphs with all data in static-shaped JAX arrays
# evaluate
Source: https://bloqade.quera.com/tsim/api-reference/compile/evaluate
Evaluation of compiled scalar graphs using exact arithmetic.
## class `CompiledScalarGraphs`
```python theme={null}
CompiledScalarGraphs()
```
JAX-compatible compiled representation of a list of scalar ZX graphs.
The scalar for each graph is a product of four term families, multiplied by
a per-graph `ScalarPrefactor` (global phase, floatfactor, `2^power2`,
optional approximate complex floatfactor). All arrays are static-shaped so
the whole struct can be traced under `jax.jit`.
## class `ExactScalarArray`
```python theme={null}
ExactScalarArray(coeffs: Array, power: Array | None = None)
```
Exact scalar array for ZX-calculus phase arithmetic using dyadic representation.
Represents values of the form (c\_0 + c\_1·ω + c\_2·ω² + c\_3·ω³) × 2^power
where ω = e^(iπ/4). This enables exact computation without floating-point errors.
### `prod`
```python theme={null}
prod(axis: int = -1) -> ExactScalarArray
```
Compute product along the specified axis using associative scan.
Returns identity (1+0i with power 0) for empty reductions.
**Parameters:**
* `axis` (`int`) — The axis along which to compute the product.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the product computed along the axis.
### `sum`
```python theme={null}
sum(axis: int = -1) -> ExactScalarArray
```
Sum elements along the specified axis using normalized pairwise adds.
**Parameters:**
* `axis` (`int`) — The axis along which to sum.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the sum computed along the axis.
### `to_complex`
```python theme={null}
to_complex() -> jax.Array
```
Convert to complex number.
## `evaluate`
```python theme={null}
evaluate(circuit: CompiledScalarGraphs, param_vals: Array) -> Array
```
Evaluate compiled circuit with batched parameter values.
Each term family (`NodePhases`, `HalfPiPhases`, `PiProducts`,
`PhasePairs`) computes its own contribution via `.evaluate(param_vals)`.
This function multiplies those together with the per-graph
`ScalarPrefactor` and folds in `power2` / any approximate floatfactor.
**Parameters:**
* `circuit` (`CompiledScalarGraphs`) — Compiled circuit representation.
* `param_vals` (`Array`) — Binary parameter values (error bits + measurement/detector outcomes), shape `(batch_size, n_params)`.
**Returns:**
* `Array` — Complex array of shape `(batch_size,)` — the per-sample amplitude.
# pipeline
Source: https://bloqade.quera.com/tsim/api-reference/compile/pipeline
Compilation pipeline from prepared graphs to executable programs.
## class `CompiledComponent`
```python theme={null}
CompiledComponent()
```
A single compiled connected component of a circuit.
Each component is independent and can be sampled separately. The results
are then combined according to output\_indices.
## class `CompiledProgram`
```python theme={null}
CompiledProgram(components: tuple[CompiledComponent, ...], direct_f_indices: Array, direct_flips: Array, output_order: Array, output_reindex: Array | None, num_outputs: int, num_detectors: int)
```
A fully compiled circuit program ready for sampling.
This is the result of compiling a SamplingGraph and contains everything
needed to sample from the circuit.
## class `CompiledScalarGraphs`
```python theme={null}
CompiledScalarGraphs()
```
JAX-compatible compiled representation of a list of scalar ZX graphs.
The scalar for each graph is a product of four term families, multiplied by
a per-graph `ScalarPrefactor` (global phase, floatfactor, `2^power2`,
optional approximate complex floatfactor). All arrays are static-shaped so
the whole struct can be traced under `jax.jit`.
## class `ConnectedComponent`
```python theme={null}
ConnectedComponent(graph: BaseGraph, output_indices: list[int])
```
A connected subgraph with its associated output indices.
## class `SamplingGraph`
```python theme={null}
SamplingGraph(graph: BaseGraph, error_transform: np.ndarray, channel_probs: list[np.ndarray], num_outputs: int, num_detectors: int)
```
Result of the graph preparation phase for sampling.
Contains all data structures needed for sampling. This represents a circuit
that has been:
1. Parsed from stim format
2. Converted to a ZX graph
3. Doubled (composed with adjoint)
4. Reduced via zx.full\_reduce
5. Had its error basis transformed (Gaussian elimination: e → f)
## `classify_direct`
```python theme={null}
classify_direct(component: ConnectedComponent) -> tuple[int, bool] | None
```
Check if a component is directly determined by a single f-variable.
A component qualifies when its graph consists of exactly two vertices — one
boundary output and one Z-spider — connected by a Hadamard edge, where the
Z-spider carries a single `f` parameter and a constant phase of either 0
(no flip) or π (flip).
**Parameters:**
* `component` (`ConnectedComponent`) — A connected component to classify.
**Returns:**
* `tuple[int, bool] | None` — `(f_index, flip)` if the fast path applies, otherwise `None`.
## `compile_program`
```python theme={null}
compile_program(prepared: SamplingGraph, mode: DecompositionMode, strategy: DecompositionStrategy = 'cat5') -> CompiledProgram
```
Compile a prepared graph into an executable sampling program.
This function performs the second phase of compilation:
1. Split the graph into connected components
2. For each component:
* Plug outputs according to mode (sequential or joint)
* Reduce each plugged graph
* Perform stabilizer rank decomposition
* Compile into CompiledScalarGraphs objects
3. Assemble into CompiledProgram with output ordering
**Parameters:**
* `prepared` (`SamplingGraph`) — The prepared graph from prepare\_graph().
* `mode` (`DecompositionMode`) — Decomposition mode: - "sequential": For sampling - creates \[0, 1, 2, ..., n] circuits - "joint": For probability estimation - creates \[0, n] circuits
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
**Returns:**
* `CompiledProgram` — A CompiledProgram ready for sampling.
## `compile_scalar_graphs`
```python theme={null}
compile_scalar_graphs(g_list: list[BaseGraph], params: list[str]) -> CompiledScalarGraphs
```
Compile ZX-graph list into JAX-compatible structure for fast evaluation.
**Parameters:**
* `g_list` (`list[BaseGraph]`) — List of ZX-graphs to compile (must be scalar graphs with no vertices)
* `params` (`list[str]`) — List of parameter names used by this circuit. Each parameter will correspond to columns in the jax.Arrays of the compiled circuit.
**Returns:**
* `CompiledScalarGraphs` — CompiledScalarGraphs with all data in static-shaped JAX arrays
## `connected_components`
```python theme={null}
connected_components(g: BaseGraph) -> list[ConnectedComponent]
```
Return each connected component of `g` as its own ZX subgraph.
Each component is packaged inside a :class:`ConnectedComponent` that contains
the subgraph and a list of output indices matching the original output indices.
## `find_stab`
```python theme={null}
find_stab(graph: BaseGraph, strategy: DecompositionStrategy) -> list[BaseGraph]
```
Decompose a ZX-graph into a sum of stabilizer components.
This is the main entry point for stabilizer rank decomposition. It first removes
U3 phases, then decomposes T gates via BSS decompositions, producing a sum of
scalar graphs.
**Parameters:**
* `graph` (`BaseGraph`) — The ZX graph to decompose.
* `strategy` (`DecompositionStrategy`) — Decomposition strategy. Must be one of "cat5", "bss", "cutting".
**Returns:**
* `list[BaseGraph]` — A list of scalar graphs whose sum equals the original graph.
## `get_params`
```python theme={null}
get_params(g: BaseGraph) -> set[str]
```
Get all parameter variables that appear in the graph and its scalar.
Collects variables from:
* Vertex phases (g.\_phaseVars)
* Scalar phase variables (phasevars\_pi, phasevars\_pi\_pair, phasevars\_halfpi)
* Scalar phase pairs (phasepairs with paramsA, paramsB)
* Scalar phase nodes (phasenodevars)
**Parameters:**
* `g` (`BaseGraph`) — A ZX graph with parametrized phases
**Returns:**
* `set[str]` — Set of all variable names (e.g., \{'f0', 'f2', 'm1'}) that appear in the graph
# stabrank
Source: https://bloqade.quera.com/tsim/api-reference/compile/stabrank
Stabilizer rank decomposition for non-Clifford ZX graphs.
## `find_stab`
```python theme={null}
find_stab(graph: BaseGraph, strategy: DecompositionStrategy) -> list[BaseGraph]
```
Decompose a ZX-graph into a sum of stabilizer components.
This is the main entry point for stabilizer rank decomposition. It first removes
U3 phases, then decomposes T gates via BSS decompositions, producing a sum of
scalar graphs.
**Parameters:**
* `graph` (`BaseGraph`) — The ZX graph to decompose.
* `strategy` (`DecompositionStrategy`) — Decomposition strategy. Must be one of "cat5", "bss", "cutting".
**Returns:**
* `list[BaseGraph]` — A list of scalar graphs whose sum equals the original graph.
## `find_stab_magic`
```python theme={null}
find_stab_magic(graphs: Iterable[BaseGraph], strategy: DecompositionStrategy) -> list[BaseGraph]
```
Recursively decompose ZX-graphs into stabilizer components via magic-state removal.
## `find_stab_u3`
```python theme={null}
find_stab_u3(graphs: Iterable[BaseGraph], strategy: DecompositionStrategy) -> list[BaseGraph]
```
Recursively decompose ZX-graphs by removing U3 phases.
# terms
Source: https://bloqade.quera.com/tsim/api-reference/compile/terms
Term-family modules for compiled scalar graphs.
Each compiled ZX scalar is the product of four term families plus a global
phase and a floatfactor. This module defines the four families as
`equinox.Module` records, bundles the shared phase tables, and gives each
family an `evaluate` method that turns a batch of binary parameter values
into an `ExactScalarArray`.
Downstream, `compile.py` builds instances of these classes from
`pyzx_param` scalars, and `evaluate.py` orchestrates the products.
## class `ExactScalarArray`
```python theme={null}
ExactScalarArray(coeffs: Array, power: Array | None = None)
```
Exact scalar array for ZX-calculus phase arithmetic using dyadic representation.
Represents values of the form (c\_0 + c\_1·ω + c\_2·ω² + c\_3·ω³) × 2^power
where ω = e^(iπ/4). This enables exact computation without floating-point errors.
### `prod`
```python theme={null}
prod(axis: int = -1) -> ExactScalarArray
```
Compute product along the specified axis using associative scan.
Returns identity (1+0i with power 0) for empty reductions.
**Parameters:**
* `axis` (`int`) — The axis along which to compute the product.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the product computed along the axis.
### `sum`
```python theme={null}
sum(axis: int = -1) -> ExactScalarArray
```
Sum elements along the specified axis using normalized pairwise adds.
**Parameters:**
* `axis` (`int`) — The axis along which to sum.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the sum computed along the axis.
### `to_complex`
```python theme={null}
to_complex() -> jax.Array
```
Convert to complex number.
## class `HalfPiPhases`
```python theme={null}
HalfPiPhases()
```
Sum of `exp(i·j·π·⊕params / 2)` terms with `j ∈ \{1, 3\}`.
Terms sharing a parameter bitstring have been combined to a single stored
coefficient `j' ∈ \{1, 2, 3\}` (see `_compile_halfpi_phases`). Coefficients
are stored in eighth-turn units — i.e. as `2·j'` — so the evaluator can
reuse the `ω = e^(iπ/4)` phase table.
Padded slots use `0` (the additive identity for phase sums), so padded
entries contribute nothing to the summed exponent.
Shapes are `(num_graphs, max_terms)` except `params` which is
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate ω^(Σ coeffs · parity) per graph, batched over param\_vals.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `NodePhases`
```python theme={null}
NodePhases()
```
Product of `1 + exp(i·(α + ⊕params)·π)` terms, one factor per stored term.
Padded slots use `0` for both `phases` and `params`; the evaluator masks
padded slots to the multiplicative identity using `counts`.
Shapes are `(num_graphs, max_terms)` except `params` which is
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (1 + ω^(4·parity + phase)) per graph, batched over param\_vals.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `PhasePairs`
```python theme={null}
PhasePairs()
```
Product of `1 + e^(iα) + e^(iβ) − e^(i(α+β))` terms.
Each of `α` and `β` combines a constant phase with a parameter parity.
Padded slots use `0` and are masked to the multiplicative identity using
`counts`.
Shapes are `(num_graphs, max_terms)` except `*_params` which are
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (1 + ω^α + ω^β - ω^(α+β)) per graph, batched.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`.
## class `PiProducts`
```python theme={null}
PiProducts()
```
Product of `(-1)^(ψ · φ)` terms, with ψ and φ each a parity expression.
Each side is encoded as a constant bit plus a parameter bitmask. Padded slots
use `0` everywhere; a padded term contributes `(-1)^0 = 1` to the product.
Shapes are `(num_graphs, max_terms)` except `*_params` which are
`(num_graphs, max_terms, n_params)`.
### `evaluate`
```python theme={null}
evaluate(param_vals: Array) -> ExactScalarArray
```
Evaluate Π (-1)^(ψ·φ) per graph as a real ±1 exact scalar.
**Parameters:**
* `param_vals` (`Array`) — Binary parameter values, shape `(batch, n_params)`.
**Returns:**
* `ExactScalarArray` — `ExactScalarArray` of shape `(batch, num_graphs)`, with values
* `ExactScalarArray` — in \{+1, -1} represented exactly.
## class `ScalarPrefactor`
```python theme={null}
ScalarPrefactor()
```
Per-graph static scalar prefactor — independent of parameter values.
Each graph's amplitude is the product of the four term families times the
prefactor `ω^phase_index · floatfactor · 2^power2`, with an additional
complex `approximate_floatfactor` multiplied in when any graph's phase
had a denominator outside `\{1, 2, 4\}` and was folded into float form.
This class is pure data; the final fold-in with the term-family product
(which requires branching on `has_approximate_floatfactors`) lives in
`evaluate.py`.
## `matmul_gf2`
```python theme={null}
matmul_gf2(a: Array, b: Array) -> Array
```
Compute binary dot products mod 2 as `a_GTP x b_BP -\> b_BGT`.
Uses float32 matmul (integer matmul does not have BLAS support on CPU)
then casts back to uint8.
**Parameters:**
* `a` (`Array`) — Parameter bit-masks, shape `(G, T, P)` — G graphs, T terms, P parameters.
* `b` (`Array`) — Binary parameter values, shape `(B, P)` — B batch elements.
**Returns:**
* `Array` — Binary row-sums mod 2, shape `(B, G, T)`.
# exact_scalar
Source: https://bloqade.quera.com/tsim/api-reference/core/exact_scalar
Exact scalar arithmetic for ZX-calculus phase computations.
Implements exact arithmetic for complex numbers of the form:
(a + b*e^(i*pi/4) + c*i + d*e^(-i\*pi/4)) \* 2^power
This representation enables exact computation of phases in ZX-calculus graphs
without floating-point errors.
## class `ExactScalarArray`
```python theme={null}
ExactScalarArray(coeffs: Array, power: Array | None = None)
```
Exact scalar array for ZX-calculus phase arithmetic using dyadic representation.
Represents values of the form (c\_0 + c\_1·ω + c\_2·ω² + c\_3·ω³) × 2^power
where ω = e^(iπ/4). This enables exact computation without floating-point errors.
### `prod`
```python theme={null}
prod(axis: int = -1) -> ExactScalarArray
```
Compute product along the specified axis using associative scan.
Returns identity (1+0i with power 0) for empty reductions.
**Parameters:**
* `axis` (`int`) — The axis along which to compute the product.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the product computed along the axis.
### `sum`
```python theme={null}
sum(axis: int = -1) -> ExactScalarArray
```
Sum elements along the specified axis using normalized pairwise adds.
**Parameters:**
* `axis` (`int`) — The axis along which to sum.
**Returns:**
* `ExactScalarArray` — ExactScalarArray with the sum computed along the axis.
### `to_complex`
```python theme={null}
to_complex() -> jax.Array
```
Convert to complex number.
# graph
Source: https://bloqade.quera.com/tsim/api-reference/core/graph
ZX graph construction, manipulation, and preparation for sampling.
## class `Circuit`
```python theme={null}
Circuit(stim_program_text: str = '')
```
Quantum circuit as a thin wrapper around stim.Circuit.
Circuits are constructed like stim circuits:
\>>> circuit = Circuit('''
... H 0
... T 0
... CNOT 0 1
... M 0 1
... ''')
### `append`
```python theme={null}
append(name: str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit, targets: int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString] = (), arg: float | Iterable[float] | None = None, tag: str = '') -> None
```
Append an operation into the circuit.
**Parameters:**
* `name` (`str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit`) — The name of the operation's gate (e.g. "H" or "M" or "CNOT"). This argument can also be set to a `stim.CircuitInstruction` or `stim.CircuitInstructionBlock`, which results in the instruction or block being appended to the circuit. The other arguments (targets and arg) can't be specified when doing so.
* `targets` (`int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString]`) — The objects operated on by the gate. This can be either a single target or an iterable of multiple targets. Each target can be: An int: The index of a targeted qubit. A `stim.GateTarget`: Could be a variety of things. Methods like `stim.target_rec`, `stim.target_sweet`, `stim.target_x`, and `stim.CircuitInstruction.__getitem__` all return this type. A `stim.PauliString`: This will automatically be expanded into a product of pauli targets like `X1*Y2*Z3`.
* `arg` (`float | Iterable[float] | None`) — The "parens arguments" for the gate, such as the probability for a noise operation. A double or list of doubles parameterizing the gate. Different gates take different parens arguments. For example, X\_ERROR takes a probability, OBSERVABLE\_INCLUDE takes an observable index, and PAULI\_CHANNEL\_1 takes three disjoint probabilities.
* `tag` (`str`) — A customizable string attached to the instruction.
### `append_from_stim_program_text`
```python theme={null}
append_from_stim_program_text(stim_program_text: str) -> None
```
Append operations described by a Stim format program into the circuit.
Supports the same shorthand syntax as the constructor.
### `approx_equals`
```python theme={null}
approx_equals(other: object, atol: float) -> bool
```
Check if a circuit is approximately equal to another circuit.
Two circuits are approximately equal if they are equal up to slight
perturbations of instruction arguments such as probabilities. For example,
`X_ERROR(0.100) 0` is approximately equal to `X_ERROR(0.099)` within an absolute
tolerance of 0.002. All other details of the circuits (such as the ordering of
instructions and targets) must be exactly the same.
**Parameters:**
* `other` (`object`) — The circuit, or other object, to compare to this one.
* `atol` (`float`) — The absolute error tolerance. The maximum amount each probability may have been perturbed by.
**Returns:**
* `bool` — True if the given object is a circuit approximately equal up to the
* `bool` — receiving circuit up to the given tolerance, otherwise False.
### `cast_to_stim`
```python theme={null}
cast_to_stim() -> stim.Circuit
```
Return self with type cast to `stim.Circuit`.
This is useful for passing the circuit to functions that expect a
`stim.Circuit`.
### `compile_detector_sampler`
```python theme={null}
compile_detector_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledDetectorSampler
```
Compile circuit into a detector sampler.
Connected components whose single output is deterministically given by
one f-variable are handled via a fast direct path (no compilation or
autoregressive sampling). Remaining components go through the full
compilation pipeline.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledDetectorSampler` — A CompiledDetectorSampler that can be used to sample detectors and observables.
### `compile_m2d_converter`
```python theme={null}
compile_m2d_converter(skip_reference_sample: bool = False) -> stim.CompiledMeasurementsToDetectionEventsConverter
```
Create a measurement-to-detection-event converter for the given circuit.
The converter can efficiently compute detection events and observable flips
from raw measurement data.
The converter uses a noiseless reference sample, collected from the circuit
using stim's Tableau simulator during initialization of the converter, as a
baseline for determining what the expected value of a detector is.
Note that the expected behavior of gauge detectors (detectors that are not
actually deterministic under noiseless execution) can vary depending on the
reference sample. Stim mitigates this by always generating the same reference
sample for a given circuit.
**Parameters:**
* `skip_reference_sample` (`bool`) — Defaults to False. When set to True, the reference sample used by the converter is initialized to all-zeroes instead of being collected from the circuit. This should only be used if it's known that the all-zeroes sample is actually a possible result from the circuit (under noiseless execution).
**Returns:**
* `stim.CompiledMeasurementsToDetectionEventsConverter` — An initialized stim.CompiledMeasurementsToDetectionEventsConverter.
### `compile_sampler`
```python theme={null}
compile_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledMeasurementSampler
```
Compile circuit into a measurement sampler.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledMeasurementSampler` — A CompiledMeasurementSampler that can be used to sample measurements.
### `copy`
```python theme={null}
copy() -> Circuit
```
Create a copy of this circuit.
### `detector_error_model`
```python theme={null}
detector_error_model(decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true.
**Parameters:**
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. This is not supported by tsim and setting it to true will raise an error. The argument is present for compatibility with stim.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_gauge\_detectors=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. Note that, unlike in stim, logical observables are also allowed to be non-deterministic.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
### `diagram`
```python theme={null}
diagram(type: Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg'] = 'timeline-svg', tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, height: float | None = None, width: float | None = None, zoomable: bool = True, kwargs: Any = {}) -> Any
```
Return a diagram of the circuit, from a variety of options.
**Parameters:**
* `type` (`Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg']`) — The type of diagram. Available types are: "pyzx": A pyzx SVG of the ZX diagram of the circuit. "pyzx-dets": A pyzx SVG of the ZX diagram that is used to compute probabilities of detectors and observables. "pyzx-meas": A pyzx SVG of the ZX diagram that is used to compute probabilities of measurements. "timeline-svg": An SVG image of the operations applied by the circuit over time. Includes annotations showing the measurement record index that each measurement writes to, and the measurements used by detectors. "timeslice-svg": An SVG image of the operations applied between two TICK instructions in the circuit, with the operations laid out in 2d.
* `tick` (`int | range | None`) — Required for time slice diagrams. Specifies which TICK instruction, or range of TICK instructions, to slice at. Note that the first TICK instruction in the circuit corresponds tick=1. The value tick=0 refers to the very start of the circuit. Passing `range(A, B)` for a detector slice will show the slices for ticks A through B including A but excluding B. Passing `range(A, B)` for a time slice will show the operations between tick A and tick B.
* `rows` (`int | None`) — In diagrams that have multiple separate pieces, such as timeslice diagrams and detslice diagrams, this controls how many rows of pieces there will be. If not specified, a number of rows that creates a roughly square layout will be chosen.
* `filter_coords` (`Iterable[Iterable[float] | stim.DemTarget]`) — A list of things to include in the diagram. Different effects depending on the diagram. For detslice diagrams, the filter defaults to showing all detectors and no observables. When specified, each list entry can be a collection of floats (detectors whose coordinates start with the same numbers will be included), a stim.DemTarget (specifying a detector or observable to include), a string like "D5" or "L0" specifying a detector or observable to include.
* `height` (`float | None`) — Optional height for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types. For timeline-svg, when both height and width are None, the height is automatically determined based on the number of qubits. When only one dimension is given, the other is computed from the SVG aspect ratio.
* `width` (`float | None`) — Optional width for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types.
* `zoomable` (`bool`) — If True (default), wraps SVG diagrams in an interactive container with pan and Ctrl/Cmd+wheel zoom. Only applies to timeline-svg and timeslice-svg diagram types.
* `**kwargs` (`Any`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `Any` — An object whose `__str__` method returns the diagram, so that
* `Any` — writing the diagram to a file works correctly. The returned
* `Any` — object may also define methods such as `_repr_html_`, so that
* `Any` — ipython notebooks recognize it can be shown using a specialized
* `Any` — viewer instead of as raw text.
### `flattened`
```python theme={null}
flattened() -> Circuit
```
Return a copy of the circuit with all repeat blocks expanded.
### `from_file`
```python theme={null}
from_file(cls, filename: str) -> Circuit
```
Create a Circuit from a file.
**Parameters:**
* `filename` (`str`) — The filename to read the circuit from.
**Returns:**
* `Circuit` — A new Circuit instance.
### `from_stim_program`
```python theme={null}
from_stim_program(cls, stim_circuit: stim.Circuit) -> Circuit
```
Create a Circuit from a stim.Circuit object.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to wrap.
**Returns:**
* `Circuit` — A new Circuit instance.
### `get_graph`
```python theme={null}
get_graph() -> BaseGraph
```
Construct the ZX graph.
### `get_sampling_graph`
```python theme={null}
get_sampling_graph(sample_detectors: bool = False) -> BaseGraph
```
Get a ZX graph that can be used to compute probabilities.
This graph will be constructed as follows:
1. Double the ZX-diagram by composing it with its adjoint.
2. Connect all rec\[i] vertices to their corresponding adjoint rec\[i] vertices.
3. Add outputs:
(a) When sampling measurements (i.e. `sample_detectors` is False),
add output nodes for each measurement. Detectors and observables are
removed since they are ignored when sampling measurements.
(b) When sampling detectors and observables (i.e. `sample_detectors` is True),
add output nodes for each detector and observable. Only one set of detector
and observable nodes is kept, i.e., detectors and observables are not
composed with their adjoints.
**Parameters:**
* `sample_detectors` (`bool`) — If True, sample detectors and observables instead of measurements.
**Returns:**
* `BaseGraph` — A ZX graph for sampling.
### `inverse`
```python theme={null}
inverse() -> Circuit
```
Return the inverse of the circuit.
### `pop`
```python theme={null}
pop(index: int = -1) -> stim.CircuitInstruction | stim.CircuitRepeatBlock
```
Pops an operation from the end of the circuit, or at the given index.
**Parameters:**
* `index` (`int`) — Defaults to -1 (end of circuit). The index to pop from.
**Returns:**
* `stim.CircuitInstruction | stim.CircuitRepeatBlock` — The popped instruction or repeat block.
**Raises:**
* `IndexError` — The given index is outside the bounds of the circuit.
### `tcount`
```python theme={null}
tcount() -> int
```
Count the number of T gates in the circuit.
### `to_matrix`
```python theme={null}
to_matrix() -> Any
```
Convert circuit to matrix representation.
### `to_tensor`
```python theme={null}
to_tensor() -> Any
```
Convert circuit to tensor representation.
### `without_annotations`
```python theme={null}
without_annotations() -> Circuit
```
Return a copy of the circuit with all detector and observable annotations removed.
### `without_noise`
```python theme={null}
without_noise() -> Circuit
```
Return a copy of the circuit with all noise removed.
## class `ConnectedComponent`
```python theme={null}
ConnectedComponent(graph: BaseGraph, output_indices: list[int])
```
A connected subgraph with its associated output indices.
## class `GraphRepresentation`
```python theme={null}
GraphRepresentation(graph: GraphS = GraphS(), rec: list[int] = list(), silent_rec: list[int] = list(), detectors: list[int] = list(), observables_dict: dict[int, int] = dict(), first_vertex: dict[int, int] = dict(), last_vertex: dict[int, int] = dict(), channel_probs: list[np.ndarray] = list(), correlated_error_probs: list[float] = list(), num_error_bits: int = 0, num_correlated_error_bits: int = 0, track_classical_wires: bool = False)
```
ZX graph built from a stim circuit.
Contains the graph and all auxiliary data needed for sampling.
## class `SamplingGraph`
```python theme={null}
SamplingGraph(graph: BaseGraph, error_transform: np.ndarray, channel_probs: list[np.ndarray], num_outputs: int, num_detectors: int)
```
Result of the graph preparation phase for sampling.
Contains all data structures needed for sampling. This represents a circuit
that has been:
1. Parsed from stim format
2. Converted to a ZX graph
3. Doubled (composed with adjoint)
4. Reduced via zx.full\_reduce
5. Had its error basis transformed (Gaussian elimination: e → f)
## `build_sampling_graph`
```python theme={null}
build_sampling_graph(built: GraphRepresentation, sample_detectors: bool = False) -> BaseGraph
```
Build a ZX graph for sampling from a GraphRepresentation.
This is the internal implementation of get\_sampling\_graph.
## `classify_direct`
```python theme={null}
classify_direct(component: ConnectedComponent) -> tuple[int, bool] | None
```
Check if a component is directly determined by a single f-variable.
A component qualifies when its graph consists of exactly two vertices — one
boundary output and one Z-spider — connected by a Hadamard edge, where the
Z-spider carries a single `f` parameter and a constant phase of either 0
(no flip) or π (flip).
**Parameters:**
* `component` (`ConnectedComponent`) — A connected component to classify.
**Returns:**
* `tuple[int, bool] | None` — `(f_index, flip)` if the fast path applies, otherwise `None`.
## `connected_components`
```python theme={null}
connected_components(g: BaseGraph) -> list[ConnectedComponent]
```
Return each connected component of `g` as its own ZX subgraph.
Each component is packaged inside a :class:`ConnectedComponent` that contains
the subgraph and a list of output indices matching the original output indices.
## `evaluate_graph`
```python theme={null}
evaluate_graph(g: GraphS, vals: dict[str, Fraction] | None = None) -> np.ndarray
```
Evaluate a ZX graph to a tensor with given parameter values.
## `find_basis`
```python theme={null}
find_basis(vectors: np.ndarray) -> tuple[np.ndarray, np.ndarray]
```
Decompose a set of binary vectors into a basis subset and a transformation matrix over GF(2).
Given a set of vectors V, this function finds a maximal linearly independent subset B
(the basis) and computes a transformation matrix T such that the original vectors can be
reconstructed from the basis via matrix multiplication over GF(2):
V = T @ B (mod 2)
**Parameters:**
* `vectors` (`np.ndarray`) — Input binary vectors of shape `(N, D)`. Can be a list of lists or a numpy array. Elements should be 0 or 1 (or convertible to them).
**Returns:**
* `tuple[np.ndarray, np.ndarray]` — A tuple `(basis, transform)` where: basis: The subset of independent vectors, shape `(K, D)`, where `K` is the rank. transform: The transformation matrix, shape `(N, K)`.
## `get_params`
```python theme={null}
get_params(g: BaseGraph) -> set[str]
```
Get all parameter variables that appear in the graph and its scalar.
Collects variables from:
* Vertex phases (g.\_phaseVars)
* Scalar phase variables (phasevars\_pi, phasevars\_pi\_pair, phasevars\_halfpi)
* Scalar phase pairs (phasepairs with paramsA, paramsB)
* Scalar phase nodes (phasenodevars)
**Parameters:**
* `g` (`BaseGraph`) — A ZX graph with parametrized phases
**Returns:**
* `set[str]` — Set of all variable names (e.g., \{'f0', 'f2', 'm1'}) that appear in the graph
## `parse_stim_circuit`
```python theme={null}
parse_stim_circuit(stim_circuit: stim.Circuit, track_classical_wires: bool = False) -> GraphRepresentation
```
Parse a stim circuit into a GraphRepresentation.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to convert.
* `track_classical_wires` (`bool`) — Whether to track classical wires.
**Returns:**
* `GraphRepresentation` — A GraphRepresentation containing the ZX graph and all auxiliary data.
## `prepare_graph`
```python theme={null}
prepare_graph(circuit: Circuit, sample_detectors: bool) -> SamplingGraph
```
Prepare a circuit for compilation.
This function performs the graph preparation phase:
1. Parse the stim circuit into a ZX graph
2. Build the sampling graph (compose with adjoint, add outputs)
3. Reduce the graph via zx.full\_reduce
4. Transform error basis via Gaussian elimination (e → f)
5. Clear the scalar (safe before stabilizer rank decomposition)
**Parameters:**
* `circuit` (`Circuit`) — The quantum circuit to prepare.
* `sample_detectors` (`bool`) — If True, prepare for detector sampling. If False, prepare for measurement sampling.
**Returns:**
* `SamplingGraph` — A SamplingGraph containing the reduced graph and error transformation.
## `scale_horizontally`
```python theme={null}
scale_horizontally(g: BaseGraph, scale: float) -> None
```
Scale horizontal positions of graph vertices by a factor of `scale`.
**Parameters:**
* `g` (`BaseGraph`) — A ZX graph
* `scale` (`float`) — The factor to scale the graph by
## `squash_graph`
```python theme={null}
squash_graph(g: BaseGraph) -> None
```
Compact the graph by placing vertices underneath their output connections.
Starting from output vertices, each vertex is placed directly underneath
(same row, qubit - 1) its already-placed neighbor. Positions are assigned
via BFS traversal from outputs, ensuring no (qubit, row) collisions.
## `transform_error_basis`
```python theme={null}
transform_error_basis(g: BaseGraph, num_e: int | None = None) -> tuple[BaseGraph, np.ndarray]
```
Transform phase variables from the original 'e' basis to a reduced 'f' basis.
This function finds a linearly independent basis for the phase variables
across all vertices and transforms them accordingly. The original variables
(e0, e1, ...) are mapped to a smaller set (f0, f1, ...) where each f\_i
corresponds to a linear combination of original e variables.
**Parameters:**
* `g` (`BaseGraph`) — A ZX graph with phase variables attached to vertices.
* `num_e` (`int | None`) — Total number of e-variables. If provided, the returned matrix will have exactly this many columns (padded with zeros if needed). If None, the matrix will have only the columns that appear in the graph.
**Returns:**
* `tuple[BaseGraph, np.ndarray]` — A tuple containing: - The modified graph (same object, mutated in place) - A binary matrix of shape (num\_f, num\_e) where entry \[i, j] = 1 means f\_i depends on e\_j. For example, if row 0 is \[0, 1, 0, 1], then f0 = e1 XOR e3.
# instructions
Source: https://bloqade.quera.com/tsim/api-reference/core/instructions
ZX graph representations of quantum gates and instructions.
## class `GraphRepresentation`
```python theme={null}
GraphRepresentation(graph: GraphS = GraphS(), rec: list[int] = list(), silent_rec: list[int] = list(), detectors: list[int] = list(), observables_dict: dict[int, int] = dict(), first_vertex: dict[int, int] = dict(), last_vertex: dict[int, int] = dict(), channel_probs: list[np.ndarray] = list(), correlated_error_probs: list[float] = list(), num_error_bits: int = 0, num_correlated_error_bits: int = 0, track_classical_wires: bool = False)
```
ZX graph built from a stim circuit.
Contains the graph and all auxiliary data needed for sampling.
## `add_dummy`
```python theme={null}
add_dummy(b: GraphRepresentation, qubit: int, row: float | int | None = None) -> int
```
Add a dummy boundary vertex for a qubit.
## `add_lane`
```python theme={null}
add_lane(b: GraphRepresentation, qubit: int) -> int
```
Initialize a qubit lane if it doesn't exist.
## `c_nxyz`
```python theme={null}
c_nxyz(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending -X -> Y -> Z -> -X.
## `c_nzyx`
```python theme={null}
c_nzyx(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending -Z -> Y -> X -> -Z.
## `c_xnyz`
```python theme={null}
c_xnyz(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending X -> -Y -> Z -> X.
## `c_xynz`
```python theme={null}
c_xynz(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending X -> Y -> -Z -> X.
## `c_xyz`
```python theme={null}
c_xyz(b: GraphRepresentation, qubit: int) -> None
```
Right handed period 3 axis cycling gate, sending X -> Y -> Z -> X.
## `c_znyx`
```python theme={null}
c_znyx(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending Z -> -Y -> X -> Z.
## `c_zynx`
```python theme={null}
c_zynx(b: GraphRepresentation, qubit: int) -> None
```
Period 3 axis cycling gate, sending Z -> Y -> -X -> Z.
## `c_zyx`
```python theme={null}
c_zyx(b: GraphRepresentation, qubit: int) -> None
```
Left handed period 3 axis cycling gate, sending Z -> Y -> X -> Z.
## `cnot`
```python theme={null}
cnot(b: GraphRepresentation, control: int, target: int, classically_controlled: list[bool] | None = None) -> None
```
Apply CNOT (controlled-X) gate.
## `correlated_error`
```python theme={null}
correlated_error(b: GraphRepresentation, qubits: list[int], types: list[Literal['X', 'Y', 'Z']], p: float) -> None
```
Add a correlated error term affecting multiple qubits with given Pauli types.
## `correlated_error_probs`
```python theme={null}
correlated_error_probs(probabilities: list[float]) -> np.ndarray
```
Build probability distribution for correlated error chain.
Given conditional probabilities \[p1, p2, ..., pk] from a chain of
CORRELATED\_ERROR(p1) ELSE\_CORRELATED\_ERROR(p2) ... ELSE\_CORRELATED\_ERROR(pk),
computes the joint probability distribution over 2^k outcomes.
Since errors are mutually exclusive, only outcomes with at most one bit set
have non-zero probability.
* `probs[0]` is the probability that no branch fires.
* `probs[1 \<\< i]` is the probability that branch `i` fires after all
previous branches did not fire.
**Parameters:**
* `probabilities` (`list[float]`) — List of conditional probabilities \[p1, p2, ..., pk]
**Returns:**
* `np.ndarray` — Array of shape (2^k,) with probabilities for each outcome.
## `cxswap`
```python theme={null}
cxswap(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Apply CX then SWAP.
## `cy`
```python theme={null}
cy(b: GraphRepresentation, control: int, target: int, classically_controlled: list[bool] | None = None) -> None
```
Apply controlled-Y gate.
## `cz`
```python theme={null}
cz(b: GraphRepresentation, control: int, target: int, classically_controlled: list[bool] | None = None) -> None
```
Apply controlled-Z gate.
## `czswap`
```python theme={null}
czswap(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Apply CZ then SWAP.
## `depolarize1`
```python theme={null}
depolarize1(b: GraphRepresentation, qubit: int, p: float) -> None
```
Apply single-qubit depolarizing channel with total error probability p.
## `depolarize2`
```python theme={null}
depolarize2(b: GraphRepresentation, qubit_i: int, qubit_j: int, p: float) -> None
```
Apply two-qubit depolarizing channel with total error probability p.
## `detector`
```python theme={null}
detector(b: GraphRepresentation, rec: list[int], args = ()) -> None
```
Add detector annotation that XORs the given measurement record bits.
## `ensure_lane`
```python theme={null}
ensure_lane(b: GraphRepresentation, qubit: int) -> None
```
Ensure qubit lane exists.
## `error_probs`
```python theme={null}
error_probs(p: float) -> np.ndarray
```
Single-bit error channel.
Returns `[P(bit0=0), P(bit0=1)]`.
## `finalize_correlated_error`
```python theme={null}
finalize_correlated_error(b: GraphRepresentation) -> None
```
Finalize the current correlated error channel.
1. Rename all "c\{i}" phases to "e\{num\_error\_bits + i}" in the graph
2. Compute and append the 2^k probability array to channel\_probs
3. Increment num\_error\_bits by k
4. Reset num\_correlated\_error\_bits to 0 and correlated\_error\_probs to \[]
## `h`
```python theme={null}
h(b: GraphRepresentation, qubit: int) -> None
```
Apply Hadamard gate.
## `h_nxy`
```python theme={null}
h_nxy(b: GraphRepresentation, qubit: int) -> None
```
Apply Hadamard-like gate that sends -X \<-> Y, Z -> -Z.
## `h_nxz`
```python theme={null}
h_nxz(b: GraphRepresentation, qubit: int) -> None
```
Apply Hadamard-like gate that sends -X \<-> Z.
## `h_nyz`
```python theme={null}
h_nyz(b: GraphRepresentation, qubit: int) -> None
```
Apply Hadamard-like gate that sends -Y \<-> Z, X -> -X.
## `h_xy`
```python theme={null}
h_xy(b: GraphRepresentation, qubit: int) -> None
```
Apply variant of Hadamard gate that swaps the X and Y axes (instead of X and Z).
## `h_yz`
```python theme={null}
h_yz(b: GraphRepresentation, qubit: int) -> None
```
Apply variant of Hadamard gate that swaps the Y and Z axes (instead of X and Z).
## `heralded_erase`
```python theme={null}
heralded_erase(b: GraphRepresentation, qubit: int, p: float) -> None
```
Apply heralded erasure channel.
Special case of heralded\_pauli\_channel\_1 with equal probabilities p/4
for each of I, X, Y, Z when the channel fires.
## `heralded_pauli_channel_1`
```python theme={null}
heralded_pauli_channel_1(b: GraphRepresentation, qubit: int, pi: float, px: float, py: float, pz: float) -> None
```
Apply heralded single-qubit Pauli channel.
Records a herald bit into the measurement record. When the channel fires
(with total probability pi+px+py+pz), the herald is 1 and one of I/X/Y/Z
is applied. When it doesn't fire, the herald is 0 and nothing happens.
## `heralded_pauli_channel_1_probs`
```python theme={null}
heralded_pauli_channel_1_probs(pi: float, px: float, py: float, pz: float) -> np.ndarray
```
Heralded single-qubit Pauli channel. Returns shape (8,).
Bit layout:
* bit 0: herald bit, written to the measurement record
* bit 1: Z error component
* bit 2: X error component
The non-zero outcomes are:
* index 0 (0b000): no herald, no Pauli error
* index 1 (0b001): herald + I
* index 3 (0b011): herald + Z
* index 5 (0b101): herald + X
* index 7 (0b111): herald + Y, represented as X+Z
## `i`
```python theme={null}
i(b: GraphRepresentation, qubit: int, _args: float = ()) -> None
```
Apply identity (advances the row).
## `ii`
```python theme={null}
ii(b: GraphRepresentation, qubit1: int, qubit2: int, _args: float = ()) -> None
```
Apply two-qubit identity (advances the row on both qubits).
## `iswap`
```python theme={null}
iswap(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Swap two qubits and phase the -1 eigenspace of the ZZ observable by i.
## `iswap_dag`
```python theme={null}
iswap_dag(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Swap two qubits and phase the -1 eigenspace of the ZZ observable by -i.
## `last_edge`
```python theme={null}
last_edge(b: GraphRepresentation, qubit: int)
```
Get the last edge for a qubit.
## `last_row`
```python theme={null}
last_row(b: GraphRepresentation, qubit: int) -> float
```
Get the row of the last vertex for a qubit.
## `m`
```python theme={null}
m(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
Measure qubit in Z basis with optional bit-flip error probability p.
## `mpad`
```python theme={null}
mpad(b: GraphRepresentation, value: int, p: float = 0) -> None
```
Pad measurement record with a fixed bit value.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `value` (`int`) — The bit value to record (0 or 1).
* `p` (`float`) — Error probability for the recorded bit.
## `mpp`
```python theme={null}
mpp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], invert: bool = False, p: float = 0) -> None
```
Measure a single Pauli product.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product.
* `invert` (`bool`) — Whether to invert the measurement result.
* `p` (`float`) — Measurement flip error probability.
## `mr`
```python theme={null}
mr(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
Z-basis demolition measurement (optionally noisy).
Projects each target qubit into |0> or |1>, reports its value (false=|0>, true=|1>),
then resets to |0>.
## `mrx`
```python theme={null}
mrx(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
X-basis demolition measurement (optionally noisy).
Projects each target qubit into |+> or |->, reports its value (false=|+>, true=|->),
then resets to |+>.
## `mry`
```python theme={null}
mry(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
Y-basis demolition measurement (optionally noisy).
Projects each target qubit into |i> or |-i>, reports its value (false=|i>, true=|-i>),
then resets to |i>.
## `mx`
```python theme={null}
mx(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
Measure qubit in X basis.
## `mxx`
```python theme={null}
mxx(b: GraphRepresentation, q0: int, q1: int, p: float = 0, invert: bool = False) -> None
```
Measure two qubits in XX basis.
## `my`
```python theme={null}
my(b: GraphRepresentation, qubit: int, p: float = 0, invert: bool = False) -> None
```
Measure qubit in Y basis.
## `myy`
```python theme={null}
myy(b: GraphRepresentation, q0: int, q1: int, p: float = 0, invert: bool = False) -> None
```
Measure two qubits in YY basis.
## `mzz`
```python theme={null}
mzz(b: GraphRepresentation, q0: int, q1: int, p: float = 0, invert: bool = False) -> None
```
Measure two qubits in ZZ basis.
## `observable_include`
```python theme={null}
observable_include(b: GraphRepresentation, rec: list[int], idx: int) -> None
```
Add observable annotation that XORs the given measurement record bits.
## `pauli_channel_1`
```python theme={null}
pauli_channel_1(b: GraphRepresentation, qubit: int, px: float, py: float, pz: float) -> None
```
Apply single-qubit Pauli channel with given X, Y, Z error probabilities.
## `pauli_channel_1_probs`
```python theme={null}
pauli_channel_1_probs(px: float, py: float, pz: float) -> np.ndarray
```
Single-qubit Pauli channel. Returns shape (4,).
Bit layout:
* bit 0: Z error component
* bit 1: X error component
The outcomes are:
* index 0 (0b00): I
* index 1 (0b01): Z
* index 2 (0b10): X
* index 3 (0b11): Y
## `pauli_channel_2`
```python theme={null}
pauli_channel_2(b: GraphRepresentation, qubit_i: int, qubit_j: int, pix: float, piy: float, piz: float, pxi: float, pxx: float, pxy: float, pxz: float, pyi: float, pyx: float, pyy: float, pyz: float, pzi: float, pzx: float, pzy: float, pzz: float) -> None
```
Apply two-qubit Pauli channel with given error probabilities for all 15 Pauli pairs.
## `pauli_channel_2_probs`
```python theme={null}
pauli_channel_2_probs(pix: float, piy: float, piz: float, pxi: float, pxx: float, pxy: float, pxz: float, pyi: float, pyx: float, pyy: float, pyz: float, pzi: float, pzx: float, pzy: float, pzz: float) -> np.ndarray
```
Two-qubit Pauli channel. Returns shape (16,).
Bit layout:
* bit 0: Z error component on `qubit_i`
* bit 1: X error component on `qubit_i`
* bit 2: Z error component on `qubit_j`
* bit 3: X error component on `qubit_j`
With that layout, index `z_i + 2*x_i + 4*z_j + 8*x_j` stores the
probability for the corresponding two-qubit Pauli outcome. The arguments
follow Stim's naming convention: `pix` is I on `qubit_i` and X on
`qubit_j`, `pzi` is Z on `qubit_i` and I on `qubit_j`, etc.
## `r`
```python theme={null}
r(b: GraphRepresentation, qubit: int) -> None
```
Z-basis reset.
Forces each target qubit into the |0> state by silently measuring it in the Z basis
and applying an X gate if it ended up in the |1> state.
## `r_pauli`
```python theme={null}
r_pauli(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], theta: Fraction, dagger: bool = False) -> None
```
Apply exp(-i theta pi/2 P) for a Pauli product P, with theta in half-turns.
Generalizes `SPP` (theta=1/2) and `TPP` (theta=1/4) to an arbitrary angle.
For a single qubit, `r_pauli` reduces to the named single-qubit rotation in
that Pauli's basis (e.g. `R_PAULI Z0` is `R_Z`). If `dagger` is True, apply
exp(+i theta pi/2 P) instead.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `theta` (`Fraction`) — Rotation angle in half-turns (units of pi).
* `dagger` (`bool`) — If True, negate the rotation angle.
## `r_x`
```python theme={null}
r_x(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_X rotation gate with given phase (in units of π).
## `r_y`
```python theme={null}
r_y(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_Y rotation gate with given phase (in units of π).
## `r_z`
```python theme={null}
r_z(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_Z rotation gate with given phase (in units of π).
## `rx`
```python theme={null}
rx(b: GraphRepresentation, qubit: int) -> None
```
X-basis reset.
Forces each target qubit into the |+> state by silently measuring it in the X basis
and applying a Z gate if it ended up in the |-> state.
## `ry`
```python theme={null}
ry(b: GraphRepresentation, qubit: int) -> None
```
Y-basis reset.
Forces each target qubit into the |i> state by silently measuring it in the Y basis
and applying an X gate if it ended up in the |-i> state.
## `s`
```python theme={null}
s(b: GraphRepresentation, qubit: int) -> None
```
Apply S gate (π/2 Z rotation).
## `s_dag`
```python theme={null}
s_dag(b: GraphRepresentation, qubit: int) -> None
```
Apply S† gate (-π/2 Z rotation).
## `spp`
```python theme={null}
spp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], dagger: bool = False) -> None
```
Apply exp(-i pi/4 P) (up to global phase) for a Pauli product P.
Phases the -1 eigenspace of P by i (or -i if dagger). For a single qubit,
`SPP Z0` is the S gate and `SPP_DAG Z0` is S\_DAG.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `dagger` (`bool`) — If True, apply exp(+i pi/4 P) (phase by -i) instead.
## `sqrt_x`
```python theme={null}
sqrt_x(b: GraphRepresentation, qubit: int) -> None
```
Apply √X gate (π/2 X rotation).
## `sqrt_x_dag`
```python theme={null}
sqrt_x_dag(b: GraphRepresentation, qubit: int) -> None
```
Apply √X† gate (-π/2 X rotation).
## `sqrt_xx`
```python theme={null}
sqrt_xx(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the XX observable by i.
## `sqrt_xx_dag`
```python theme={null}
sqrt_xx_dag(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the XX observable by -i.
## `sqrt_y`
```python theme={null}
sqrt_y(b: GraphRepresentation, qubit: int) -> None
```
Apply √Y gate (π/2 Y rotation).
## `sqrt_y_dag`
```python theme={null}
sqrt_y_dag(b: GraphRepresentation, qubit: int) -> None
```
Apply √Y† gate (-π/2 Y rotation).
## `sqrt_yy`
```python theme={null}
sqrt_yy(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the YY observable by i.
## `sqrt_yy_dag`
```python theme={null}
sqrt_yy_dag(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the YY observable by -i.
## `sqrt_z`
```python theme={null}
sqrt_z(b: GraphRepresentation, qubit: int) -> None
```
Apply √Z gate (alias for S gate).
## `sqrt_z_dag`
```python theme={null}
sqrt_z_dag(b: GraphRepresentation, qubit: int) -> None
```
Apply √Z† gate (alias for S† gate).
## `sqrt_zz`
```python theme={null}
sqrt_zz(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the ZZ observable by i.
## `sqrt_zz_dag`
```python theme={null}
sqrt_zz_dag(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Phases the -1 eigenspace of the ZZ observable by -i.
## `swap`
```python theme={null}
swap(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Apply SWAP gate.
## `swapcx`
```python theme={null}
swapcx(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Apply SWAP then CX.
## `swapcz`
```python theme={null}
swapcz(b: GraphRepresentation, qubit1: int, qubit2: int) -> None
```
Apply SWAP then CZ.
## `t`
```python theme={null}
t(b: GraphRepresentation, qubit: int) -> None
```
Apply T gate (π/4 Z rotation).
## `t_dag`
```python theme={null}
t_dag(b: GraphRepresentation, qubit: int) -> None
```
Apply T† gate (-π/4 Z rotation).
## `tick`
```python theme={null}
tick(b: GraphRepresentation) -> None
```
Add a tick to the circuit (align all qubits to same row).
## `tpp`
```python theme={null}
tpp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], dagger: bool = False) -> None
```
Apply exp(-i pi/8 P) (up to global phase) for a Pauli product P.
Phases the -1 eigenspace of P by exp(i pi/4) (or exp(-i pi/4) if dagger).
For a single qubit, `TPP Z0` is the T gate and `TPP_DAG Z0` is T\_DAG.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `dagger` (`bool`) — If True, apply exp(+i pi/8 P) (phase by exp(-i pi/4)) instead.
## `u3`
```python theme={null}
u3(b: GraphRepresentation, qubit: int, theta: Fraction, phi: Fraction, lambda_: Fraction) -> None
```
Apply U3 gate: U3(θ,φ,λ) = R\_Z(φ)·R\_Y(θ)·R\_Z(λ).
## `x`
```python theme={null}
x(b: GraphRepresentation, qubit: int) -> None
```
Apply Pauli X gate.
## `x_error`
```python theme={null}
x_error(b: GraphRepresentation, qubit: int, p: float) -> None
```
Apply X error with probability p.
## `x_phase`
```python theme={null}
x_phase(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply X-axis rotation to qubit. This is equivalent to `r_x` up to a phase.
## `xcx`
```python theme={null}
xcx(b: GraphRepresentation, control: int, target: int) -> None
```
X-controlled X gate. Applies X to target if control is in |-> state.
## `xcy`
```python theme={null}
xcy(b: GraphRepresentation, control: int, target: int) -> None
```
X-controlled Y gate. Applies Y to target if control is in |-> state.
## `xcz`
```python theme={null}
xcz(b: GraphRepresentation, control: int, target: int, classically_controlled: list[bool] | None = None) -> None
```
X-controlled Z gate. Applies Z to target if control is in |-> state.
## `y`
```python theme={null}
y(b: GraphRepresentation, qubit: int) -> None
```
Apply Pauli Y gate.
## `y_error`
```python theme={null}
y_error(b: GraphRepresentation, qubit: int, p: float) -> None
```
Apply Y error with probability p.
## `ycx`
```python theme={null}
ycx(b: GraphRepresentation, control: int, target: int) -> None
```
Y-controlled X gate. Applies X to target if control is in |-i> state.
## `ycy`
```python theme={null}
ycy(b: GraphRepresentation, control: int, target: int) -> None
```
Y-controlled Y gate. Applies Y to target if control is in |-i> state.
## `ycz`
```python theme={null}
ycz(b: GraphRepresentation, control: int, target: int, classically_controlled: list[bool] | None = None) -> None
```
Y-controlled Z gate. Applies Z to target if control is in |-i> state.
## `z`
```python theme={null}
z(b: GraphRepresentation, qubit: int) -> None
```
Apply Pauli Z gate.
## `z_error`
```python theme={null}
z_error(b: GraphRepresentation, qubit: int, p: float) -> None
```
Apply Z error with probability p.
## `z_phase`
```python theme={null}
z_phase(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply Z-axis phase rotation to qubit. This is equivalent to `r_z` up to a phase.
# parse
Source: https://bloqade.quera.com/tsim/api-reference/core/parse
Parser for converting stim circuits to ZX graph representations.
## class `GraphRepresentation`
```python theme={null}
GraphRepresentation(graph: GraphS = GraphS(), rec: list[int] = list(), silent_rec: list[int] = list(), detectors: list[int] = list(), observables_dict: dict[int, int] = dict(), first_vertex: dict[int, int] = dict(), last_vertex: dict[int, int] = dict(), channel_probs: list[np.ndarray] = list(), correlated_error_probs: list[float] = list(), num_error_bits: int = 0, num_correlated_error_bits: int = 0, track_classical_wires: bool = False)
```
ZX graph built from a stim circuit.
Contains the graph and all auxiliary data needed for sampling.
## `correlated_error`
```python theme={null}
correlated_error(b: GraphRepresentation, qubits: list[int], types: list[Literal['X', 'Y', 'Z']], p: float) -> None
```
Add a correlated error term affecting multiple qubits with given Pauli types.
## `detector`
```python theme={null}
detector(b: GraphRepresentation, rec: list[int], args = ()) -> None
```
Add detector annotation that XORs the given measurement record bits.
## `finalize_correlated_error`
```python theme={null}
finalize_correlated_error(b: GraphRepresentation) -> None
```
Finalize the current correlated error channel.
1. Rename all "c\{i}" phases to "e\{num\_error\_bits + i}" in the graph
2. Compute and append the 2^k probability array to channel\_probs
3. Increment num\_error\_bits by k
4. Reset num\_correlated\_error\_bits to 0 and correlated\_error\_probs to \[]
## `is_t_tag`
```python theme={null}
is_t_tag(tag: str) -> bool
```
Return whether a Stim tag encodes a tsim T-family gate.
## `mpad`
```python theme={null}
mpad(b: GraphRepresentation, value: int, p: float = 0) -> None
```
Pad measurement record with a fixed bit value.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `value` (`int`) — The bit value to record (0 or 1).
* `p` (`float`) — Error probability for the recorded bit.
## `mpp`
```python theme={null}
mpp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], invert: bool = False, p: float = 0) -> None
```
Measure a single Pauli product.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product.
* `invert` (`bool`) — Whether to invert the measurement result.
* `p` (`float`) — Measurement flip error probability.
## `observable_include`
```python theme={null}
observable_include(b: GraphRepresentation, rec: list[int], idx: int) -> None
```
Add observable annotation that XORs the given measurement record bits.
## `parse_parametric_tag`
```python theme={null}
parse_parametric_tag(instruction: stim.CircuitInstruction) -> tuple[str, dict[str, Fraction]] | None
```
Parse the parametric tag on an instruction (e.g. `I[R_Z(theta=0.3*pi)]`).
Supports gates: R\_Z, R\_X, R\_Y, U3.
**Parameters:**
* `instruction` (`stim.CircuitInstruction`) — The stim instruction whose tag will be parsed.
**Returns:**
* `tuple[str, dict[str, Fraction]] | None` — Tuple of (gate\_name, params\_dict) when the instruction's tag is a
* `tuple[str, dict[str, Fraction]] | None` — well-formed parametric tag, or `None` when the tag is not
* `tuple[str, dict[str, Fraction]] | None` — parametric-looking (no `name(...)` shape, or empty).
**Raises:**
* `ValueError` — When the tag looks parametric (matches `name(...)`) but is malformed: a parameter value does not parse, the gate name is unknown, or the parameter keys do not match the expected set for the gate.
## `parse_stim_circuit`
```python theme={null}
parse_stim_circuit(stim_circuit: stim.Circuit, track_classical_wires: bool = False) -> GraphRepresentation
```
Parse a stim circuit into a GraphRepresentation.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to convert.
* `track_classical_wires` (`bool`) — Whether to track classical wires.
**Returns:**
* `GraphRepresentation` — A GraphRepresentation containing the ZX graph and all auxiliary data.
## `r_pauli`
```python theme={null}
r_pauli(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], theta: Fraction, dagger: bool = False) -> None
```
Apply exp(-i theta pi/2 P) for a Pauli product P, with theta in half-turns.
Generalizes `SPP` (theta=1/2) and `TPP` (theta=1/4) to an arbitrary angle.
For a single qubit, `r_pauli` reduces to the named single-qubit rotation in
that Pauli's basis (e.g. `R_PAULI Z0` is `R_Z`). If `dagger` is True, apply
exp(+i theta pi/2 P) instead.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `theta` (`Fraction`) — Rotation angle in half-turns (units of pi).
* `dagger` (`bool`) — If True, negate the rotation angle.
## `r_x`
```python theme={null}
r_x(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_X rotation gate with given phase (in units of π).
## `r_y`
```python theme={null}
r_y(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_Y rotation gate with given phase (in units of π).
## `r_z`
```python theme={null}
r_z(b: GraphRepresentation, qubit: int, phase: Fraction) -> None
```
Apply R\_Z rotation gate with given phase (in units of π).
## `spp`
```python theme={null}
spp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], dagger: bool = False) -> None
```
Apply exp(-i pi/4 P) (up to global phase) for a Pauli product P.
Phases the -1 eigenspace of P by i (or -i if dagger). For a single qubit,
`SPP Z0` is the S gate and `SPP_DAG Z0` is S\_DAG.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `dagger` (`bool`) — If True, apply exp(+i pi/4 P) (phase by -i) instead.
## `tick`
```python theme={null}
tick(b: GraphRepresentation) -> None
```
Add a tick to the circuit (align all qubits to same row).
## `tpp`
```python theme={null}
tpp(b: GraphRepresentation, paulis: list[tuple[Literal['X', 'Y', 'Z'], int]], dagger: bool = False) -> None
```
Apply exp(-i pi/8 P) (up to global phase) for a Pauli product P.
Phases the -1 eigenspace of P by exp(i pi/4) (or exp(-i pi/4) if dagger).
For a single qubit, `TPP Z0` is the T gate and `TPP_DAG Z0` is T\_DAG.
**Parameters:**
* `b` (`GraphRepresentation`) — The graph representation to modify.
* `paulis` (`list[tuple[Literal['X', 'Y', 'Z'], int]]`) — List of (pauli\_type, qubit) pairs defining the Pauli product P.
* `dagger` (`bool`) — If True, apply exp(+i pi/8 P) (phase by exp(-i pi/4)) instead.
## `u3`
```python theme={null}
u3(b: GraphRepresentation, qubit: int, theta: Fraction, phi: Fraction, lambda_: Fraction) -> None
```
Apply U3 gate: U3(θ,φ,λ) = R\_Z(φ)·R\_Y(θ)·R\_Z(λ).
# tags
Source: https://bloqade.quera.com/tsim/api-reference/core/tags
Helpers for tags that encode tsim-specific gate metadata.
## `decode_t_user_tag`
```python theme={null}
decode_t_user_tag(tag: str) -> str
```
Return the user tag attached to an encoded T-family gate tag.
## `encode_t_tag`
```python theme={null}
encode_t_tag(user_tag: str = '') -> str
```
Encode a T-family gate tag while preserving an optional user tag.
## `is_t_tag`
```python theme={null}
is_t_tag(tag: str) -> bool
```
Return whether a Stim tag encodes a tsim T-family gate.
# types
Source: https://bloqade.quera.com/tsim/api-reference/core/types
Core data types for the tsim compilation and sampling pipeline.
This module defines immutable data structures that represent the different
stages of circuit compilation:
1. SamplingGraph: Result of parsing and reducing a circuit graph
2. CompiledComponent: A single compiled connected component
3. CompiledProgram: The full compiled circuit ready for sampling
## class `CompiledComponent`
```python theme={null}
CompiledComponent()
```
A single compiled connected component of a circuit.
Each component is independent and can be sampled separately. The results
are then combined according to output\_indices.
## class `CompiledProgram`
```python theme={null}
CompiledProgram(components: tuple[CompiledComponent, ...], direct_f_indices: Array, direct_flips: Array, output_order: Array, output_reindex: Array | None, num_outputs: int, num_detectors: int)
```
A fully compiled circuit program ready for sampling.
This is the result of compiling a SamplingGraph and contains everything
needed to sample from the circuit.
## class `CompiledScalarGraphs`
```python theme={null}
CompiledScalarGraphs()
```
JAX-compatible compiled representation of a list of scalar ZX graphs.
The scalar for each graph is a product of four term families, multiplied by
a per-graph `ScalarPrefactor` (global phase, floatfactor, `2^power2`,
optional approximate complex floatfactor). All arrays are static-shaped so
the whole struct can be traced under `jax.jit`.
## class `SamplingGraph`
```python theme={null}
SamplingGraph(graph: BaseGraph, error_transform: np.ndarray, channel_probs: list[np.ndarray], num_outputs: int, num_detectors: int)
```
Result of the graph preparation phase for sampling.
Contains all data structures needed for sampling. This represents a circuit
that has been:
1. Parsed from stim format
2. Converted to a ZX graph
3. Doubled (composed with adjoint)
4. Reduced via zx.full\_reduce
5. Had its error basis transformed (Gaussian elimination: e → f)
# tsim
Source: https://bloqade.quera.com/tsim/api-reference/index
ZX-calculus based circuit sampler for universal quantum circuits.
tsim supports fast sampling from quantum circuits with few non-Clifford gates.
It exposes `Circuit`, `CompiledDetectorSampler`, and `CompiledMeasurementSampler`
objects, which follow the Stim API.
The package is organized as follows:
* `circuit.py`: The thin `Circuit` wrapper around `stim.Circuit`.
* `sampler.py`: Orchestrates compilation and evaluation to sample measurements or detectors.
* `core/`: ZX graph construction, parsing, and data types.
* `compile/`: JAX compilation pipeline including stabilizer rank decomposition.
* `noise/`: Pauli noise channels and detector error models.
* `utils/`: Utility functions for visualization and linear algebra.
## class `Circuit`
```python theme={null}
Circuit(stim_program_text: str = '')
```
Quantum circuit as a thin wrapper around stim.Circuit.
Circuits are constructed like stim circuits:
\>>> circuit = Circuit('''
... H 0
... T 0
... CNOT 0 1
... M 0 1
... ''')
### `append`
```python theme={null}
append(name: str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit, targets: int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString] = (), arg: float | Iterable[float] | None = None, tag: str = '') -> None
```
Append an operation into the circuit.
**Parameters:**
* `name` (`str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit`) — The name of the operation's gate (e.g. "H" or "M" or "CNOT"). This argument can also be set to a `stim.CircuitInstruction` or `stim.CircuitInstructionBlock`, which results in the instruction or block being appended to the circuit. The other arguments (targets and arg) can't be specified when doing so.
* `targets` (`int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString]`) — The objects operated on by the gate. This can be either a single target or an iterable of multiple targets. Each target can be: An int: The index of a targeted qubit. A `stim.GateTarget`: Could be a variety of things. Methods like `stim.target_rec`, `stim.target_sweet`, `stim.target_x`, and `stim.CircuitInstruction.__getitem__` all return this type. A `stim.PauliString`: This will automatically be expanded into a product of pauli targets like `X1*Y2*Z3`.
* `arg` (`float | Iterable[float] | None`) — The "parens arguments" for the gate, such as the probability for a noise operation. A double or list of doubles parameterizing the gate. Different gates take different parens arguments. For example, X\_ERROR takes a probability, OBSERVABLE\_INCLUDE takes an observable index, and PAULI\_CHANNEL\_1 takes three disjoint probabilities.
* `tag` (`str`) — A customizable string attached to the instruction.
### `append_from_stim_program_text`
```python theme={null}
append_from_stim_program_text(stim_program_text: str) -> None
```
Append operations described by a Stim format program into the circuit.
Supports the same shorthand syntax as the constructor.
### `approx_equals`
```python theme={null}
approx_equals(other: object, atol: float) -> bool
```
Check if a circuit is approximately equal to another circuit.
Two circuits are approximately equal if they are equal up to slight
perturbations of instruction arguments such as probabilities. For example,
`X_ERROR(0.100) 0` is approximately equal to `X_ERROR(0.099)` within an absolute
tolerance of 0.002. All other details of the circuits (such as the ordering of
instructions and targets) must be exactly the same.
**Parameters:**
* `other` (`object`) — The circuit, or other object, to compare to this one.
* `atol` (`float`) — The absolute error tolerance. The maximum amount each probability may have been perturbed by.
**Returns:**
* `bool` — True if the given object is a circuit approximately equal up to the
* `bool` — receiving circuit up to the given tolerance, otherwise False.
### `cast_to_stim`
```python theme={null}
cast_to_stim() -> stim.Circuit
```
Return self with type cast to `stim.Circuit`.
This is useful for passing the circuit to functions that expect a
`stim.Circuit`.
### `compile_detector_sampler`
```python theme={null}
compile_detector_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledDetectorSampler
```
Compile circuit into a detector sampler.
Connected components whose single output is deterministically given by
one f-variable are handled via a fast direct path (no compilation or
autoregressive sampling). Remaining components go through the full
compilation pipeline.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledDetectorSampler` — A CompiledDetectorSampler that can be used to sample detectors and observables.
### `compile_m2d_converter`
```python theme={null}
compile_m2d_converter(skip_reference_sample: bool = False) -> stim.CompiledMeasurementsToDetectionEventsConverter
```
Create a measurement-to-detection-event converter for the given circuit.
The converter can efficiently compute detection events and observable flips
from raw measurement data.
The converter uses a noiseless reference sample, collected from the circuit
using stim's Tableau simulator during initialization of the converter, as a
baseline for determining what the expected value of a detector is.
Note that the expected behavior of gauge detectors (detectors that are not
actually deterministic under noiseless execution) can vary depending on the
reference sample. Stim mitigates this by always generating the same reference
sample for a given circuit.
**Parameters:**
* `skip_reference_sample` (`bool`) — Defaults to False. When set to True, the reference sample used by the converter is initialized to all-zeroes instead of being collected from the circuit. This should only be used if it's known that the all-zeroes sample is actually a possible result from the circuit (under noiseless execution).
**Returns:**
* `stim.CompiledMeasurementsToDetectionEventsConverter` — An initialized stim.CompiledMeasurementsToDetectionEventsConverter.
### `compile_sampler`
```python theme={null}
compile_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledMeasurementSampler
```
Compile circuit into a measurement sampler.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledMeasurementSampler` — A CompiledMeasurementSampler that can be used to sample measurements.
### `copy`
```python theme={null}
copy() -> Circuit
```
Create a copy of this circuit.
### `detector_error_model`
```python theme={null}
detector_error_model(decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true.
**Parameters:**
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. This is not supported by tsim and setting it to true will raise an error. The argument is present for compatibility with stim.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_gauge\_detectors=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. Note that, unlike in stim, logical observables are also allowed to be non-deterministic.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
### `diagram`
```python theme={null}
diagram(type: Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg'] = 'timeline-svg', tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, height: float | None = None, width: float | None = None, zoomable: bool = True, kwargs: Any = {}) -> Any
```
Return a diagram of the circuit, from a variety of options.
**Parameters:**
* `type` (`Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg']`) — The type of diagram. Available types are: "pyzx": A pyzx SVG of the ZX diagram of the circuit. "pyzx-dets": A pyzx SVG of the ZX diagram that is used to compute probabilities of detectors and observables. "pyzx-meas": A pyzx SVG of the ZX diagram that is used to compute probabilities of measurements. "timeline-svg": An SVG image of the operations applied by the circuit over time. Includes annotations showing the measurement record index that each measurement writes to, and the measurements used by detectors. "timeslice-svg": An SVG image of the operations applied between two TICK instructions in the circuit, with the operations laid out in 2d.
* `tick` (`int | range | None`) — Required for time slice diagrams. Specifies which TICK instruction, or range of TICK instructions, to slice at. Note that the first TICK instruction in the circuit corresponds tick=1. The value tick=0 refers to the very start of the circuit. Passing `range(A, B)` for a detector slice will show the slices for ticks A through B including A but excluding B. Passing `range(A, B)` for a time slice will show the operations between tick A and tick B.
* `rows` (`int | None`) — In diagrams that have multiple separate pieces, such as timeslice diagrams and detslice diagrams, this controls how many rows of pieces there will be. If not specified, a number of rows that creates a roughly square layout will be chosen.
* `filter_coords` (`Iterable[Iterable[float] | stim.DemTarget]`) — A list of things to include in the diagram. Different effects depending on the diagram. For detslice diagrams, the filter defaults to showing all detectors and no observables. When specified, each list entry can be a collection of floats (detectors whose coordinates start with the same numbers will be included), a stim.DemTarget (specifying a detector or observable to include), a string like "D5" or "L0" specifying a detector or observable to include.
* `height` (`float | None`) — Optional height for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types. For timeline-svg, when both height and width are None, the height is automatically determined based on the number of qubits. When only one dimension is given, the other is computed from the SVG aspect ratio.
* `width` (`float | None`) — Optional width for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types.
* `zoomable` (`bool`) — If True (default), wraps SVG diagrams in an interactive container with pan and Ctrl/Cmd+wheel zoom. Only applies to timeline-svg and timeslice-svg diagram types.
* `**kwargs` (`Any`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `Any` — An object whose `__str__` method returns the diagram, so that
* `Any` — writing the diagram to a file works correctly. The returned
* `Any` — object may also define methods such as `_repr_html_`, so that
* `Any` — ipython notebooks recognize it can be shown using a specialized
* `Any` — viewer instead of as raw text.
### `flattened`
```python theme={null}
flattened() -> Circuit
```
Return a copy of the circuit with all repeat blocks expanded.
### `from_file`
```python theme={null}
from_file(cls, filename: str) -> Circuit
```
Create a Circuit from a file.
**Parameters:**
* `filename` (`str`) — The filename to read the circuit from.
**Returns:**
* `Circuit` — A new Circuit instance.
### `from_stim_program`
```python theme={null}
from_stim_program(cls, stim_circuit: stim.Circuit) -> Circuit
```
Create a Circuit from a stim.Circuit object.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to wrap.
**Returns:**
* `Circuit` — A new Circuit instance.
### `get_graph`
```python theme={null}
get_graph() -> BaseGraph
```
Construct the ZX graph.
### `get_sampling_graph`
```python theme={null}
get_sampling_graph(sample_detectors: bool = False) -> BaseGraph
```
Get a ZX graph that can be used to compute probabilities.
This graph will be constructed as follows:
1. Double the ZX-diagram by composing it with its adjoint.
2. Connect all rec\[i] vertices to their corresponding adjoint rec\[i] vertices.
3. Add outputs:
(a) When sampling measurements (i.e. `sample_detectors` is False),
add output nodes for each measurement. Detectors and observables are
removed since they are ignored when sampling measurements.
(b) When sampling detectors and observables (i.e. `sample_detectors` is True),
add output nodes for each detector and observable. Only one set of detector
and observable nodes is kept, i.e., detectors and observables are not
composed with their adjoints.
**Parameters:**
* `sample_detectors` (`bool`) — If True, sample detectors and observables instead of measurements.
**Returns:**
* `BaseGraph` — A ZX graph for sampling.
### `inverse`
```python theme={null}
inverse() -> Circuit
```
Return the inverse of the circuit.
### `pop`
```python theme={null}
pop(index: int = -1) -> stim.CircuitInstruction | stim.CircuitRepeatBlock
```
Pops an operation from the end of the circuit, or at the given index.
**Parameters:**
* `index` (`int`) — Defaults to -1 (end of circuit). The index to pop from.
**Returns:**
* `stim.CircuitInstruction | stim.CircuitRepeatBlock` — The popped instruction or repeat block.
**Raises:**
* `IndexError` — The given index is outside the bounds of the circuit.
### `tcount`
```python theme={null}
tcount() -> int
```
Count the number of T gates in the circuit.
### `to_matrix`
```python theme={null}
to_matrix() -> Any
```
Convert circuit to matrix representation.
### `to_tensor`
```python theme={null}
to_tensor() -> Any
```
Convert circuit to tensor representation.
### `without_annotations`
```python theme={null}
without_annotations() -> Circuit
```
Return a copy of the circuit with all detector and observable annotations removed.
### `without_noise`
```python theme={null}
without_noise() -> Circuit
```
Return a copy of the circuit with all noise removed.
## class `CompiledDetectorSampler`
```python theme={null}
CompiledDetectorSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples detector and observable outcomes from a quantum circuit.
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None, prepend_observables: bool = False, append_observables: bool = False, separate_observables: bool = False, bit_packed: bool = False, use_detector_reference_sample: bool = False, use_observable_reference_sample: bool = False, postselection_mask: np.ndarray | None = None) -> np.ndarray | tuple[np.ndarray, np.ndarray]
```
Return detector samples from the circuit.
The circuit must define the detectors using DETECTOR instructions. Observables
defined by OBSERVABLE\_INCLUDE instructions can also be included in the results
as honorary detectors.
**Parameters:**
* `shots` (`int`) — The number of times to sample every detector in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
* `separate_observables` (`bool`) — Defaults to False. When set to True, the return value is a (detection\_events, observable\_flips) tuple instead of a flat detection\_events array.
* `prepend_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the start of the results.
* `append_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the end of the results.
* `bit_packed` (`bool`) — Defaults to false. When set, results are bit-packed.
* `use_detector_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with detector outcomes so that results represent deviations from the noiseless baseline. This should only be used when detectors are deterministic. Otherwise, it can unpredictably change the results.
* `use_observable_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with observable outcomes so that results represent deviations from the noiseless baseline. This should only be used when observables are deterministic. Otherwise, it can unpredictably change the results.
* `postselection_mask` (`np.ndarray | None`) — Optional boolean array of length `num_detectors`. When set, shots where any masked direct detector fires skip the JAX sampling loop. All `shots` rows are still returned: survivors contain the full sample, while discarded rows retain direct detector columns and fill component columns with `False`. Re-apply the mask to the returned detector columns to recover surviving shots.
**Returns:**
* `np.ndarray | tuple[np.ndarray, np.ndarray]` — A numpy array or tuple of numpy arrays containing the samples.
**Raises:**
* `ValueError` — If `separate_observables` is combined with `prepend_observables` or `append_observables`.
## class `CompiledMeasurementSampler`
```python theme={null}
CompiledMeasurementSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples measurement outcomes from a quantum circuit.
Uses sequential decomposition \[0, 1, 2, ..., n] where:
* compiled\_scalar\_graphs\[0]: normalization (0 outputs plugged)
* compiled\_scalar\_graphs\[i]: cumulative probability up to bit i
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None) -> np.ndarray
```
Sample measurement outcomes from the circuit.
**Parameters:**
* `shots` (`int`) — The number of times to sample every measurement in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
**Returns:**
* `np.ndarray` — A numpy array containing the measurement samples.
# channels
Source: https://bloqade.quera.com/tsim/api-reference/noise/channels
Pauli noise channels and error sampling infrastructure.
## class `Channel`
```python theme={null}
Channel(probs: np.ndarray, unique_col_ids: tuple[int, ...])
```
A probability distribution over error outcomes.
Outcome indices: bit position `i` corresponds
to `1 \<\< i` in `probs`. For example, in a 2-bit channel, index 1
(0b01) is bit pattern `bit0=1, bit1=0` and index 2 (0b10) is
`bit0=0, bit1=1`.
## class `ChannelSampler`
```python theme={null}
ChannelSampler(channel_probs: list[np.ndarray], error_transform: np.ndarray, seed: int | None = None)
```
Samples from multiple error channels and transforms to a reduced basis.
This class combines multiple error channels (each producing error bits e0, e1, ...)
and applies a linear transformation over GF(2) to convert samples from the original
"e" basis to a reduced "f" basis using geometric-skip sampling optimized for
low-noise regimes.
`f_i = error_transform_ij * e_j mod 2`. Within each channel,
probability-array bit 0 corresponds to the first produced error bit, bit 1
to the second, and so on.
Channels are automatically simplified by:
1. Removing bits e\_i that do not affect any f-variable (i.e. all-zero columns in error\_transform)
2. Folding duplicate column IDs, i.e. channels whose column signatures contain duplicate IDs.
3. Merging channels with identical column signatures, i.e. channels whose corresponding
columns in error\_transform are identical.
4. Absorbing channels whose signatures are subsets of others, i.e. channels whose corresponding
columns in error\_transform are a strict subset of another channel's columns.
**Example:**
\>>> probs = \[error\_probs(0.1), error\_probs(0.2)] # two 1-bit channels
\>>> transform = np.array(\[\[1, 1]]) # f0 = e0 XOR e1
\>>> sampler = ChannelSampler(probs, transform)
\>>> samples = sampler.sample(1000) # shape (1000, 1)
### `sample`
```python theme={null}
sample(num_samples: int = 1) -> np.ndarray
```
Sample from all error channels and transform to new error basis.
Uses geometric-skip sampling, optimized for low-noise regimes where
P(non-identity) \<\< 1 per channel.
**Parameters:**
* `num_samples` (`int`) — Number of samples to draw.
**Returns:**
* `np.ndarray` — NumPy array of shape (num\_samples, num\_f) with uint8 values indicating
* `np.ndarray` — which f-variables are set for each sample.
## `absorb_subset_channels`
```python theme={null}
absorb_subset_channels(channels: list[Channel], max_bits: int = 4) -> list[Channel]
```
Absorb channels whose signatures are subsets of others.
If channel A's signatures are a strict subset of channel B's signatures,
and |B| \<= max\_bits, then A is absorbed into B.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels
* `max_bits` (`int`) — Maximum number of bits allowed per channel
**Returns:**
* `list[Channel]` — List with no channel being a strict subset of another
## `correlated_error_probs`
```python theme={null}
correlated_error_probs(probabilities: list[float]) -> np.ndarray
```
Build probability distribution for correlated error chain.
Given conditional probabilities \[p1, p2, ..., pk] from a chain of
CORRELATED\_ERROR(p1) ELSE\_CORRELATED\_ERROR(p2) ... ELSE\_CORRELATED\_ERROR(pk),
computes the joint probability distribution over 2^k outcomes.
Since errors are mutually exclusive, only outcomes with at most one bit set
have non-zero probability.
* `probs[0]` is the probability that no branch fires.
* `probs[1 \<\< i]` is the probability that branch `i` fires after all
previous branches did not fire.
**Parameters:**
* `probabilities` (`list[float]`) — List of conditional probabilities \[p1, p2, ..., pk]
**Returns:**
* `np.ndarray` — Array of shape (2^k,) with probabilities for each outcome.
## `error_probs`
```python theme={null}
error_probs(p: float) -> np.ndarray
```
Single-bit error channel.
Returns `[P(bit0=0), P(bit0=1)]`.
## `expand_channel`
```python theme={null}
expand_channel(channel: Channel, target_col_ids: tuple[int, ...]) -> Channel
```
Expand a channel's distribution to a larger signature set.
The channel's existing column IDs must be a strict subset of
`target_col_ids` when considered as sets, and both tuples must be sorted.
New target bit positions are treated as always zero.
Duplicate source column IDs are allowed. When multiple source bits map to
the same target bit, their contribution is XORed, matching GF(2)
composition. Duplicate target column IDs are not allowed; channels with
duplicate IDs should be canonicalized before subset absorption.
**Parameters:**
* `channel` (`Channel`) — Channel to expand (must have sorted unique\_col\_ids)
* `target_col_ids` (`tuple[int, ...]`) — Target signature set (must be sorted superset)
**Returns:**
* `Channel` — New channel with expanded distribution
## `fold_duplicate_channel_bits`
```python theme={null}
fold_duplicate_channel_bits(channels: list[Channel]) -> list[Channel]
```
Canonicalize channels by XOR-folding duplicate column IDs.
If two bits in the same channel have identical column signatures, sampling
both bits only affects the reduced error basis through their parity. This
replaces those duplicate bits with one bit whose probability is the sum of
all old outcomes with the same XOR-folded value.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels with sorted unique\_col\_ids
**Returns:**
* `list[Channel]` — List of channels whose unique\_col\_ids contain no duplicates
## `heralded_pauli_channel_1_probs`
```python theme={null}
heralded_pauli_channel_1_probs(pi: float, px: float, py: float, pz: float) -> np.ndarray
```
Heralded single-qubit Pauli channel. Returns shape (8,).
Bit layout:
* bit 0: herald bit, written to the measurement record
* bit 1: Z error component
* bit 2: X error component
The non-zero outcomes are:
* index 0 (0b000): no herald, no Pauli error
* index 1 (0b001): herald + I
* index 3 (0b011): herald + Z
* index 5 (0b101): herald + X
* index 7 (0b111): herald + Y, represented as X+Z
## `merge_identical_channels`
```python theme={null}
merge_identical_channels(channels: list[Channel]) -> list[Channel]
```
Merge all channels with identical signature sets.
Groups channels by their unique\_col\_ids and convolves all channels
in each group into a single channel.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels
**Returns:**
* `list[Channel]` — List with at most one channel per unique signature set
## `normalize_channels`
```python theme={null}
normalize_channels(channels: list[Channel]) -> list[Channel]
```
Normalize channels by sorting unique\_col\_ids, permuting probs accordingly.
This ensures channels affecting the same set of columns have identical
`unique_col_ids` tuples, enabling `merge_identical_channels` to group
them. The probability tensor is transposed using the same axis permutation
so little-endian outcome bits continue to refer to the matching column IDs.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels
**Returns:**
* `list[Channel]` — List of channels with sorted unique\_col\_ids
## `pauli_channel_1_probs`
```python theme={null}
pauli_channel_1_probs(px: float, py: float, pz: float) -> np.ndarray
```
Single-qubit Pauli channel. Returns shape (4,).
Bit layout:
* bit 0: Z error component
* bit 1: X error component
The outcomes are:
* index 0 (0b00): I
* index 1 (0b01): Z
* index 2 (0b10): X
* index 3 (0b11): Y
## `pauli_channel_2_probs`
```python theme={null}
pauli_channel_2_probs(pix: float, piy: float, piz: float, pxi: float, pxx: float, pxy: float, pxz: float, pyi: float, pyx: float, pyy: float, pyz: float, pzi: float, pzx: float, pzy: float, pzz: float) -> np.ndarray
```
Two-qubit Pauli channel. Returns shape (16,).
Bit layout:
* bit 0: Z error component on `qubit_i`
* bit 1: X error component on `qubit_i`
* bit 2: Z error component on `qubit_j`
* bit 3: X error component on `qubit_j`
With that layout, index `z_i + 2*x_i + 4*z_j + 8*x_j` stores the
probability for the corresponding two-qubit Pauli outcome. The arguments
follow Stim's naming convention: `pix` is I on `qubit_i` and X on
`qubit_j`, `pzi` is Z on `qubit_i` and I on `qubit_j`, etc.
## `reduce_null_bits`
```python theme={null}
reduce_null_bits(channels: list[Channel], null_col_id: int | None = None) -> list[Channel]
```
Remove bits corresponding to the null column (all-zero column).
If a channel has bits mapped to null\_col\_id (representing an all-zero
column in the transform matrix), those bits don't affect any f-variable
and can be marginalized out by summing over them.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels
* `null_col_id` (`int | None`) — Column ID representing the all-zero column, or None if there is no all-zero column.
**Returns:**
* `list[Channel]` — List of channels with null bits marginalized out. Channels with all
* `list[Channel]` — null entries are removed entirely (they have no effect on outputs).
## `simplify_channels`
```python theme={null}
simplify_channels(channels: list[Channel], max_bits: int = 4, null_col_id: int | None = None) -> list[Channel]
```
Simplify channels by removing null columns, folding, merging identical and absorbing subsets.
**Parameters:**
* `channels` (`list[Channel]`) — List of channels to simplify
* `max_bits` (`int`) — Maximum number of bits allowed per channel
* `null_col_id` (`int | None`) — Column ID representing the all-zero column, or None if there is no all-zero column.
**Returns:**
* `list[Channel]` — Simplified list of channels
## `xor_convolve`
```python theme={null}
xor_convolve(probs_a: np.ndarray, probs_b: np.ndarray) -> np.ndarray
```
XOR convolution of two probability distributions.
Computes P(A XOR B = o) = sum\_\{a ^ b = o} P(A=a) \* P(B=b)
**Parameters:**
* `probs_a` (`np.ndarray`) — Shape (2^k,) probabilities for channel A
* `probs_b` (`np.ndarray`) — Shape (2^k,) probabilities for channel B (same size as A)
**Returns:**
* `np.ndarray` — Shape (2^k,) probabilities for the combined channel
# dem
Source: https://bloqade.quera.com/tsim/api-reference/noise/dem
Detector error model generation for QEC decoder integration.
## `get_detector_error_model`
```python theme={null}
get_detector_error_model(stim_circuit: stim.Circuit, allow_non_deterministic_observables: bool = True, decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true. This is achieved by converting logical
observables into detectors, calling the stim.Circuit.detector\_error\_model(allow\_gauge\_detectors=True), and then
reconverting the detectors back into observables in the detector error model.
WARNING: If the circuit has distance one, i.e. if there are errors that only flip logical observables,
this method will return an incorrect detector error model.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to compute the detector error model for.
* `allow_non_deterministic_observables` (`bool`) — Defaults to true. When set to true, the detector error model allows for non-deterministic observables. This is achieved by converting logical observables into detectors, calling the stim.Circuit.detector\_error\_model(allow\_gauge\_detectors=True), and then reconverting the detectors back into observables in the detector error model.
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. When allow\_non\_deterministic\_observables is set to true, decomposition is not supported.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_non\_deterministic\_observables=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. When set to true, these detectors are instead considered to be part of degrees freedom that can be removed from the error model. For example, if detectors D1 and D3 both anti-commute with a reset, then the error model has a gauge `error(0.5) D1 D3`. When gauges are identified, one of the involved detectors is removed from the system using Gaussian elimination. Note that logical observables are still verified to be deterministic, even if this option is set.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
# sampler
Source: https://bloqade.quera.com/tsim/api-reference/sampler
Compiled samplers for measurements and detectors.
## class `ChannelSampler`
```python theme={null}
ChannelSampler(channel_probs: list[np.ndarray], error_transform: np.ndarray, seed: int | None = None)
```
Samples from multiple error channels and transforms to a reduced basis.
This class combines multiple error channels (each producing error bits e0, e1, ...)
and applies a linear transformation over GF(2) to convert samples from the original
"e" basis to a reduced "f" basis using geometric-skip sampling optimized for
low-noise regimes.
`f_i = error_transform_ij * e_j mod 2`. Within each channel,
probability-array bit 0 corresponds to the first produced error bit, bit 1
to the second, and so on.
Channels are automatically simplified by:
1. Removing bits e\_i that do not affect any f-variable (i.e. all-zero columns in error\_transform)
2. Folding duplicate column IDs, i.e. channels whose column signatures contain duplicate IDs.
3. Merging channels with identical column signatures, i.e. channels whose corresponding
columns in error\_transform are identical.
4. Absorbing channels whose signatures are subsets of others, i.e. channels whose corresponding
columns in error\_transform are a strict subset of another channel's columns.
**Example:**
\>>> probs = \[error\_probs(0.1), error\_probs(0.2)] # two 1-bit channels
\>>> transform = np.array(\[\[1, 1]]) # f0 = e0 XOR e1
\>>> sampler = ChannelSampler(probs, transform)
\>>> samples = sampler.sample(1000) # shape (1000, 1)
### `sample`
```python theme={null}
sample(num_samples: int = 1) -> np.ndarray
```
Sample from all error channels and transform to new error basis.
Uses geometric-skip sampling, optimized for low-noise regimes where
P(non-identity) \<\< 1 per channel.
**Parameters:**
* `num_samples` (`int`) — Number of samples to draw.
**Returns:**
* `np.ndarray` — NumPy array of shape (num\_samples, num\_f) with uint8 values indicating
* `np.ndarray` — which f-variables are set for each sample.
## class `Circuit`
```python theme={null}
Circuit(stim_program_text: str = '')
```
Quantum circuit as a thin wrapper around stim.Circuit.
Circuits are constructed like stim circuits:
\>>> circuit = Circuit('''
... H 0
... T 0
... CNOT 0 1
... M 0 1
... ''')
### `append`
```python theme={null}
append(name: str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit, targets: int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString] = (), arg: float | Iterable[float] | None = None, tag: str = '') -> None
```
Append an operation into the circuit.
**Parameters:**
* `name` (`str | stim.CircuitInstruction | stim.CircuitRepeatBlock | stim.Circuit`) — The name of the operation's gate (e.g. "H" or "M" or "CNOT"). This argument can also be set to a `stim.CircuitInstruction` or `stim.CircuitInstructionBlock`, which results in the instruction or block being appended to the circuit. The other arguments (targets and arg) can't be specified when doing so.
* `targets` (`int | stim.GateTarget | stim.PauliString | Iterable[int | stim.GateTarget | stim.PauliString]`) — The objects operated on by the gate. This can be either a single target or an iterable of multiple targets. Each target can be: An int: The index of a targeted qubit. A `stim.GateTarget`: Could be a variety of things. Methods like `stim.target_rec`, `stim.target_sweet`, `stim.target_x`, and `stim.CircuitInstruction.__getitem__` all return this type. A `stim.PauliString`: This will automatically be expanded into a product of pauli targets like `X1*Y2*Z3`.
* `arg` (`float | Iterable[float] | None`) — The "parens arguments" for the gate, such as the probability for a noise operation. A double or list of doubles parameterizing the gate. Different gates take different parens arguments. For example, X\_ERROR takes a probability, OBSERVABLE\_INCLUDE takes an observable index, and PAULI\_CHANNEL\_1 takes three disjoint probabilities.
* `tag` (`str`) — A customizable string attached to the instruction.
### `append_from_stim_program_text`
```python theme={null}
append_from_stim_program_text(stim_program_text: str) -> None
```
Append operations described by a Stim format program into the circuit.
Supports the same shorthand syntax as the constructor.
### `approx_equals`
```python theme={null}
approx_equals(other: object, atol: float) -> bool
```
Check if a circuit is approximately equal to another circuit.
Two circuits are approximately equal if they are equal up to slight
perturbations of instruction arguments such as probabilities. For example,
`X_ERROR(0.100) 0` is approximately equal to `X_ERROR(0.099)` within an absolute
tolerance of 0.002. All other details of the circuits (such as the ordering of
instructions and targets) must be exactly the same.
**Parameters:**
* `other` (`object`) — The circuit, or other object, to compare to this one.
* `atol` (`float`) — The absolute error tolerance. The maximum amount each probability may have been perturbed by.
**Returns:**
* `bool` — True if the given object is a circuit approximately equal up to the
* `bool` — receiving circuit up to the given tolerance, otherwise False.
### `cast_to_stim`
```python theme={null}
cast_to_stim() -> stim.Circuit
```
Return self with type cast to `stim.Circuit`.
This is useful for passing the circuit to functions that expect a
`stim.Circuit`.
### `compile_detector_sampler`
```python theme={null}
compile_detector_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledDetectorSampler
```
Compile circuit into a detector sampler.
Connected components whose single output is deterministically given by
one f-variable are handled via a fast direct path (no compilation or
autoregressive sampling). Remaining components go through the full
compilation pipeline.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledDetectorSampler` — A CompiledDetectorSampler that can be used to sample detectors and observables.
### `compile_m2d_converter`
```python theme={null}
compile_m2d_converter(skip_reference_sample: bool = False) -> stim.CompiledMeasurementsToDetectionEventsConverter
```
Create a measurement-to-detection-event converter for the given circuit.
The converter can efficiently compute detection events and observable flips
from raw measurement data.
The converter uses a noiseless reference sample, collected from the circuit
using stim's Tableau simulator during initialization of the converter, as a
baseline for determining what the expected value of a detector is.
Note that the expected behavior of gauge detectors (detectors that are not
actually deterministic under noiseless execution) can vary depending on the
reference sample. Stim mitigates this by always generating the same reference
sample for a given circuit.
**Parameters:**
* `skip_reference_sample` (`bool`) — Defaults to False. When set to True, the reference sample used by the converter is initialized to all-zeroes instead of being collected from the circuit. This should only be used if it's known that the all-zeroes sample is actually a possible result from the circuit (under noiseless execution).
**Returns:**
* `stim.CompiledMeasurementsToDetectionEventsConverter` — An initialized stim.CompiledMeasurementsToDetectionEventsConverter.
### `compile_sampler`
```python theme={null}
compile_sampler(strategy: DecompositionStrategy = 'cat5', seed: int | None = None) -> CompiledMeasurementSampler
```
Compile circuit into a measurement sampler.
**Parameters:**
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
* `seed` (`int | None`) — Random seed for the sampler. IMPORTANT: Currently, the sampler will only produce deterministic samples for fixed batch size. If deterministic samples are needed, the batch size should be set manually.
**Returns:**
* `CompiledMeasurementSampler` — A CompiledMeasurementSampler that can be used to sample measurements.
### `copy`
```python theme={null}
copy() -> Circuit
```
Create a copy of this circuit.
### `detector_error_model`
```python theme={null}
detector_error_model(decompose_errors: bool = False, flatten_loops: bool = False, allow_gauge_detectors: bool = False, approximate_disjoint_errors: bool = False, ignore_decomposition_failures: bool = False, block_decomposition_from_introducing_remnant_edges: bool = False) -> stim.DetectorErrorModel
```
Return a stim.DetectorErrorModel describing the error processes in the circuit.
Unlike the stim.Circuit.detector\_error\_model() method, this method allows for non-deterministic observables
when `allow_gauge_detectors` is set to true.
**Parameters:**
* `decompose_errors` (`bool`) — Defaults to false. When set to true, the error analysis attempts to decompose the components of composite error mechanisms (such as depolarization errors) into simpler errors, and suggest this decomposition via `stim.target_separator()` between the components. For example, in an XZ surface code, single qubit depolarization has a Y error term which can be decomposed into simpler X and Z error terms. Decomposition fails (causing this method to throw) if it's not possible to decompose large errors into simple errors that affect at most two detectors. This is not supported by tsim and setting it to true will raise an error. The argument is present for compatibility with stim.
* `flatten_loops` (`bool`) — Defaults to false. When set to true, the output will not contain any `repeat` blocks. When set to false, the error analysis watches for loops in the circuit reaching a periodic steady state with respect to the detectors being introduced, the error mechanisms that affect them, and the locations of the logical observables. When it identifies such a steady state, it outputs a repeat block. This is massively more efficient than flattening for circuits that contain loops, but creates a more complex output. Irrelevant unless allow\_gauge\_detectors=False.
* `allow_gauge_detectors` (`bool`) — Defaults to false. When set to false, the error analysis verifies that detectors in the circuit are actually deterministic under noiseless execution of the circuit. Note that, unlike in stim, logical observables are also allowed to be non-deterministic.
* `approximate_disjoint_errors` (`bool`) — Defaults to false. When set to false, composite error mechanisms with disjoint components (such as `PAULI_CHANNEL_1(0.1, 0.2, 0.0)`) can cause the error analysis to throw exceptions (because detector error models can only contain independent error mechanisms). When set to true, the probabilities of the disjoint cases are instead assumed to be independent probabilities. For example, a `PAULI_CHANNEL_1(0.1, 0.2, 0.0)` becomes equivalent to an `X_ERROR(0.1)` followed by a `Z_ERROR(0.2)`. This assumption is an approximation, but it is a good approximation for small probabilities. This argument can also be set to a probability between 0 and 1, setting a threshold below which the approximation is acceptable. Any error mechanisms that have a component probability above the threshold will cause an exception to be thrown.
* `ignore_decomposition_failures` (`bool`) — Defaults to False. When this is set to True, circuit errors that fail to decompose into graphlike detector error model errors no longer cause the conversion process to abort. Instead, the undecomposed error is inserted into the output. Whatever tool the detector error model is then given to is responsible for dealing with the undecomposed errors (e.g. a tool may choose to simply ignore them). Irrelevant unless decompose\_errors=True.
* `block_decomposition_from_introducing_remnant_edges` (`bool`) — Defaults to False. Requires that both A B and C D be present elsewhere in the detector error model in order to decompose A B C D into A B ^ C D. Normally, only one of A B or C D needs to appear to allow this decomposition. Remnant edges can be a useful feature for ensuring decomposition succeeds, but they can also reduce the effective code distance by giving the decoder single edges that actually represent multiple errors in the circuit (resulting in the decoder making misinformed choices when decoding). Irrelevant unless decompose\_errors=True.
### `diagram`
```python theme={null}
diagram(type: Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg'] = 'timeline-svg', tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, height: float | None = None, width: float | None = None, zoomable: bool = True, kwargs: Any = {}) -> Any
```
Return a diagram of the circuit, from a variety of options.
**Parameters:**
* `type` (`Literal['pyzx', 'pyzx-dets', 'pyzx-meas', 'timeline-svg', 'timeslice-svg']`) — The type of diagram. Available types are: "pyzx": A pyzx SVG of the ZX diagram of the circuit. "pyzx-dets": A pyzx SVG of the ZX diagram that is used to compute probabilities of detectors and observables. "pyzx-meas": A pyzx SVG of the ZX diagram that is used to compute probabilities of measurements. "timeline-svg": An SVG image of the operations applied by the circuit over time. Includes annotations showing the measurement record index that each measurement writes to, and the measurements used by detectors. "timeslice-svg": An SVG image of the operations applied between two TICK instructions in the circuit, with the operations laid out in 2d.
* `tick` (`int | range | None`) — Required for time slice diagrams. Specifies which TICK instruction, or range of TICK instructions, to slice at. Note that the first TICK instruction in the circuit corresponds tick=1. The value tick=0 refers to the very start of the circuit. Passing `range(A, B)` for a detector slice will show the slices for ticks A through B including A but excluding B. Passing `range(A, B)` for a time slice will show the operations between tick A and tick B.
* `rows` (`int | None`) — In diagrams that have multiple separate pieces, such as timeslice diagrams and detslice diagrams, this controls how many rows of pieces there will be. If not specified, a number of rows that creates a roughly square layout will be chosen.
* `filter_coords` (`Iterable[Iterable[float] | stim.DemTarget]`) — A list of things to include in the diagram. Different effects depending on the diagram. For detslice diagrams, the filter defaults to showing all detectors and no observables. When specified, each list entry can be a collection of floats (detectors whose coordinates start with the same numbers will be included), a stim.DemTarget (specifying a detector or observable to include), a string like "D5" or "L0" specifying a detector or observable to include.
* `height` (`float | None`) — Optional height for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types. For timeline-svg, when both height and width are None, the height is automatically determined based on the number of qubits. When only one dimension is given, the other is computed from the SVG aspect ratio.
* `width` (`float | None`) — Optional width for the rendered diagram in pixels. Only applied to timeline-svg and timeslice-svg diagram types.
* `zoomable` (`bool`) — If True (default), wraps SVG diagrams in an interactive container with pan and Ctrl/Cmd+wheel zoom. Only applies to timeline-svg and timeslice-svg diagram types.
* `**kwargs` (`Any`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `Any` — An object whose `__str__` method returns the diagram, so that
* `Any` — writing the diagram to a file works correctly. The returned
* `Any` — object may also define methods such as `_repr_html_`, so that
* `Any` — ipython notebooks recognize it can be shown using a specialized
* `Any` — viewer instead of as raw text.
### `flattened`
```python theme={null}
flattened() -> Circuit
```
Return a copy of the circuit with all repeat blocks expanded.
### `from_file`
```python theme={null}
from_file(cls, filename: str) -> Circuit
```
Create a Circuit from a file.
**Parameters:**
* `filename` (`str`) — The filename to read the circuit from.
**Returns:**
* `Circuit` — A new Circuit instance.
### `from_stim_program`
```python theme={null}
from_stim_program(cls, stim_circuit: stim.Circuit) -> Circuit
```
Create a Circuit from a stim.Circuit object.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to wrap.
**Returns:**
* `Circuit` — A new Circuit instance.
### `get_graph`
```python theme={null}
get_graph() -> BaseGraph
```
Construct the ZX graph.
### `get_sampling_graph`
```python theme={null}
get_sampling_graph(sample_detectors: bool = False) -> BaseGraph
```
Get a ZX graph that can be used to compute probabilities.
This graph will be constructed as follows:
1. Double the ZX-diagram by composing it with its adjoint.
2. Connect all rec\[i] vertices to their corresponding adjoint rec\[i] vertices.
3. Add outputs:
(a) When sampling measurements (i.e. `sample_detectors` is False),
add output nodes for each measurement. Detectors and observables are
removed since they are ignored when sampling measurements.
(b) When sampling detectors and observables (i.e. `sample_detectors` is True),
add output nodes for each detector and observable. Only one set of detector
and observable nodes is kept, i.e., detectors and observables are not
composed with their adjoints.
**Parameters:**
* `sample_detectors` (`bool`) — If True, sample detectors and observables instead of measurements.
**Returns:**
* `BaseGraph` — A ZX graph for sampling.
### `inverse`
```python theme={null}
inverse() -> Circuit
```
Return the inverse of the circuit.
### `pop`
```python theme={null}
pop(index: int = -1) -> stim.CircuitInstruction | stim.CircuitRepeatBlock
```
Pops an operation from the end of the circuit, or at the given index.
**Parameters:**
* `index` (`int`) — Defaults to -1 (end of circuit). The index to pop from.
**Returns:**
* `stim.CircuitInstruction | stim.CircuitRepeatBlock` — The popped instruction or repeat block.
**Raises:**
* `IndexError` — The given index is outside the bounds of the circuit.
### `tcount`
```python theme={null}
tcount() -> int
```
Count the number of T gates in the circuit.
### `to_matrix`
```python theme={null}
to_matrix() -> Any
```
Convert circuit to matrix representation.
### `to_tensor`
```python theme={null}
to_tensor() -> Any
```
Convert circuit to tensor representation.
### `without_annotations`
```python theme={null}
without_annotations() -> Circuit
```
Return a copy of the circuit with all detector and observable annotations removed.
### `without_noise`
```python theme={null}
without_noise() -> Circuit
```
Return a copy of the circuit with all noise removed.
## class `CompiledComponent`
```python theme={null}
CompiledComponent()
```
A single compiled connected component of a circuit.
Each component is independent and can be sampled separately. The results
are then combined according to output\_indices.
## class `CompiledDetectorSampler`
```python theme={null}
CompiledDetectorSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples detector and observable outcomes from a quantum circuit.
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None, prepend_observables: bool = False, append_observables: bool = False, separate_observables: bool = False, bit_packed: bool = False, use_detector_reference_sample: bool = False, use_observable_reference_sample: bool = False, postselection_mask: np.ndarray | None = None) -> np.ndarray | tuple[np.ndarray, np.ndarray]
```
Return detector samples from the circuit.
The circuit must define the detectors using DETECTOR instructions. Observables
defined by OBSERVABLE\_INCLUDE instructions can also be included in the results
as honorary detectors.
**Parameters:**
* `shots` (`int`) — The number of times to sample every detector in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
* `separate_observables` (`bool`) — Defaults to False. When set to True, the return value is a (detection\_events, observable\_flips) tuple instead of a flat detection\_events array.
* `prepend_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the start of the results.
* `append_observables` (`bool`) — Defaults to false. When set, observables are included with the detectors and are placed at the end of the results.
* `bit_packed` (`bool`) — Defaults to false. When set, results are bit-packed.
* `use_detector_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with detector outcomes so that results represent deviations from the noiseless baseline. This should only be used when detectors are deterministic. Otherwise, it can unpredictably change the results.
* `use_observable_reference_sample` (`bool`) — Defaults to False. When True, a noiseless reference sample is computed and XORed with observable outcomes so that results represent deviations from the noiseless baseline. This should only be used when observables are deterministic. Otherwise, it can unpredictably change the results.
* `postselection_mask` (`np.ndarray | None`) — Optional boolean array of length `num_detectors`. When set, shots where any masked direct detector fires skip the JAX sampling loop. All `shots` rows are still returned: survivors contain the full sample, while discarded rows retain direct detector columns and fill component columns with `False`. Re-apply the mask to the returned detector columns to recover surviving shots.
**Returns:**
* `np.ndarray | tuple[np.ndarray, np.ndarray]` — A numpy array or tuple of numpy arrays containing the samples.
**Raises:**
* `ValueError` — If `separate_observables` is combined with `prepend_observables` or `append_observables`.
## class `CompiledMeasurementSampler`
```python theme={null}
CompiledMeasurementSampler(circuit: Circuit, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Samples measurement outcomes from a quantum circuit.
Uses sequential decomposition \[0, 1, 2, ..., n] where:
* compiled\_scalar\_graphs\[0]: normalization (0 outputs plugged)
* compiled\_scalar\_graphs\[i]: cumulative probability up to bit i
### `sample`
```python theme={null}
sample(shots: int, batch_size: int | None = None) -> np.ndarray
```
Sample measurement outcomes from the circuit.
**Parameters:**
* `shots` (`int`) — The number of times to sample every measurement in the circuit.
* `batch_size` (`int | None`) — The number of samples to process in each batch. Defaults to None, which automatically chooses a batch size based on available memory. When using a GPU, setting this explicitly can help fully utilize VRAM for maximum performance. NOTE: Changing the batch size will affect reproducibility even with a fixed seed.
**Returns:**
* `np.ndarray` — A numpy array containing the measurement samples.
## class `CompiledProgram`
```python theme={null}
CompiledProgram(components: tuple[CompiledComponent, ...], direct_f_indices: Array, direct_flips: Array, output_order: Array, output_reindex: Array | None, num_outputs: int, num_detectors: int)
```
A fully compiled circuit program ready for sampling.
This is the result of compiling a SamplingGraph and contains everything
needed to sample from the circuit.
## class `CompiledStateProbs`
```python theme={null}
CompiledStateProbs(circuit: Circuit, sample_detectors: bool = False, strategy: DecompositionStrategy = 'cat5', seed: int | None = None)
```
Computes measurement probabilities for a given state.
Uses joint decomposition \[0, n] where:
* compiled\_scalar\_graphs\[0]: normalization (0 outputs plugged)
* compiled\_scalar\_graphs\[1]: full joint probability (all outputs plugged)
### `probability_of`
```python theme={null}
probability_of(state: np.ndarray, batch_size: int) -> np.ndarray
```
Compute probabilities for a batch of error samples given a measurement state.
**Parameters:**
* `state` (`np.ndarray`) — The measurement outcome state to compute probability for.
* `batch_size` (`int`) — Number of error samples to use for estimation.
**Returns:**
* `np.ndarray` — Array of probabilities P(state | error\_sample) for each error sample.
## `compile_program`
```python theme={null}
compile_program(prepared: SamplingGraph, mode: DecompositionMode, strategy: DecompositionStrategy = 'cat5') -> CompiledProgram
```
Compile a prepared graph into an executable sampling program.
This function performs the second phase of compilation:
1. Split the graph into connected components
2. For each component:
* Plug outputs according to mode (sequential or joint)
* Reduce each plugged graph
* Perform stabilizer rank decomposition
* Compile into CompiledScalarGraphs objects
3. Assemble into CompiledProgram with output ordering
**Parameters:**
* `prepared` (`SamplingGraph`) — The prepared graph from prepare\_graph().
* `mode` (`DecompositionMode`) — Decomposition mode: - "sequential": For sampling - creates \[0, 1, 2, ..., n] circuits - "joint": For probability estimation - creates \[0, n] circuits
* `strategy` (`DecompositionStrategy`) — Stabilizer rank decomposition strategy. Must be one of "cat5", "bss", "cutting".
**Returns:**
* `CompiledProgram` — A CompiledProgram ready for sampling.
## `copy_d2h`
```python theme={null}
copy_d2h(src, dst: np.ndarray | None = None) -> np.ndarray
```
Device-to-host copy, pinned-destination fast path when available.
**Parameters:**
* `src` — Single-device contiguous array-like exposing `unsafe_buffer_pointer()`, `nbytes`, `shape`, and `dtype`. The caller must sync to the source's stream before invocation (`jax.block_until_ready(src)` for a jax.Array).
* `dst` (`np.ndarray | None`) — Optional pre-allocated pinned ndarray to write into. Must have at least `src.nbytes` bytes.
**Returns:**
* `np.ndarray` — ndarray with the same shape and dtype as `src`.
## `evaluate`
```python theme={null}
evaluate(circuit: CompiledScalarGraphs, param_vals: Array) -> Array
```
Evaluate compiled circuit with batched parameter values.
Each term family (`NodePhases`, `HalfPiPhases`, `PiProducts`,
`PhasePairs`) computes its own contribution via `.evaluate(param_vals)`.
This function multiplies those together with the per-graph
`ScalarPrefactor` and folds in `power2` / any approximate floatfactor.
**Parameters:**
* `circuit` (`CompiledScalarGraphs`) — Compiled circuit representation.
* `param_vals` (`Array`) — Binary parameter values (error bits + measurement/detector outcomes), shape `(batch_size, n_params)`.
**Returns:**
* `Array` — Complex array of shape `(batch_size,)` — the per-sample amplitude.
## `prepare_graph`
```python theme={null}
prepare_graph(circuit: Circuit, sample_detectors: bool) -> SamplingGraph
```
Prepare a circuit for compilation.
This function performs the graph preparation phase:
1. Parse the stim circuit into a ZX graph
2. Build the sampling graph (compose with adjoint, add outputs)
3. Reduce the graph via zx.full\_reduce
4. Transform error basis via Gaussian elimination (e → f)
5. Clear the scalar (safe before stabilizer rank decomposition)
**Parameters:**
* `circuit` (`Circuit`) — The quantum circuit to prepare.
* `sample_detectors` (`bool`) — If True, prepare for detector sampling. If False, prepare for measurement sampling.
**Returns:**
* `SamplingGraph` — A SamplingGraph containing the reduced graph and error transformation.
## `sample_component`
```python theme={null}
sample_component(component: CompiledComponent, f_params: jax.Array, key: PRNGKey) -> tuple[jax.Array, PRNGKey, jax.Array]
```
Sample outputs from a single component using autoregressive sampling.
**Parameters:**
* `component` (`CompiledComponent`) — The compiled component to sample from.
* `f_params` (`jax.Array`) — Error parameters, shape (batch\_size, num\_f\_params).
* `key` (`PRNGKey`) — JAX random key.
**Returns:**
* `jax.Array` — Tuple of (samples, next\_key, max\_norm\_deviation) where samples has shape
* `PRNGKey` — (batch\_size, num\_outputs\_for\_component).
## `sample_program`
```python theme={null}
sample_program(program: CompiledProgram, f_params: jax.Array, key: PRNGKey) -> jax.Array
```
Sample all outputs from a compiled program.
**Parameters:**
* `program` (`CompiledProgram`) — The compiled program to sample from.
* `f_params` (`jax.Array`) — Error parameters, shape (batch\_size, num\_f\_params).
* `key` (`PRNGKey`) — JAX random key.
**Returns:**
* `jax.Array` — Samples array of shape (batch\_size, num\_outputs), reordered to
* `jax.Array` — match the original output indices.
# clifford
Source: https://bloqade.quera.com/tsim/api-reference/utils/clifford
Mapping tables for converting parametric rotations with half-pi angles to Clifford gates.
## `expand_clifford_rotations`
```python theme={null}
expand_clifford_rotations(source: stim.Circuit) -> stim.Circuit
```
Return `source` with half-π parametric rotations expanded to Clifford gates.
`REPEAT` blocks are preserved structurally and expanded recursively.
## `is_clifford`
```python theme={null}
is_clifford(source: stim.Circuit) -> bool
```
Return True iff every instruction in `source` is Clifford.
Recurses into `REPEAT` block bodies.
## `is_t_tag`
```python theme={null}
is_t_tag(tag: str) -> bool
```
Return whether a Stim tag encodes a tsim T-family gate.
## `parametric_to_clifford_gates`
```python theme={null}
parametric_to_clifford_gates(gate_name: str, params: dict[str, Fraction]) -> list[str] | None
```
Convert a parametric gate with half-π angles to stim Clifford gate names.
**Parameters:**
* `gate_name` (`str`) — One of `"R_X"`, `"R_Y"`, `"R_Z"`, `"U3"`.
* `params` (`dict[str, Fraction]`) — Dict as returned by :func:`~tsim.core.parse.parse_parametric_tag`.
**Returns:**
* `list[str] | None` — Stim gate names in circuit order,
* `list[str] | None` — or `None` when the angles are not half-π multiples.
## `parse_parametric_tag`
```python theme={null}
parse_parametric_tag(instruction: stim.CircuitInstruction) -> tuple[str, dict[str, Fraction]] | None
```
Parse the parametric tag on an instruction (e.g. `I[R_Z(theta=0.3*pi)]`).
Supports gates: R\_Z, R\_X, R\_Y, U3.
**Parameters:**
* `instruction` (`stim.CircuitInstruction`) — The stim instruction whose tag will be parsed.
**Returns:**
* `tuple[str, dict[str, Fraction]] | None` — Tuple of (gate\_name, params\_dict) when the instruction's tag is a
* `tuple[str, dict[str, Fraction]] | None` — well-formed parametric tag, or `None` when the tag is not
* `tuple[str, dict[str, Fraction]] | None` — parametric-looking (no `name(...)` shape, or empty).
**Raises:**
* `ValueError` — When the tag looks parametric (matches `name(...)`) but is malformed: a parameter value does not parse, the gate name is unknown, or the parameter keys do not match the expected set for the gate.
# cuda_helpers
Source: https://bloqade.quera.com/tsim/api-reference/utils/cuda_helpers
CUDA-runtime helpers used by the sampler hot path.
Lazy-imports `cuda.bindings`. When the import succeeds, host-pinned
allocations + a direct `cudaMemcpy` replace numpy's default pageable d2h:
the pageable path forces the driver to stage the transfer through internal
pinned scratch before copying into the user buffer (host-DRAM-bandwidth
bound), while a pinned destination skips the staging hop and lets d2h reach
PCIe line rate. When the import fails, `copy_d2h` transparently falls
back to `numpy.array`.
## `alloc_pinned_numpy`
```python theme={null}
alloc_pinned_numpy(nbytes: int, dtype, shape) -> np.ndarray
```
Allocate a pinned host region and return it as an ndarray view.
The returned array's `base` chain pins the underlying `_PinnedBuf`
alive until the array and all derived views are dropped; only then does
`cudaFreeHost` run.
**Parameters:**
* `nbytes` (`int`) — Size of the underlying allocation in bytes. Must be at least `prod(shape) * dtype.itemsize`.
* `dtype` — numpy-compatible dtype for the returned view.
* `shape` — Shape of the returned view.
**Returns:**
* `np.ndarray` — ndarray of the requested shape and dtype, backed by pinned memory.
**Raises:**
* `RuntimeError` — if cuda.bindings is unavailable, or the underlying `cudaHostAlloc` fails.
## `copy_d2h`
```python theme={null}
copy_d2h(src, dst: np.ndarray | None = None) -> np.ndarray
```
Device-to-host copy, pinned-destination fast path when available.
**Parameters:**
* `src` — Single-device contiguous array-like exposing `unsafe_buffer_pointer()`, `nbytes`, `shape`, and `dtype`. The caller must sync to the source's stream before invocation (`jax.block_until_ready(src)` for a jax.Array).
* `dst` (`np.ndarray | None`) — Optional pre-allocated pinned ndarray to write into. Must have at least `src.nbytes` bytes.
**Returns:**
* `np.ndarray` — ndarray with the same shape and dtype as `src`.
# diagram
Source: https://bloqade.quera.com/tsim/api-reference/utils/diagram
SVG diagram rendering for quantum circuits.
## class `Diagram`
```python theme={null}
Diagram(svg: str, html: str)
```
Wrapper for SVG diagram with Jupyter notebook display support.
## class `GateLabel`
```python theme={null}
GateLabel(label: str, annotation: str | None = None)
```
Label for a gate in the SVG diagram.
## `is_t_tag`
```python theme={null}
is_t_tag(tag: str) -> bool
```
Return whether a Stim tag encodes a tsim T-family gate.
## `parse_stim_circuit`
```python theme={null}
parse_stim_circuit(stim_circuit: stim.Circuit, track_classical_wires: bool = False) -> GraphRepresentation
```
Parse a stim circuit into a GraphRepresentation.
**Parameters:**
* `stim_circuit` (`stim.Circuit`) — The stim circuit to convert.
* `track_classical_wires` (`bool`) — Whether to track classical wires.
**Returns:**
* `GraphRepresentation` — A GraphRepresentation containing the ZX graph and all auxiliary data.
## `placeholders_to_t`
```python theme={null}
placeholders_to_t(svg_string: str, placeholder_id_to_labels: dict[float, GateLabel]) -> str
```
Replace I\_ERROR placeholder gates in an SVG diagram with actual gate names.
Supported gates are T, T†, R\_Z, R\_X, R\_Y, U\_3.
**Parameters:**
* `svg_string` (`str`) — The SVG string from stim's diagram() method containing I\_ERROR placeholder gates whose p-value are used as identifiers.
* `placeholder_id_to_labels` (`dict[float, GateLabel]`) — Mapping from identifier (float), i.e. the p values of I\_ERROR gates, to GateLabel.
**Returns:**
* `str` — Modified SVG string with I\_ERROR gates replaced by the actual gate names.
## `render_pyzx_d3`
```python theme={null}
render_pyzx_d3(stim_circ: stim.Circuit, kwargs: dict[str, Any]) -> GraphS
```
Render a stim circuit as a pyzx ZX diagram using d3.js.
**Parameters:**
* `stim_circ` (`stim.Circuit`) — The stim circuit to render.
* `kwargs` (`dict[str, Any]`) — Additional keyword arguments passed to the underlying diagram renderer.
**Returns:**
* `GraphS` — A pyzx ZX diagram.
## `render_svg`
```python theme={null}
render_svg(c: stim.Circuit, type: str, tick: int | range | None = None, filter_coords: Iterable[Iterable[float] | stim.DemTarget] = ((),), rows: int | None = None, width: float | None = None, height: float | None = None, zoomable: bool = True) -> Diagram
```
Render a stim circuit timeline/timeslice diagram with custom labels.
## `scale_horizontally`
```python theme={null}
scale_horizontally(g: BaseGraph, scale: float) -> None
```
Scale horizontal positions of graph vertices by a factor of `scale`.
**Parameters:**
* `g` (`BaseGraph`) — A ZX graph
* `scale` (`float`) — The factor to scale the graph by
## `tagged_gates_to_placeholder`
```python theme={null}
tagged_gates_to_placeholder(circuit: stim.Circuit) -> tuple[stim.Circuit, dict[float, GateLabel]]
```
Replace tagged gates with I\_ERROR placeholder gates for rendering.
Converts S\[T], S\_DAG\[T], I\[R\_X(...)], I\[R\_Y(...)], I\[R\_Z(...)], I\[U3(...)]
to I\_ERROR placeholder gates whose p-values are used as identifiers.
**Parameters:**
* `circuit` (`stim.Circuit`) — The stim circuit to replace tagged gates with I\_ERROR placeholder gates.
**Returns:**
* `stim.Circuit` — A tuple containing the modified circuit and a dictionary mapping the p-values
* `dict[float, GateLabel]` — of the I\_ERROR placeholder gates to the actual gate names.
## `wrap_svg`
```python theme={null}
wrap_svg(svg: str, width: float | None = None, height: float | None = None) -> str
```
Wrap an SVG string in a container div.
**Parameters:**
* `svg` (`str`) — Raw SVG markup.
* `width` (`float | None`) — Width of the container in pixels.
* `height` (`float | None`) — Height of the container in pixels (unused, kept for API symmetry with :func:`wrap_svg_zoomable`).
## `wrap_svg_zoomable`
```python theme={null}
wrap_svg_zoomable(svg: str, width: float | None = None, height: float = 700) -> str
```
Wrap an SVG in a zoomable, scrollable container.
The container has the given pixel dimensions. When *width* is `None`
the container fills the available width. Users can pan by scrolling
and zoom with pinch-zoom (trackpad) or Ctrl/Cmd + wheel. Zoom is
anchored at the cursor position.
**Parameters:**
* `svg` (`str`) — Raw SVG markup.
* `width` (`float | None`) — Pixel width of the container, or `None` for full width.
* `height` (`float`) — Pixel height of the container.
# encoder
Source: https://bloqade.quera.com/tsim/api-reference/utils/encoder
Transversal encoder utilities for QEC code experiments.
## class `ColorEncoder5`
```python theme={null}
ColorEncoder5()
```
Transversal encoder for the \[\[17,1,5]] 2D color code.
## class `SteaneEncoder`
```python theme={null}
SteaneEncoder()
```
Transversal encoder for the \[\[7,1,3]] Steane code.
## class `TransversalEncoder`
```python theme={null}
TransversalEncoder(n: int, encoding_qubit: int, encoding_program_text: str | None, stabilizer_generators: list[list[int]], observables: list[list[int]])
```
Base class for transversal quantum error correction encoders.
### `diagram`
```python theme={null}
diagram(kwargs = {})
```
Return a timeline-svg diagram of the encoded circuit.
### `encode_transversally`
```python theme={null}
encode_transversally(program_text: str) -> None
```
Encode a program transversally by replacing physicalgates with transversal gates.
Transform a program on m qubits into a program on n \* m qubits (consisting of n code blocks).
**Parameters:**
* `program_text` (`str`) — The program to encode transversally.
### `encoding_flow_generators`
```python theme={null}
encoding_flow_generators()
```
Return the Pauli flow generators for the encoding circuit.
### `initialize`
```python theme={null}
initialize(program_text: str, encoding_program_text: str | None = None) -> None
```
Initialize state preparation and apply encoding circuit.
Apply the state preparation program for k qubits, then apply an encoding
circuit to encode the state into n qubits.
**Parameters:**
* `program_text` (`str`) — The state preparation program for k qubits. Generally, this should be a simple program that prepares each of the k qubits in a single-qubit state.
* `encoding_program_text` (`optional`) — An encoding circuit for a single logical qubit. This should encode a single logical qubit at input `self.encoding_qubit` into a state of n qubits. If not provided, the encoder will use a noiseless default encoding.
## `broadcast_targets`
```python theme={null}
broadcast_targets(groups: list[list[stim.GateTarget]], stride: int, offsets: list[int]) -> list[int]
```
Broadcast gate target groups with a stride and set of offsets.
# linalg
Source: https://bloqade.quera.com/tsim/api-reference/utils/linalg
Linear algebra utilities for GF(2) operations.
## `find_basis`
```python theme={null}
find_basis(vectors: np.ndarray) -> tuple[np.ndarray, np.ndarray]
```
Decompose a set of binary vectors into a basis subset and a transformation matrix over GF(2).
Given a set of vectors V, this function finds a maximal linearly independent subset B
(the basis) and computes a transformation matrix T such that the original vectors can be
reconstructed from the basis via matrix multiplication over GF(2):
V = T @ B (mod 2)
**Parameters:**
* `vectors` (`np.ndarray`) — Input binary vectors of shape `(N, D)`. Can be a list of lists or a numpy array. Elements should be 0 or 1 (or convertible to them).
**Returns:**
* `tuple[np.ndarray, np.ndarray]` — A tuple `(basis, transform)` where: basis: The subset of independent vectors, shape `(K, D)`, where `K` is the rank. transform: The transformation matrix, shape `(N, K)`.
## `matmul_gf2`
```python theme={null}
matmul_gf2(a: Array, b: Array) -> Array
```
Compute binary dot products mod 2 as `a_GTP x b_BP -\> b_BGT`.
Uses float32 matmul (integer matmul does not have BLAS support on CPU)
then casts back to uint8.
**Parameters:**
* `a` (`Array`) — Parameter bit-masks, shape `(G, T, P)` — G graphs, T terms, P parameters.
* `b` (`Array`) — Binary parameter values, shape `(B, P)` — B batch elements.
**Returns:**
* `Array` — Binary row-sums mod 2, shape `(B, G, T)`.
# program_text
Source: https://bloqade.quera.com/tsim/api-reference/utils/program_text
Conversion utilities between tsim shorthand and stim program text.
## `controlled_gate_decomposition_lines`
```python theme={null}
controlled_gate_decomposition_lines(gate: str, control1: int | str, control2: int | str, target: int | str, tag: str = '') -> list[str]
```
Return a Clifford+T decomposition for a controlled-controlled gate.
## `decode_t_user_tag`
```python theme={null}
decode_t_user_tag(tag: str) -> str
```
Return the user tag attached to an encoded T-family gate tag.
## `encode_t_tag`
```python theme={null}
encode_t_tag(user_tag: str = '') -> str
```
Encode a T-family gate tag while preserving an optional user tag.
## `enriched_stim_error`
```python theme={null}
enriched_stim_error(exc: ValueError, converted_text: str) -> ValueError
```
Improve stim parse errors for tsim-specific gates.
When stim raises a 'Gate not found' error for a gate that should have been
converted by shorthand\_to\_stim, this searches the converted text for the
unconverted usage and returns a more helpful error message.
## `shorthand_to_stim`
```python theme={null}
shorthand_to_stim(text: str) -> str
```
Convert tsim shorthand syntax to valid stim instructions.
**Converts:**
CCZ 0 1 2 → Clifford+T decomposition of CCZ
CCX 0 1 2 → Clifford+T decomposition of CCX/Toffoli
T 0 1 → S\[T] 0 1
T\_DAG 0 1 → S\_DAG\[T] 0 1
TPP X0*Y1 → SPP\[T] X0*Y1
TPP\_DAG X0*Y1 → SPP\_DAG\[T] X0*Y1
R\_Z(0.3) 0 → I\[R\_Z(theta=0.3*pi)] 0
R\_X(0.25) 0 → I\[R\_X(theta=0.25*pi)] 0
R\_Y(-0.5) 0 → I\[R\_Y(theta=-0.5*pi)] 0
R\_XX(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] X0*X1
R\_YY(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] Y0*Y1
R\_ZZ(0.5) 0 1 → SPP\[R\_PAULI(theta=0.5*pi)] Z0*Z1
R\_PAULI(0.3) X0*Y1 → SPP\[R\_PAULI(theta=0.3*pi)] X0*Y1
U3(0.3, 0.24, 0.49) 0 → I\[U3(theta=0.3*pi, phi=0.24*pi, lambda=0.49\*pi)] 0
## `stim_to_shorthand`
```python theme={null}
stim_to_shorthand(text: str) -> str
```
Convert expanded stim annotations back to tsim shorthand.
Rewrites:
* I\[U3(theta=θ*pi, phi=φ*pi, lambda=λ\*pi)] → U3(θ, φ, λ)
* I\[R\_X(theta=α\*pi)] / I\[R\_Y(...)] / I\[R\_Z(...)] → R\_X(α) / R\_Y(α) / R\_Z(α)
* SPP\[R\_PAULI(theta=α*pi)] P0*P1 → R\_PP(α) 0 1 (P ∈ \{X, Y, Z})
* SPP\[R\_PAULI(theta=α*pi)] X0*Y1 → R\_PAULI(α) X0\*Y1
* SPP\[T] → TPP
* SPP\_DAG\[T] → TPP\_DAG
* S\[T] → T
* S\_DAG\[T] → T\_DAG
# Contributing
Source: https://bloqade.quera.com/tsim/contributing
How to set up your development environment, run tests, and contribute to tsim.
Please see [Installation](/tsim/install) for instructions on how to set up your development environment.
## Pre-commit hooks
We use `pre-commit` to run formatting, linter and type checks before you commit your changes. The pre-commit hooks are installed as part of the development dependencies. You can set up `pre-commit` using the following command:
```bash theme={null}
pre-commit install
```
If the checks fail, the commit will be rejected.
## Running the tests
We use `pytest` for testing. To run the tests, simply run:
```bash theme={null}
uv run pytest
```
## Code style
We use `black` for code formatting. Besides the linter requirements, we also require the following good-to-have practices.
### Naming
* try not to use abbreviation as names, unless it's a common abbreviation like `idx` for `index`
* try not create a lot of duplicated name prefix unless the extra information is necessary when accessing the class object.
* use `snake_case` for naming variables and functions, and `CamelCase` for classes.
### Comments
* try not to write comments, unless it's really necessary. The code should be self-explanatory.
* if you have to write comments, try to use `NOTE:`, `TODO:` `FIXME:` tags to make it easier to search for them.
## Documentation
We use `just` for managing command-line tools and scripts. It should be installed when you run `uv sync`. The documentation site is built with [Mintlify](https://mintlify.com/); the local Mintlify CLI is installed separately via npm:
```bash theme={null}
npm i -g mint
```
Once you have `mint` installed, run:
```bash theme={null}
uv run just doc
```
This regenerates the API reference and tutorial MDX files via `docs/build.py`, then launches a local Mintlify preview. Use `uv run just doc-quick` to skip re-executing notebooks during iteration.
# tsim
Source: https://bloqade.quera.com/tsim/index
A GPU-accelerated quantum circuit sampler based on ZX-calculus stabilizer rank decomposition.
A GPU-accelerated quantum circuit sampler based on ZX-calculus stabilizer rank decomposition.
tsim feels just like [Stim](https://github.com/quantumlib/Stim), but supports non-Clifford gates.
A detailed description of tsim is given in [arXiv:2604.01059](https://arxiv.org/abs/2604.01059).
## Quick start
An introductory tutorial is available [here](/tsim/tutorials/encoding_demo). For many existing scripts, replacing `stim` with `tsim` should just work. tsim mirrors the Stim API and supports all [Stim instructions](https://github.com/quantumlib/Stim/wiki/Stim-v1.13-Gate-Reference).
Additionally, tsim supports the instructions `T`, `T_DAG`, `R_Z`, `R_X`, `R_Y`, `U3`, `TPP`, `TPP_DAG`, `R_XX`, `R_YY`, `R_ZZ`, `R_PAULI`, `CCZ`, and `CCX`.
```python theme={null}
import tsim
c = tsim.Circuit(
"""
RX 0
R 1
T 0
PAULI_CHANNEL_1(0.1, 0.1, 0.2) 0 1
H 0
CNOT 0 1
DEPOLARIZE2(0.01) 0 1
M 0 1
DETECTOR rec[-1] rec[-2]
"""
)
detector_sampler = c.compile_detector_sampler()
samples = detector_sampler.sample(shots=100)
```
## Installation
```bash theme={null}
uv add bloqade-tsim
```
For GPU acceleration, use
```bash theme={null}
uv add "bloqade-tsim[cuda13]"
```
See [Installation](/tsim/install) for more options.
## Architecture
Quantum programs are translated into ZX diagrams in which Pauli noise channels appear as parameterized vertices with binary variables $e_i$.
ZX simplification factors the diagram into a classical part that represents the Tanner graph and a quantum part containing the observable circuit. Both parts define a new basis of *error mechanisms* $f_i = \bigoplus_j T_{ij}\,e_j$.
The observable diagram is used to compute marginal probabilities for autoregressive sampling. Here, each diagram is decomposed into a sum of Clifford terms via stabilizer rank decomposition, following [Sutcliffe and Kissinger (2024)](https://arxiv.org/abs/2403.06777), and compiled into binary JAX tensors $g_{tki}$.
At sampling time, JIT-compiled XLA kernels contract $g_{tki}$ with batched noise configurations $f_i^{s}$ to evaluate marginal probabilities and autoregressively sample detector and observable bits.
## Differences from Stim
tsim supports non-deterministic detectors and observables. An important consequence is that
tsim will simulate actual detector samples, whereas Stim only reports detection flips (i.e. detection samples XORed with
a noiseless reference sample). Concretely,
```python theme={null}
c = tsim.Circuit(
"""
X 0
M 0
DETECTOR rec[-1]
"""
)
sampler = c.compile_detector_sampler()
samples = sampler.sample(shots=100)
print(samples)
```
will report `True` values, whereas the same circuit would result in `False` values in Stim. To reproduce the behavior of Stim, you can use the following:
```python theme={null}
samples = sampler.sample(
shots=100,
use_detector_reference_sample=True,
use_observable_reference_sample=True,
)
```
When set to `True`, a noiseless reference sample is computed and XORed with the
results, so that output values represent deviations from the noiseless baseline.
Note that this feature should be used carefully. If detectors or observables are not deterministic, this may lead to incorrect statistics.
## Postselected simulations
For postselected QEC experiments, pass a boolean mask to
`CompiledDetectorSampler.sample`. The mask has length `num_detectors`; a shot is
*discarded* when any masked detector fires.
```python theme={null}
import numpy as np
c = tsim.Circuit(
"""
X_ERROR(0.01) 0 1
M 0 1
DETECTOR rec[-2]
DETECTOR rec[-1] rec[-2]
OBSERVABLE_INCLUDE(0) rec[-1]
"""
)
sampler = c.compile_detector_sampler()
mask = np.array([True, False]) # postselect on detector 0
samples = sampler.sample(
shots=10_000,
postselection_mask=mask,
append_observables=True,
)
keep = ~np.any(samples[:, : c.num_detectors] & mask, axis=1)
survivors = samples[keep]
```
`sample` always returns exactly `shots` rows. Shots discarded by a **direct**
postselected detector skip the expensive JAX autoregressive loop; their direct
detector columns are still correct, and all other columns are filled with
`False`. Re-apply the mask to the detector columns (as above) to recover the
surviving shots. Detectors that live inside a JAX component cannot be evaluated
without running JAX, so those shots are always computed in full.
This is independent of `prepend_observables`, `append_observables`,
`separate_observables`, and `bit_packed`. When combined with
`use_detector_reference_sample`, the reference XOR is applied before the
postselection discard check. On surviving rows it is applied to every detector
column; on direct-discarded partial rows it is applied only to direct detector
columns (component columns stay `False`). When combined with
`use_observable_reference_sample`, the reference XOR is applied to every row
that ran JAX; direct-discarded partial rows are left unchanged.
## Benchmarks
With GPU acceleration, tsim can achieve sampling throughput for low-magic circuits that approaches the throughput of Stim on Clifford circuits of the same size. The figure below shows a comparison for [distillation circuits](https://arxiv.org/html/2412.15165v1) (35 and 85 qubits), [cultivation circuits](https://arxiv.org/abs/2409.17595), and rotated surface code circuits.
tsim can be five orders of magnitude faster than [quizx](https://github.com/zxcalc/quizx).
## Citing tsim
If you use tsim, please consider citing the paper describing the core simulation approach:
```
@article{tsim2026,
title={Tsim: Fast Universal Simulator for Quantum Error Correction},
author={Haenel, Rafael and Luo, Xiuzhe and Zhao, Chen},
journal={arXiv preprint arXiv:2604.01059},
year={2026}
}
```
# Installation
Source: https://bloqade.quera.com/tsim/install
Install tsim with uv or pip, with optional CUDA support.
tsim requires Python 3.10 or later.
## Using uv (recommended)
We recommend using [`uv`](https://docs.astral.sh/uv/) for dependency management.
```bash theme={null}
uv add bloqade-tsim
```
For GPU acceleration with CUDA:
```bash theme={null}
# For CUDA 13
uv add "bloqade-tsim[cuda13]"
# For CUDA 12
uv add "bloqade-tsim[cuda12]"
```
## Using pip
```bash theme={null}
pip install bloqade-tsim
```
For GPU acceleration with CUDA:
```bash theme={null}
pip install "bloqade-tsim[cuda13]"
```
## Development setup
If you're contributing to tsim, clone the repository and install development dependencies:
```bash theme={null}
git clone https://github.com/QuEraComputing/tsim.git
cd tsim
uv sync
```
Install pre-commit hooks to run linting checks automatically:
```bash theme={null}
pre-commit install
```
This will run formatters and linters (black, isort, ruff, pyright) before each commit.
# Quantum State Encoding with a Color Code
Source: https://bloqade.quera.com/tsim/tutorials/encoding_demo
```python theme={null}
from tsim import Circuit
import sinter
import numpy as np
import matplotlib.pyplot as plt
from tesseract_decoder import tesseract, TesseractSinterDecoder
from utils.no_decoder import NoDecoder
```
This tutorial showcases the basic functionality of **Tsim**.
tsim is a circuit sampler for Clifford+T circuits, based on stabilizer rank decomposition and ZX-calculus techniques. It closely follows the API of stim and directly uses stim's circuit format.
In contrast to Stim, Tsim supports `T` and `T_DAG` instructions.
The following circuit demonstrates this by preparing and measuring the state
$$
H\,T\,|+\rangle
= e^{i\pi/8}\Big[\cos\!\left(\tfrac{\pi}{8}\right)\,|0\rangle
\;-\; i\,\sin\!\left(\tfrac{\pi}{8}\right)\,|1\rangle\Big].
$$
```python theme={null}
c = Circuit("""
RX 0
T 0
H 0
M 0
""")
c.diagram("timeline-svg", height=150)
```
To sample from this circuit, we first compile it into a sampler:
```python theme={null}
sampler = c.compile_sampler()
```
We can now sample bitstrings from the measurement instructions:
```python theme={null}
sampler.sample(shots=10)
```
```text title="Output" theme={null}
array([[False],
[False],
[False],
[False],
[False],
[False],
[False],
[False],
[False],
[False]])
```
Let's run a large number of shots to estimate the probability of measuring `1`.
```python theme={null}
samples = sampler.sample(shots=10_000_000, batch_size=1_000_000)
int(np.count_nonzero(samples)) / len(samples)
```
```text title="Output" theme={null}
0.1463239
```
As expected, the probability is close to $\sin(\pi/8)^2 \approx 0.1464$.
## Detectors and Observables
Next, we consider a more complex example: an encoding circuit for the \[\[7,1,3]] Steane code. This circuit prepares the logical state
$\frac{1}{2}\Big[(1 + e^{i\pi/4})|\bar{0}\rangle + (1 - e^{i\pi/4})|\bar{1}\rangle\Big]$
```python theme={null}
c = Circuit("""
RX 6
T 6
H 6
R 0 1 2 3 4 5
SQRT_Y_DAG 0 1 2 3 4 5
CZ 1 2 3 4 5 6
SQRT_Y 6
CZ 0 3 2 5 4 6
SQRT_Y 2 3 4 5 6
CZ 0 1 2 3 4 5
SQRT_Y 1 2 4
X 3
TICK
M 0 1 2 3 4 5 6
DETECTOR rec[-7] rec[-6] rec[-5] rec[-4]
DETECTOR rec[-6] rec[-5] rec[-3] rec[-2]
DETECTOR rec[-5] rec[-4] rec[-3] rec[-1]
OBSERVABLE_INCLUDE(0) rec[-7] rec[-6] rec[-2]
""")
```
tsim supports multiple visualization methods. The default makes use of `stim`'s "timeline-svg" visualization function:
```python theme={null}
c.diagram(height=350)
```
Tsim also supports visualization as a ZX diagram, where measurement vertices are annotated with `rec[i]`, and detector and observable vertices are annotated with `det[i]` and `obs[i]`, respectively.
```python theme={null}
c.diagram("pyzx");
```
To sample detection events and logical observables, we can compile a detector sampler, similar to `stim`.
```python theme={null}
det_sampler = c.compile_detector_sampler(seed=1)
```
```python theme={null}
det_samples, obs_samples = det_sampler.sample(shots=100_000, separate_observables=True)
print(det_samples[:5])
print(obs_samples[:5])
```
```text title="Output" theme={null}
[[False False False]
[False False False]
[False False False]
[False False False]
[False False False]]
[[False]
[False]
[False]
[ True]
[False]]
```
Since the circuit is just a logical encoding of the 1-qubit circuit from the beginning of the tutorial, the logical observable should behave exactly like the physical qubit in the first example, i.e., the observable should be 1 with probability $\sin(\pi/8)^2 \approx 0.1464$.
Additionally, since the circuit is noiseless, we should not observe any non-zero detection events:
```python theme={null}
assert np.count_nonzero(det_samples) == 0
int(np.count_nonzero(obs_samples)) / len(obs_samples)
```
```text title="Output" theme={null}
0.14618
```
## Adding Noise
A core capability of tsim is its support for Pauli noise channels.
Let's look at a simple example. We'll insert a depolarizing channel `DEPOLARIZE1(0.01)` before the final stabilizer measurements.
```python theme={null}
def make_circuit(p):
return Circuit(f"""
RX 6
T 6
H 6
R 0 1 2 3 4 5
TICK
SQRT_Y_DAG 0 1 2 3 4 5
DEPOLARIZE1({p}) 0 1
TICK
CZ 1 2 3 4 5 6
DEPOLARIZE2({p}) 1 2
TICK
SQRT_Y 6
DEPOLARIZE1({p}) 6
TICK
CZ 0 3 2 5 4 6
TICK
SQRT_Y 2 3 4 5 6
DEPOLARIZE1({p}) 2 4 6
TICK
CZ 0 1 2 3 4 5
TICK
DEPOLARIZE1({p}) 0 1 2 3 4 5 6
SQRT_Y 1 2 4
X 3
TICK
M 0 1 2 3 4 5 6
DETECTOR rec[-7] rec[-6] rec[-5] rec[-4]
DETECTOR rec[-6] rec[-5] rec[-3] rec[-2]
DETECTOR rec[-5] rec[-4] rec[-3] rec[-1]
OBSERVABLE_INCLUDE(0) rec[-7] rec[-6] rec[-2]
""")
c = make_circuit(0.01)
```
```python theme={null}
c.diagram("timeline-svg", height=350)
```
In the ZX diagram, the noise is represented by parametrized vertices with binary parameters `e0`, `e1`, etc. Since a depolarizing channel either applies X, Y, Z gates, each channel requires two bits, i.e. an X and a Z vertex.
```python theme={null}
c.diagram("pyzx");
```
Now we compile the sampler for the noisy circuit.
```python theme={null}
det_sampler = c.compile_detector_sampler()
```
Sampling from the noisy circuit, we expect to see some non-zero detector events.
```python theme={null}
det_samples, obs_samples = det_sampler.sample(shots=10_000, separate_observables=True)
print(det_samples[:5], "\nTriggered detection events:", np.count_nonzero(det_samples))
print(obs_samples[:5])
```
```text title="Output" theme={null}
[[False False False]
[False False False]
[False False False]
[False False False]
[False False False]]
Triggered detection events: 1478
[[False]
[False]
[False]
[False]
[False]]
```
We again calculate the probability of measuring a logical $|\bar{1}\rangle$. Due to the noise, it deviates from the ideal value of $\sin(\pi/8)^2 \approx 0.1464$.
```python theme={null}
int(np.count_nonzero(obs_samples)) / len(obs_samples)
```
```text title="Output" theme={null}
0.1686
```
## Error detection
One simple error correction strategy is **post-selection**: we discard any shots where a detector fired (indicating an error occurred). This effectively projects us back to the code space, but reduces the success rate (yield).
```python theme={null}
perfect_stabilizers = np.all(det_samples == 0, axis=1)
post_selected_obs = obs_samples[perfect_stabilizers]
int(np.count_nonzero(post_selected_obs)) / len(post_selected_obs)
```
```text title="Output" theme={null}
0.1451806571335007
```
## Error correction
To actively correct errors, we need a **decoder**. A decoder takes the detector syndrome and predicts whether the observable should be flipped.
In this example, we will use the [tesseract](https://github.com/quantumlib/tesseract-decoder) decoder.
After correction, we see that the the probability of getting a logical $|\bar{1}\rangle$ is close to the ideal value.
```python theme={null}
c.detector_error_model()
```
```text title="Output" theme={null}
stim.DetectorErrorModel('''
error(0.0132444) D0 D1 D2
error(0.0184365) D0 D1 L0
error(0.00666667) D0 D2
error(0.00666667) D0 L0
error(0.0132444) D1 D2
error(0.00666667) D1 L0
error(0.0197345) D2
''')
```
```python theme={null}
config = tesseract.TesseractConfig(dem=c.detector_error_model())
decoder = config.compile_decoder()
obs_corrected = np.zeros_like(obs_samples)
for i, det_sample in enumerate(det_samples):
flip_obs = decoder.decode(det_sample)
obs_corrected[i] = np.logical_xor(obs_samples[i], flip_obs[0])
print("Raw obs.: ", int(np.count_nonzero(obs_samples)) / len(obs_samples))
print("Corrected:", int(np.count_nonzero(obs_corrected)) / len(obs_corrected))
```
```text title="Output" theme={null}
Raw obs.: 0.1686
Corrected: 0.1463
```
## Monte Carlo Simulation with sinter
tsim is compatible with sinter, a tool for performing large Monte Carlo simulations. We can use sinter to sample and decode over a range of physical error rates.
```python theme={null}
tesseract_dec = TesseractSinterDecoder()
no_dec = NoDecoder()
tasks = [
sinter.Task(
circuit=make_circuit(noise).cast_to_stim(),
json_metadata={"p": noise},
)
for noise in np.logspace(-3.3, -0.2, 6)
]
collected_stats = sinter.collect(
num_workers=1,
tasks=tasks,
decoders=["tesseract", "no decoding"],
max_shots=1024 * 64,
max_errors=1024 * 64,
custom_decoders={"tesseract": tesseract_dec, "no decoding": NoDecoder()},
start_batch_size=1024 * 64,
max_batch_size=1024 * 64,
)
```
sinter provides a number of convenient plotting tools. Here, we use them to plot the observable flip rate as a function of the physical error rate. We observe that the decoded probability approaches the expected value of $\sin(\pi/8)^2$ much faster.
```python theme={null}
fig, ax = plt.subplots(1, 1)
sinter.plot_error_rate(
ax=ax,
stats=collected_stats,
x_func=lambda stats: stats.json_metadata["p"],
group_func=lambda stats: stats.decoder,
)
ax.loglog()
ax.set_xlabel("Physical Error Rate")
ax.set_ylabel(f"Probability of logical $|\\bar{1}\\rangle$")
ax.axhline(np.sin(np.pi / 8) ** 2, color="k", linestyle="--", lw=0.4)
ax.text(0.1, np.sin(np.pi / 8) ** 2 * 1.01, "$\\sin(\\pi/8)^2$", fontsize=10)
ax.legend();
```
# From Stim to Tsim
Source: https://bloqade.quera.com/tsim/tutorials/from_stim_to_tsim
```python theme={null}
import tsim
import stim
```
If you have been using Stim, using Tsim should feel very similar. In many cases, replacing `stim` with `tsim` should just work.
If you're not yet familiar with Stim, there are lots of resources out there: For example, [Stim's getting\_started notebook](https://github.com/quantumlib/Stim/blob/main/doc/getting_started.ipynb), [Craig Gidney's Stim tutorial on YouTube](https://www.youtube.com/watch?v=sprDaLxPVNI), [Google's course on QEC](https://www.coursera.org/learn/quantum-error-correction#modules), or [this Stim 101 tutorial](https://www.youtube.com/watch?v=LiT6NDQcOm0).
### Non-Clifford instructions
Tsim extends the Stim text format with the non-Clifford instructions `T`, `T_DAG`, `R_X`, `R_Y`, and `R_Z`, `U3`. Rotation angles are specified in units of π.
```python theme={null}
c = tsim.Circuit("""
T 0
T_DAG 0
R_X(0.1) 0
R_Y(0.2) 0
R_Z(0.3) 0
U3(0.1, 0.2, 0.3) 0
""")
c.diagram(height=150)
```
### Detector Reference Samples
There are a few differences between the two packages. For example, Stim's detector sampler will output results relative to a noiseless reference sample. For example, in the following circuit, detector and observable bits will always be `False`:
```python theme={null}
circuit_str = """
X 0
M 0
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-1]
"""
c = stim.Circuit(circuit_str)
stim_sampler = c.compile_detector_sampler(seed=None)
samples = stim_sampler.sample(shots=5, append_observables=True)
print(samples)
```
```text title="Output" theme={null}
[[False False]
[False False]
[False False]
[False False]
[False False]]
```
By default, Tsim reports actual detector and observable bits. To match Stim's behavior, set `use_detector_reference_sample=True` and `use_observable_reference_sample=True`. This computes a noiseless reference sample and XORs it with the results.
```python theme={null}
c = tsim.Circuit(circuit_str)
tsim_sampler = c.compile_detector_sampler(seed=0)
# Without reference sample (default): actual detector/observable values
samples = tsim_sampler.sample(shots=5, append_observables=True)
print("Without reference sample:")
print(samples)
# With reference sample: matches Stim behavior
samples_ref = tsim_sampler.sample(
shots=5,
append_observables=True,
use_detector_reference_sample=True,
use_observable_reference_sample=True,
)
print("\nWith reference sample:")
print(samples_ref)
```
```text title="Output" theme={null}
Without reference sample:
[[ True True]
[ True True]
[ True True]
[ True True]
[ True True]]
With reference sample:
[[False False]
[False False]
[False False]
[False False]
[False False]]
```
### Batch size and just-in-time compilation
Tsim's samplers also have a `batch_size` argument, which does not exist in Stim. This parameter controls the number of shots sampled in parallel.
When `batch_size` is not specified, it is automatically chosen based on available device memory (GPU VRAM or system RAM). To achieve maximum performance on a GPU, you can also set `batch_size` explicitly.
```python theme={null}
tsim_sampler.sample(shots=1_000_000, batch_size=100_000)
```
```text title="Output" theme={null}
array([[ True],
[ True],
[ True],
...,
[ True],
[ True],
[ True]], shape=(1000000, 1))
```
Tsim uses `jax` just-in-time compilation, which is triggered upon first execution of the `sample` function. This means that subsequent calls to `sample` with the same parameters will be faster. Note that recompilation is triggered whenever the `batch_size` is changed.
### Decomposition strategies
Tsim supports three stabilizer rank decomposition strategies: `"cat5"` (default), `"bss"`, and `"cutting"`. The strategy can be passed to `compile_sampler` or `compile_detector_sampler`:
```python theme={null}
c.compile_detector_sampler(strategy="cutting")
```
```text title="Output" theme={null}
CompiledDetectorSampler(0 direct, 4 graphs, 0 error channel bits, 1 outputs for largest cc, ≤ 1 parameters, 2 A terms, 0 B terms, 0 C terms, 0 D terms, 152 B)
```
Different strategies can yield different performance depending on the circuit structure. If sampling is slow, it can be worth trying an alternative strategy.
### Visualization methods
Tsim supports a subset of Stim's visualization methods. Since Tsim is based on ZX diagrams, it also provides visualization via `pyzx`.
```python theme={null}
c = tsim.Circuit("""
RX 0 1
TICK
T 0
TICK
CNOT 0 1
TICK
U3(0.1, 0.2, 0.3) 1
TICK
M 0 1
""")
c.diagram("timeline-svg", height=150)
```
```python theme={null}
c.diagram("timeslice-svg", height=80, rows=1)
```
```python theme={null}
c.diagram("pyzx");
```
It can be interesting to look at the ZX diagram of more advanced Stim programs. For example, the following program measures a qubit and records the flipped measurement outcome. Based on this outcome, an X gate is applied to a different qubit:
```python theme={null}
tsim.Circuit("""
M !0
CNOT rec[-1] 1
""").diagram("pyzx");
```
### `REPEAT` instructions are flattened
Tsim does not support `REPEAT` instructions. Any `REPEAT` instructions will be flattened.
```python theme={null}
c = tsim.Circuit("""
REPEAT 10 {
T 0
}
""")
c.diagram("pyzx");
```
# Logical magic state distillation
Source: https://bloqade.quera.com/tsim/tutorials/magic_state_distillation
```python theme={null}
import tsim
import numpy as np
from typing import Literal
import matplotlib.pyplot as plt
from tsim.utils.encoder import ColorEncoder5
```
In this tutorial, we will simulate a magic state distillation protocol using Tsim. We will simulate the magic state distillation circuit with 85 qubits introduced in [Rodriguez et al. (2025)](https://www.nature.com/articles/s41586-025-09367-3). This showcases Tsim's ability to simulate large non-Clifford circuits that would be impossible to simulate using a state vector simulator.
### Magic states
We start by preparing a particular magic state, the eigenstate of the "SH" operator. To simulate noise, we add a depolarizing channel after state preparation:
```python theme={null}
theta = -np.arccos(np.sqrt(1 / 3)) / np.pi # Distillation angle
c = tsim.Circuit(f"R 0 \n R_X({theta}) 0 \n T_DAG 0 \n DEPOLARIZE1(0.05) 0 \n")
c.diagram("timeline-svg", height=100)
```
By undoing the state preparation at the end, we can measure the infidelity:
```python theme={null}
p = 0.05
c = tsim.Circuit(f"""
R 0
R_X({theta}) 0
T_DAG 0
DEPOLARIZE1({p}) 0
T 0
R_X({-theta}) 0
M 0
""")
sampler = c.compile_sampler()
samples = sampler.sample(100_000, batch_size=100_000)
print(f"Infidelity: {np.count_nonzero(samples) / len(samples)}")
c.diagram("timeline-svg", height=100)
```
```text title="Output" theme={null}
Infidelity: 0.0325
```
The noisy state preparation process produces a magic state with 97% fidelity.
### Magic state distillation: The ideal case
Given multiple noisy magic states of low fidelity, we can distill a single high-fidelity magic state using a distillation circuit:
```python theme={null}
def make_logical_distillation_circuit(angle: float):
return tsim.Circuit(f"""
# initial state
R 0 1 2 3 4
R_X({angle}) 0 1 2 3 4
T_DAG 0 1 2 3 4
DEPOLARIZE1({p}) 0 1 2 3 4
# distillation
SQRT_X 0 1 4
CZ 0 1 2 3
SQRT_Y 0 3
CZ 0 2 3 4
TICK
SQRT_X_DAG 0
CZ 0 4 1 3
TICK
SQRT_X_DAG 0 1 2 3 4
# undo
T 0
R_X({-angle}) 0
M 0 1 2 3 4
""")
c = make_logical_distillation_circuit(theta)
c.diagram("timeline-svg", height=240)
```
In this distillation circuit, qubits \[q1, q2, q3, q4] are the distillation syndrome.
If we measure them in the state \[1,0,1,1], we know that distillation was successful and the first qubit q0 is in a magic state with higher fidelity.
We can simulate this using Tsim. To measure the fidelity, we undo the magic state preparation on the first qubit and perform a measurement in the Z basis.
```python theme={null}
sampler = c.compile_sampler(seed=0)
samples = sampler.sample(50_000, batch_size=10_000)
def post_select(samples: np.ndarray):
distilled_output = samples[:, 0]
distillation_syndromes = samples[:, 1:]
sel = np.all(distillation_syndromes == np.array([1, 0, 1, 1]), axis=1)
return distilled_output[sel]
post_selected_samples = post_select(samples)
print(
f"Infidelity: {np.count_nonzero(post_selected_samples) / len(post_selected_samples):.5f}"
)
print(
f"Percentage of post-selected samples: {len(post_selected_samples) / len(samples) * 100:.2f}%"
)
```
```text title="Output" theme={null}
Infidelity: 0.00616
Percentage of post-selected samples: 14.28%
```
After distillation, the fidelity has increased to 99.3%. This is much better than the original 97% of the individual input magic states. A downside is that the distillation process is only successful 14% of the time. This means we have to repeat the distillation process multiple times until we get a good output state.
With Tsim, we can study other properties of the distillation circuit. For example, we can analyze the dependence of the fidelity on the rotation angle of the `R_X` gate:
```python theme={null}
angles = np.linspace(-0.4, 0.4, 7) + theta
fidelities = []
post_selection_rate = []
for angle in angles:
c = make_logical_distillation_circuit(angle)
sampler = c.compile_sampler(seed=0)
samples = sampler.sample(4_096, batch_size=4_096)
post_selected_samples = post_select(samples)
post_selection_rate.append(len(post_selected_samples) / len(samples))
f = np.count_nonzero(post_selected_samples) / len(post_selected_samples)
fidelities.append(1 - f)
```
```python theme={null}
fig, ax1 = plt.subplots()
(line1,) = ax1.semilogy(
angles - theta, 1 - np.array(fidelities), "*-", c="g", lw=0.5, label="Infidelity"
)
ax1.axvline(0, linestyle="--", lw=0.5)
ax1.set_xlabel(r"$(\theta - \theta^*)/\pi$")
ax1.set_ylabel("Infidelity")
ax2 = ax1.twinx()
(line2,) = ax2.plot(
angles - theta,
post_selection_rate,
".-",
c="orange",
lw=0.5,
label="Post-selection rate",
)
ax2.set_ylabel("Post-selection rate")
ax2.legend(handles=[line1, line2], loc="lower right");
```
We see that the output fidelity and post-selection rate are peaked at the distillation angle $\theta^*$.
### Magic state distillation: The realistic case (with noise)
On a real quantum computer, the distillation circuit is noisy itself. Here, the benefit of distillation is
generally outweighed by the noise in the distillation circuit.
We can explicitly see this using a simulation, where we add noise to the distillation circuit. Note that we consider two different noise scales:
* `p` is the noise that captures the magic state preparation process.
* `noise` is the strength of the depolarizing noise in the distillation circuit.
```python theme={null}
noise = p / 5
c = tsim.Circuit(f"""
# initial state
R 0 1 2 3 4
R_X({theta}) 0 1 2 3 4
T_DAG 0 1 2 3 4
DEPOLARIZE1({p}) 0 1 2 3 4
# distillation
DEPOLARIZE1({noise}) 0 1 2 3 4
SQRT_X 0 1 4
DEPOLARIZE1({noise}) 0 1 4
CZ 0 1 2 3
DEPOLARIZE2({noise}) 0 1 2 3
SQRT_Y 0 3
DEPOLARIZE1({noise}) 0 3
CZ 0 2 3 4
DEPOLARIZE2({noise}) 0 2 3 4
TICK
SQRT_X_DAG 0
DEPOLARIZE1({noise}) 0
CZ 0 4 1 3
DEPOLARIZE2({noise}) 0 4 1 3
TICK
SQRT_X_DAG 0 1 2 3 4
DEPOLARIZE1({noise}) 0 1 2 3 4
# undo
T 0
R_X({-theta}) 0
M 0 1 2 3 4
""")
samples = c.compile_sampler(seed=0).sample(shots=40_000, batch_size=10_000)
def post_select(samples: np.ndarray):
distilled_output = samples[:, 0]
distillation_syndromes = samples[:, 1:]
sel = np.all(distillation_syndromes == np.array([1, 0, 1, 1]), axis=1)
return distilled_output[sel]
post_selected_samples = post_select(samples)
print(
f"Infidelity: {np.count_nonzero(post_selected_samples) / len(post_selected_samples):.5f}"
)
print(
f"Percentage of post-selected samples: {len(post_selected_samples) / len(samples) * 100:.2f}%"
)
```
```text title="Output" theme={null}
Infidelity: 0.04152
Percentage of post-selected samples: 12.83%
```
We see that the addition of noise has reduced the output fidelity to \~96%. This is worse than the input state!
# Encoding the distillation circuit
To address this issue, we need to make our distillation circuit resilient to noise.
We do this by encoding it using a quantum error correction code, specifically the \[17,1,5] color code. The helper class `ColorEncoder5` provides a convenient way to encode the distillation circuit. Each qubit will be encoded into 17 physical qubits using an encoding circuit. After that, each gate of the distillation circuit will be applied transversally to the encoded qubits.
```python theme={null}
def make_circuit(basis: Literal["X", "Y", "Z"], p_prep: float, noise: float = 0):
encoder = ColorEncoder5()
encoder.initialize(
f"""
R 0 1 2 3 4
R_X({theta}) 0 1 2 3 4
T_DAG 0 1 2 3 4
DEPOLARIZE1({p_prep}) 0 1 2 3 4
""",
)
encoder.encode_transversally(
f"""
SQRT_X 0 1 4
DEPOLARIZE1({noise}) 0 1 4
CZ 0 1 2 3
DEPOLARIZE2({noise}) 0 1 2 3
SQRT_Y 0 3
DEPOLARIZE1({noise}) 0 3
CZ 0 2 3 4
DEPOLARIZE2({noise}) 0 2 3 4
TICK
SQRT_X_DAG 0
DEPOLARIZE1({noise}) 0
CZ 0 4
DEPOLARIZE2({noise}) 0 4
TICK
CZ 1 3
DEPOLARIZE2({noise}) 1 3
TICK
SQRT_X_DAG 0 1 2 3 4
DEPOLARIZE1({noise}) 0 1 2 3 4
"""
+ ("H 0" if basis == "X" else "H_YZ 0" if basis == "Y" else "")
+ """
M 0 1 2 3 4
DETECTOR rec[-5]
DETECTOR rec[-4]
DETECTOR rec[-3]
DETECTOR rec[-2]
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-5]
OBSERVABLE_INCLUDE(1) rec[-4]
OBSERVABLE_INCLUDE(2) rec[-3]
OBSERVABLE_INCLUDE(3) rec[-2]
OBSERVABLE_INCLUDE(4) rec[-1]
"""
)
return encoder.circuit
c = make_circuit("Z", p_prep=p, noise=p / 10)
print(f"""Qubits: {c.num_qubits}
Detectors: {c.num_detectors}
Observables: {c.num_observables}""")
c.diagram("timeline-svg", height=500)
```
```text title="Output" theme={null}
Qubits: 85
Detectors: 40
Observables: 5
```
## The fully encoded protocol
The full circuit now contains 85 qubits: 5 logical qubits, constructed from 17 physical qubits each.
Let us summarize the protocol we are going to simulate:
* **Prepare 5 encoded magic states at 97% fidelity.** The input magic states are noisy, and we assume the encoding process to be noiseless. While this is a simplification for this tutorial, it does not introduce any qualitative changes. We can think of this as an abstract preparation protocol that produces 5 encoded magic states, each with 97% fidelity.
* **Distill 5 encoded magic states into a single logical qubit.** We use the transversal encoding of the distillation circuit shown above. This includes depolarization noise with strength `noise`.
* **Perform post-selection** We will perform two stages of post-selection: (a) post-select on perfect stabilizers, and (b) post-select on the \[1,0,1,1] distillation syndrome of logical bits.
* **Quantum state tomography** We will perform quantum state tomography on the output logical qubit to estimate its magic state fidelity.
```python theme={null}
def sample_and_post_select(circ: tsim.Circuit, shots: int = 35_000):
sampler = circ.compile_detector_sampler(seed=42)
det_samples, obs_samples = sampler.sample(
shots=shots, batch_size=shots, separate_observables=True
)
# post-select on perfect stabilizers
perfect_stabilizers = np.all(det_samples == 0, axis=1)
obs_samples = obs_samples[perfect_stabilizers]
post_selected_observables = post_select(obs_samples)
p1 = np.count_nonzero(post_selected_observables) / len(post_selected_observables)
eval = 1 - 2 * p1
print(
f"Expectation value: {eval:.4f}, percentage kept: {len(post_selected_observables) / shots * 100:.2f}%"
)
return eval
```
```python theme={null}
circ_x, circ_y, circ_z = (
make_circuit("X", p, p / 5),
make_circuit("Y", p, p / 5),
make_circuit("Z", p, p / 5),
)
vx = sample_and_post_select(circ_x)
vy = sample_and_post_select(circ_y)
vz = sample_and_post_select(circ_z)
```
```text title="Output" theme={null}
Expectation value: 0.5935, percentage kept: 1.05%
Expectation value: 0.5625, percentage kept: 1.19%
Expectation value: 0.5869, percentage kept: 1.13%
```
It would be impossible to simulate an 85-qubit circuit using a state vector simulator. But with Tsim, we can compute hundreds of thousands of shots in a few seconds.
Finally, we construct a density matrix from the measured logical expectation values:
```python theme={null}
id = np.eye(2)
x = np.array([[0, 1], [1, 0]])
y = np.array([[0, -1j], [1j, 0]])
z = np.array([[1, 0], [0, -1]])
rho = (id + vx * x + vy * y + vz * z) / 2
```
Next, we compute a statevector for the expected magic state:
```python theme={null}
a = theta * np.pi
rx = np.array(
[[np.cos(a / 2), -1j * np.sin(a / 2)], [-1j * np.sin(a / 2), np.cos(a / 2)]]
)
t_dag = np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]])
psi_expected = t_dag @ rx @ np.array([1, 0])
```
Then, we compute the fidelity as $\langle\psi_{expected}|\rho|\psi_{expected}\rangle$:
```python theme={null}
fidelity = psi_expected.conj().T @ rho @ psi_expected
print(f"Infidelity: {1 - fidelity.real:.5f}")
```
```text title="Output" theme={null}
Infidelity: -0.00313
```
We find that the overall fidelity has been increased from 97% of the individual encoded magic states to 99% of the output logical qubit, despite the presence of noise in the distillation circuit. This is an exciting result of quantum error correction!
This concludes our tutorial. We have learned:
* how to model noisy magic states and **simulate fidelities** using Tsim
* how to perform **magic state distillation** and **post-selection**
* how to **encode a distillation circuit** using the \[17,1,5] color code
* how to **simulate hundreds of thousands of shots** of this 85-qubit circuit within seconds on a laptop
* how magic state distillation can improve the fidelity of noisy magic states, **even when the distillation circuit is noisy itself**
# Overview
Source: https://bloqade.quera.com/tsim/tutorials/overview
Tsim is a quantum circuit sampler that can efficiently sample from Clifford+T circuits with Pauli noise. It is based on ZX-calculus stabilizer rank decomposition and parametrized ZX diagrams, following work of [arXiv:2403.06777](https://arxiv.org/abs/2403.06777).
## Supported Gates
Tsim supports a universal gate set, together with measurement and reset instructions, and Pauli noise channels.
### Clifford Instructions
Tsim supports all instructions [supported by Stim](https://github.com/quantumlib/Stim/wiki/Stim-v1.13-Gate-Reference). Below, we show the standard generating set of Clifford gates:
```python theme={null}
from tsim import Circuit
c = Circuit("H 0")
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 0.70710678+0.j 0.70710678+0.j]
[ 0.70710678+0.j -0.70710678+0.j]]
```
```python theme={null}
c = Circuit("S 0")
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 1.+0.j 0.-0.j]
[-0.+0.j 0.+1.j]]
```
```python theme={null}
c = Circuit("CNOT 0 1")
print(c.to_matrix())
c.diagram("timeline-svg", height=160)
```
```text title="Output" theme={null}
[[ 1.+0.j 0.+0.j -0.+0.j 0.+0.j]
[ 0.+0.j 1.+0.j 0.+0.j -0.+0.j]
[ 0.+0.j -0.+0.j 0.+0.j 1.+0.j]
[-0.+0.j 0.+0.j 1.+0.j 0.+0.j]]
```
### Non-Clifford Instructions
In addition to Clifford gates, Tsim supports the following non-Clifford gates. Note that all rotation parameters are defined in units of $\pi$.
Computation time and memory requirement scales exponentially with the number of non-Clifford gates.
```python theme={null}
c = Circuit("T 0")
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 1. +0.j 0. +0.j ]
[-0. +0.j 0.70710678+0.70710678j]]
```
```python theme={null}
c = Circuit("R_X(0.1) 0") # rotation around X-axis by 0.1π
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 0.98768834-0.j 0. -0.15643447j]
[-0. -0.15643447j 0.98768834-0.j ]]
```
$$
R_X(\alpha) =
\left(
\begin{array}{cc}
\cos(\alpha\pi/2) & -i \sin(\alpha\pi/2) \\
-i \sin(\alpha\pi/2) & \cos(\alpha\pi/2)
\end{array}
\right)
$$
```python theme={null}
c = Circuit("R_Y(0.1) 0") # rotation around Y-axis by 0.1π
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 0.98768834-0.j -0.15643447+0.j]
[ 0.15643447-0.j 0.98768834-0.j]]
```
$$
R_Y(\alpha) =
\left(
\begin{array}{cc}
\cos(\alpha\pi/2) & -\sin(\alpha\pi/2) \\
\sin(\alpha\pi/2) & \cos(\alpha\pi/2)
\end{array}
\right)
$$
```python theme={null}
c = Circuit("R_Z(0.1) 0") # rotation around Z-axis by 0.1π
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 0.98768834-0.15643447j 0. +0.j ]
[-0. +0.j 0.98768834+0.15643447j]]
```
$$
R_Z(\alpha) =
\left(
\begin{array}{cc}
e^{-i\alpha\pi/2} & 0 \\
0 & e^{i\alpha\pi/2}
\end{array}
\right)
$$
```python theme={null}
c = Circuit("U3(0.1, 0.2, 0.3) 0")
print(c.to_matrix())
c.diagram("timeline-svg", height=120)
```
```text title="Output" theme={null}
[[ 0.98768834-0.j -0.09194987-0.12655814j]
[ 0.12655814+0.09194987j 0. +0.98768834j]]
```
$$
U_3(\theta, \phi, \lambda) =
\left(
\begin{array}{cc}
\cos(\theta\pi/2) & -e^{i\lambda\pi}\sin(\theta\pi/2) \\
e^{i\phi\pi}\sin(\theta\pi/2) & e^{i(\phi+\lambda)\pi}\cos(\theta\pi/2)
\end{array}
\right)
$$
### Measurement and Reset instructions
Tsim supports all collapsing gates [supported by Stim](https://github.com/quantumlib/Stim/wiki/Stim-v1.9-Gate-Reference#collapsing-gates).
```python theme={null}
c = Circuit("M 0")
c.diagram("timeline-svg", height=120)
```
Measurements (`M`, `MX`, `MY`, `MZ`) project the state into the measurement basis and write the resulting bit into the measurement record.
The measurement record can be used to conditionally apply Pauli gates:
```python theme={null}
c = Circuit("""
M 0
CY rec[-1] 1
""")
c.diagram("timeline-svg", height=170)
```
The `!` operator can be used to invert the classical measurement bit that is written into the measurement record:
```python theme={null}
c = Circuit("M !0")
c.diagram("timeline-svg", height=120)
```
The `MPP` instruction measures Pauli strings. The MPP can also be used in conjunction with the `!` operator to flip the classical measurement bit before writing it into the measurement record.
```python theme={null}
c = Circuit("MPP !Z0*X2*Y3")
c.diagram("timeline-svg", height=260)
```
The `MXX`, `MYY`, and `MZZ` instructions measure two-qubit Pauli parity. For example, `MZZ` measures whether pairs of qubits are in the same-parity subspace ($\{|00\rangle, |11\rangle\}$) or opposite-parity subspace ($\{|01\rangle, |10\rangle\}$).
```python theme={null}
c = Circuit("MZZ 0 1")
c.diagram("timeline-svg", height=170)
```
The `MPAD` instruction pads the measurement record with fixed bit values (0 or 1), without performing any actual measurement.
```python theme={null}
c = Circuit("MPAD 0 1 1 0")
c.diagram("timeline-svg", height=170)
```
The `SPP` instruction applies $e^{-i \pi/4 \cdot P}$ (up to global phase) for a Pauli product $P$, which is equivalent to phasing the $-1$ eigenspace of $P$ by $i$. For example, `SPP Z0` is equivalent to the `S` gate, and `SPP X0*X1` is equivalent to `SQRT_XX`. The `SPP_DAG` instruction applies $e^{+i \pi/4 \cdot P}$ (phases by $-i$) instead. The `!` operator inverts the product, swapping `SPP` and `SPP_DAG` behavior.
```python theme={null}
c = Circuit("SPP X0*Y1*Z2")
c.diagram("timeline-svg", height=220)
```
Similarly, `TPP` applies $e^{-i \pi/8 \cdot P}$ (up to global phase), phasing the $-1$ eigenspace by $e^{i\pi/4}$. `TPP_DAG` applies $e^{+i \pi/8 \cdot P}$ (phases by $e^{-i\pi/4}$).
```python theme={null}
c = Circuit("""
SPP Z0 * Z3 * Y4
# TPP X0*Y1
TPP Z0
SPP Z0*Z0*Z1*Z1
TPP_DAG X0*Y1*Z2
""")
c.diagram("timeline-svg", height=420)
```
### Noise Channels
Tsim supports all noise channels [supported by Stim](https://github.com/quantumlib/Stim/wiki/Stim-v1.9-Gate-Reference#noise-channels).
The `X_ERROR(p)` instruction is a `X` instruction that is applied with probability `p`.
```python theme={null}
c = Circuit("X_ERROR(0.1) 0")
c.diagram("timeline-svg", height=120)
```
The `PAULI_CHANNEL_1(p_x, p_y, p_z)` instruction is a `X`, `Y`, and `Z` instruction that is applied with probabilities `p_x`, `p_y`, and `p_z` respectively.
```python theme={null}
c = Circuit("PAULI_CHANNEL_1(0.1, 0.2, 0.3) 0")
c.diagram("timeline-svg", height=120)
```
The `PAULI_CHANNEL_2` instruction takes fifteen floats specifying the disjoint probabilities of each possible Pauli pair
that can occur (except for the non-error double identity case).
The disjoint probability arguments are (in order):
p\_ix,
p\_iy,
p\_iz,
p\_xi,
p\_xx,
p\_xy,
p\_xz,
p\_yi,
p\_yx,
p\_yy,
p\_yz,
p\_zi,
p\_zx,
p\_zy,
p\_zz
```python theme={null}
c = Circuit(
"PAULI_CHANNEL_2(0.01, 0.01, 0.03, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01) 0 1"
)
c.diagram("timeline-svg", height=170)
```
The `DEPOLARIZE1(p)` instruction applies a randomly chosen Pauli with probability `p`.
```python theme={null}
c = Circuit("DEPOLARIZE1(0.01) 0")
c.diagram("timeline-svg", height=120)
```
The `DEPOLARIZE2(p)` instruction applies a randomly chosen two-qubit Pauli with probability `p`.
```python theme={null}
c = Circuit("DEPOLARIZE2(0.01) 0 1")
c.diagram("timeline-svg", height=170)
```
The `CORRELATED_ERROR(p)` instruction applies a specified Pauli product with probability `p`. If no error occurred, then a following `ELSE_CORRELATED_ERROR(p2)` instruction may apply a Pauli product with probabiliy `p2`. If no error occurs again, further `ELSE_CORRELATED_ERROR(pi)` instructions in the chain may apply a Pauli products.
```python theme={null}
c = Circuit("""
CORRELATED_ERROR(0.1) X0 # Apply X with probability 0.1
ELSE_CORRELATED_ERROR(0.2) Z0 Y1 # If no error occurred, apply Z0*Y1 with probability 0.2
ELSE_CORRELATED_ERROR(0.3) X1 # If still no error, apply X1 with probability 0.3
""")
c.diagram("timeline-svg", height=170)
```
### Annotations
Tsim supports detector and observable annotations.
The `DETECTOR` instruction is only used in detector sampling mode and ignored otherwise. It instructs the detector sampler to record the XOR of classical outcomes of specified measurement bits.
```python theme={null}
c = Circuit("""
M 0 0
DETECTOR rec[-1] rec[-2]
""")
c.diagram("timeline-svg", height=150)
```
The `OBSERVABLE_INCLUDE` instruction is only used in observable sampling mode and ignored otherwise. It instructs the detector sampler to record the XOR of the specified measurement bits.
```python theme={null}
c = Circuit("""
M 0 0
OBSERVABLE_INCLUDE(0) rec[-1] rec[-2]
""")
c.diagram("timeline-svg", height=150)
```
## Sampling
Tsim supports multiple samplers. The first is a measurement sampler. This will simply sample bits for each measurement instruction in the circuit. Detector and observable annotations will simply be ignored by this sampler.
```python theme={null}
c = Circuit("""
RX 0
R 1
CNOT 0 1
M 0 1
""")
sampler = c.compile_sampler()
c.diagram("timeline-svg", height=170)
```
```python theme={null}
sampler.sample(shots=5)
```
```text title="Output" theme={null}
array([[False, False],
[ True, True],
[False, False],
[False, False],
[ True, True]])
```
The second sampling mode is detector sampling. This will sample detector events and observable values. Detector and observable bits can always be obtained by linear transformations of the measurement bits as return by the measurement sampler.
In practice, however, it can be much more efficient to sample detector events directly.
```python theme={null}
c = Circuit("""
RX 0
R 1
CNOT 0 1
M 0 1
DETECTOR rec[-1] rec[-2]
OBSERVABLE_INCLUDE(0) rec[-1]
""")
sampler = c.compile_detector_sampler()
c.diagram("timeline-svg", height=170)
```
```python theme={null}
detectors, observables = sampler.sample(5, separate_observables=True)
print(detectors)
print(observables)
```
```text title="Output" theme={null}
[[False]
[False]
[False]
[False]
[False]]
[[False]
[ True]
[False]
[False]
[ True]]
```
Finally, Tsim allows to compute probability values for target states via the `CompiledStateProbs` sampler.
```python theme={null}
import numpy as np
from tsim.sampler import CompiledStateProbs
sampler = CompiledStateProbs(c)
```
```python theme={null}
sampler.probability_of(np.array([0, 0]), batch_size=1)
```
```text title="Output" theme={null}
array([0.5], dtype=float32)
```
```python theme={null}
sampler.probability_of(np.array([0, 1]), batch_size=1)
```
```text title="Output" theme={null}
array([0.], dtype=float32)
```
```python theme={null}
sampler.probability_of(np.array([1, 0]), batch_size=1)
```
```text title="Output" theme={null}
array([0.], dtype=float32)
```
```python theme={null}
sampler.probability_of(np.array([1, 1]), batch_size=1)
```
```text title="Output" theme={null}
array([0.5], dtype=float32)
```
## Visualization
Tsim supports multiple ways of visualizing quantum circuits.
The `timeline-svg` diagram shows the circuit as a time-ordered sequence of gates.
```python theme={null}
c = Circuit("""
QUBIT_COORDS(0, 0) 0 # specifies qubit coordinates for the "timeslice-svg" diagram
QUBIT_COORDS(1, 1) 1
H 0
TICK
T 0
H 0
TICK
CNOT 0 1
TICK
DEPOLARIZE2(0.1) 0 1
TICK
R_Z(0.2) 1
TICK
M 0 1
DETECTOR rec[-1] rec[-2]
OBSERVABLE_INCLUDE(0) rec[-1]
""")
c.diagram("timeline-svg", height=170)
```
When `TICK` instructions are present, each tick can be shown as a 2D time slice with the `timeslice-svg` diagram. Here, `QUBIT_COORDS` annotations can be used to specify the 2D coordinates of the qubits.
```python theme={null}
c.diagram("timeslice-svg", width=800, rows=1)
```
With the `pyzx` argument, the circuit can be visualized using the [pyzx](https://github.com/zxcalc/pyzx) as a ZX-diagram.
```python theme={null}
c.diagram("pyzx");
```
The `pyzx-meas` and `pyzx-dets` diagrams show ZX diagrams where outputs represent probabilities of measurement outcomes for measurement and detector/observables, respectively.
```python theme={null}
c.diagram("pyzx-meas", scale_horizontally=2);
```
```python theme={null}
c.diagram("pyzx-dets", scale_horizontally=1.5);
```
## Detector Error Models
Tsim allows to compute detector error models from a circuit. The method `Circuit.detector_error_model()` computes a `stim.DetectorErrorModel` from the circuit. As opposed to Stim, detectors and observables need not be deterministic.
```python theme={null}
c = Circuit("""
RX 0
R 1
CNOT 0 1
DEPOLARIZE1(0.1) 0 1
M 0 1
DETECTOR rec[-1] rec[-2]
DETECTOR rec[-1]
""")
c.detector_error_model()
```
```text title="Output" theme={null}
stim.DetectorErrorModel('''
error(0.0666667) D0
error(0.0666667) D0 D1
error(0.5) D1
''')
```