Compiling Circuits¶
Clifft compiles a supported circuit input into a reusable Program. Compilation resolves Clifford coordinates, symbolic dependencies, active-state transitions, and executable actions before sampling starts.
Most users only need clifft.compile(). The lower-level functions expose the same pipeline for inspecting intermediate representations or supplying a custom optimization sequence.
Default path¶
Pass circuit text directly to clifft.compile():
The returned program can be reused by ordinary sampling, survivor sampling, fixed-fault importance sampling, or an exact-query API when the circuit meets that API's requirements:
Compilation options such as postselection_mask, normalize_syndromes, expected_detectors, and expected_observables define the result contract used during sampling.
For detector-based post-selection, use normalize_syndromes=True when the circuit has fixed detector and observable parities after stochastic and readout noise are removed. Clifft samples the reference once during compilation without checking determinism; coherent error rotations remain. For those errors, supply expected_detectors and expected_observables from the intended ideal circuit, as described in reference selection. sample_survivors() applies the post-selection mask to the normalized detector values; it does not recompute the reference per call.
Circuits containing LEAKAGE, LOSS, or LEVEL_TRANSITION annotations, or the status probes HERALD_LEAKAGE_EVENT and HERALD_LOSS_EVENT, use the noncomputational API. Pass the circuit and model to clifft.noncomp.sample(), or reuse preparation with clifft.noncomp.compile(). It automatically selects fixed execution or general trajectories; see Leakage and Loss.
Active-width scheduling¶
For repeated sampling, try the opt-in ActiveWidthSchedulePass. It searches for an operation order with lower peak active width, or less estimated dense work at the same peak. A smaller active state can save sampling time and memory, but the search adds compilation time and may find no improvement.
Add it after the default fusion, phase reduction and squeezing passes:
import clifft
pm = clifft.default_hir_pass_manager()
pm.add(clifft.ActiveWidthSchedulePass())
program = clifft.compile("H 0 1 2\nT 0 1 2\nM 0 1 2", hir_passes=pm)
In the playground, open Passes and enable ActiveWidthSchedulePass. Ordinary compilation keeps this extra search disabled.
Compare compilation plus sampling time at the shot counts your application uses. Compilation is paid once per program, so savings across many shots can repay the search cost. A lower width or work estimate does not guarantee faster execution; omitting the pass can be faster for short runs. Reordering preserves sampling distributions but can change samples for a given random seed.
See Optimization Passes for options, result statistics, and search-budget limits, and the theoretical overview for why ordering affects active width.
Inspect or customize the pipeline¶
The following APIs are intended for power users working on compiler behavior, custom optimization, or intermediate-representation inspection.
Parse the circuit¶
clifft.parse() returns a Circuit without tracing or lowering it:
Use clifft.parse_file() for a file, or the format-specific parsing functions listed on Circuit Inputs for OpenQASM 2.
Trace to the Heisenberg IR¶
clifft.trace() absorbs Clifford operations into an offline tableau and emits explicit non-Clifford operations, measurements, noise, and classical outputs in the Heisenberg frame:
Optimize the HIR¶
The default pass manager applies the same HIR optimization sequence used by clifft.compile():
import clifft
hir = clifft.trace(clifft.parse("H 0\nCNOT 0 1\nT 0\nM 0 1"))
clifft.default_hir_pass_manager().run(hir)
Construct a focused pipeline when individual passes need to be controlled:
import clifft
hir = clifft.trace(clifft.parse("H 0\nT 0\nM 0"))
passes = clifft.HirPassManager()
passes.add(clifft.PeepholeFusionPass())
passes.run(hir)
Pass hir_passes=None to clifft.compile() to skip HIR optimization while keeping the one-step API.
Plan and prepare execution¶
clifft.lower() converts an optimized HirModule into the same public Program type returned by clifft.compile():
import clifft
hir = clifft.trace(clifft.parse("H 0\nT 0\nM 0"))
clifft.default_hir_pass_manager().run(hir)
program = clifft.lower(hir)
When optimizing for survivor sampling or normalized syndromes, pass postselection_mask, normalize_syndromes, expected_detectors, or expected_observables to clifft.trace(). The resulting HirModule carries those settings through HIR optimization and into clifft.lower(hir) automatically.
Internally, lowering first creates a semantic sampling plan that selects active coordinates and records expressions, rotations, measurements, width transitions, and outputs. Executable preparation then creates the immutable descriptors and dependency tables used by the executor and selects supported scalar or SIMD kernels.
These decisions happen before execution. Runtime kernels do not evolve a tableau, choose coordinates, localize Paulis, or discover dependencies. See Software Architecture for the contracts between HIR, semantic planning, executable preparation, and shot execution.