API Reference¶
For a guided tour see Getting Started and Circuit Execution.
Gate / operator matrix inputs
Methods that take a 1- or 2-qubit gate or operator (apply_gate_1q,
apply_gate_2q, expectation_1q, expectation_2q, apply_kraus_*,
apply_mixed_unitary_*) expect a numpy complex128 array: a (2, 2)
array for a 1-qubit gate, a (4, 4) array for a 2-qubit gate, and a
(n_ops, dim, dim) stack for a set of Kraus / mixed-unitary operators.
Qubit / bitstring ordering
Exaqt is little-endian by qubit index: qubit 0 is the
least-significant bit of the amplitude index, so bs[0] is qubit 0.
This matches Qiskit's little-endian ordering and MimiqCircuitsBase.
State vector — ExaqtSV¶
The dense quantum register. Amplitudes are complex128, indexed
little-endian (qubit 0 = LSB).
Constructors:
| Method | Description |
|---|---|
ExaqtSV.zero(num_qubits) |
\|0...0> state on num_qubits qubits |
ExaqtSV.from_bitstring(bits) |
Product state from a 0/1 sequence (qubit 0 = LSB) |
ExaqtSV.from_amplitudes(num_qubits, arr) |
State from a complex128 amplitude array |
Properties & inspection:
| Member | Returns |
|---|---|
sv.num_qubits |
int — number of qubits |
sv.len / len(sv) |
int — number of amplitudes, 2**num_qubits |
sv.get(idx) |
complex — amplitude of basis state idx |
sv.probability(idx) |
float — probability of basis state idx |
sv.amplitudes() |
complex128 numpy array of all amplitudes |
sv.norm_squared() |
float — squared norm |
sv.normalize() |
float — norm before normalising; renormalises in place |
sv.reset_to_zero() |
Reset to \|0...0> in place |
sv.clone() |
Deep copy |
Single-qubit gates (target qubit last, angles first):
| Method | Description |
|---|---|
sv.apply_x/y/z(target) |
Pauli gates |
sv.apply_h(target) |
Hadamard |
sv.apply_s/sdg/t/tdg/sx(target) |
Phase / T / SX gates |
sv.apply_p(lmbda, target) |
Phase gate P(lambda) |
sv.apply_rx/ry/rz(angle, target) |
Rotation gates |
sv.apply_u(theta, phi, lmbda, target) |
General U gate |
sv.apply_gate_1q(gate, target) |
Arbitrary 1-qubit unitary, (2, 2) complex128 |
Two-qubit gates:
| Method | Description |
|---|---|
sv.apply_cx/cy/cz(a, b) |
Controlled Pauli gates |
sv.apply_swap/iswap(q1, q2) |
(i)SWAP |
sv.apply_cp(lmbda, q1, q2) |
Controlled phase |
sv.apply_crx/cry/crz(angle, control, target) |
Controlled rotations |
sv.apply_rxx/ryy/rzz(theta, q1, q2) |
Two-qubit rotations |
sv.apply_gate_2q(gate, q1, q2) |
Arbitrary 2-qubit unitary, (4, 4) complex128 |
Multi-controlled gates:
| Method | Description |
|---|---|
sv.apply_mcx(controls, target) |
Multi-controlled X |
sv.apply_controlled_gate_1q(gate, controls, target) |
Controlled 1-qubit unitary |
sv.apply_controlled_gate_2q(gate, controls, q1, q2) |
Controlled 2-qubit unitary |
Measurement & sampling (all take a seeded Rng):
| Method | Returns |
|---|---|
sv.prob_zero(target) |
float — probability of measuring 0 on target |
sv.measure_qubit(target, rng) |
int — outcome, collapses the state |
sv.reset_qubit(target, rng) |
int — pre-reset outcome, resets qubit to \|0> |
sv.sample_bitstring(rng) |
uint8 array — one sampled bitstring |
sv.sample(rng, nsamples) |
(nsamples, num_qubits) uint8 array |
Noise channels (each takes a seeded Rng):
| Method | Description |
|---|---|
sv.apply_kraus_1q(kraus_ops, target, rng) |
1-qubit Kraus channel, (n_ops, 2, 2) |
sv.apply_kraus_2q(kraus_ops, q1, q2, rng) |
2-qubit Kraus channel, (n_ops, 4, 4) |
sv.apply_mixed_unitary_1q(unitaries, probs, target, rng) |
1-qubit mixed-unitary channel |
sv.apply_mixed_unitary_2q(unitaries, probs, q1, q2, rng) |
2-qubit mixed-unitary channel |
Expectation values:
| Method | Returns |
|---|---|
sv.expectation_1q(op, target) |
complex — <psi\|O\|psi> for a (2, 2) operator |
sv.expectation_2q(op, q1, q2) |
complex — <psi\|O\|psi> for a (4, 4) operator |
sv.expectation_pauli(paulis, qubits) |
complex — expectation of a Pauli string (e.g. "ZZZ") |
Simulator — ExaqtQCS¶
exaqt.ExaqtQCS
¶
Bases: LocalBackend
State-vector quantum circuit simulator.
Subclasses :class:mimiqcircuits.backends.LocalBackend and
declares the Julia ExaqtQCS capability set plus
parametric (Python-only — supported through the inherited
slow-path :meth:bind; the Julia wrapper omits the token).
Parametric circuits are supported via the inherited slow-path
:meth:bind (substitute then re-compile); there is no native
compile-once / bind-many fast path yet.
Pauli-string expectations on any number of qubits are supported:
ExpectationValue(PauliString("XYZ...")) dispatches to the
native mask-based expectation_pauli (cost O(2^n) in the
state size, independent of the string length). Only expectations
of non-Pauli operators (e.g. a GateCustom matrix, or a
Control/Power/Inverse-wrapped Pauli) keep the dense two-qubit cap.
Caveat — noise + :class:ExactFidelity: _apply_kraus
renormalises the state after sampling a Kraus branch, so the
per-shot fidelity is always 1.0 and the typed wrap is
:class:ExactFidelity. That is honest for trace-preserving
channels (the sampled trajectory is exact under renormalisation).
For non-trace-preserving channels (e.g. amplitude damping with
explicit loss) the surviving-trajectory probability is not
tracked, and :class:ExactFidelity over-states reliability.
Attributes:
| Name | Type | Description |
|---|---|---|
seed |
instance-level RNG seed. |
|
reorderqubits |
qubit-reordering policy for the default pipeline
(:meth: |
Examples:
>>> import mimiqcircuits as mc
>>> from exaqt import ExaqtQCS
>>> sim = ExaqtQCS()
>>> c = mc.Circuit()
>>> c.push(mc.GateH(), 0)
...
>>> c.push(mc.GateCX(), 0, 1)
...
>>> c.push(mc.Measure(), 0, 0)
...
>>> c.push(mc.Measure(), 1, 1)
...
>>> results = sim.execute(c, nsamples=1000)
__init__(seed=None, reorderqubits=None)
¶
execute(circuit, nsamples=1000, *, seed=None, rng=None, num_qubits=None, passes=None, callback=None, param_grid=None, strict_pass_order=True, stopped=None, progress=False)
¶
Execute a circuit and return :class:mimiqcircuits.QCSResults.
seed and rng are mutually exclusive entropy sources;
pass at most one. With neither, the simulator's instance
seed (set at construction) is used, falling back to fresh
OS entropy. Amplitudes are requested via in-circuit
:class:Amplitude ops that write into results.zstates.
With no passes, :meth:default_passes runs — gate fusion
above a cache-derived qubit threshold, then qubit reordering per
the reorderqubits constructor option. Pass
passes=PassPipeline() to disable both or a custom pipeline to
retune them.
evolvezerostate(circuit, *, seed=None)
¶
Evolve the zero state through circuit; return
(state, fidelity) with a plain float fidelity (always
1.0 for Exaqt's renormalising path).
Convenience: matches the engines-side SimulatorState.zero(...)
+ evolve(state, c, rng=...) two-step. The dual-shape
:meth:evolve covers the more general case where the caller
owns the state.
Seed resolution mirrors :meth:execute: call-site seed= →
instance self.seed → fresh cryptographic entropy from
:mod:secrets.
For circuits with Kraus channels or mid-circuit measurements
this is a single stochastic trajectory; use :meth:execute
for ensemble statistics.
evolve(state, compiled, *, rng=None, callback=None, stopped=None)
¶
Evolve state under compiled. Returns
(state, ExactFidelity) — state-vector evolution is exact
per trajectory, so no truncation bound is reported.
compile(circuit)
¶
Identity-wrap the circuit.
NOTE: Exaqt dispatches every supported gate natively, so
no Julia-side convertcircuit impurity applies here (cf. the
engines wrappers). The wrap is a pure identity over the circuit.
expectation(state, op, *qubits)
¶
Compute ⟨ψ|op|ψ⟩ on state without mutating it.
Supports 1- and 2-qubit operators. qubits is the list of
qubit indices op acts on (0-based, matching the rest of
the Python API).
Raises :class:ValueError for any other operator size.
capabilities()
¶
Composite state — ExaqtState¶
exaqt.ExaqtState
¶
Bases: State
Composite simulation state.
Use one of the staticmethod factories — :meth:zero,
:meth:one, :meth:product — to construct a state, then pass it
to :func:exaqt.evolve or :func:exaqt.apply_instruction.
Attributes:
| Name | Type | Description |
|---|---|---|
q |
ExaqtSV
|
the dense quantum register. |
c |
list[int]
|
classical-bit register, length |
z |
list[complex]
|
complex z-variable register, length
|
zero(num_qubits, num_bits=0, num_zvars=0)
staticmethod
¶
|0…0⟩ quantum state, classical/z registers initialised to 0.
one(num_qubits, num_bits=0, num_zvars=0)
staticmethod
¶
|1…1⟩ quantum state, classical/z registers initialised to 1.
product(bits, num_bits=0, num_zvars=0)
staticmethod
¶
Product state from a 0/1 bitstring (qubit 0 = LSB).
amplitude(bitstring)
¶
Get the amplitude for a given computational basis state.
sample(nsamples, rng=None, *, seed=None)
¶
Sample measurement outcomes from the quantum state.
Either rng (a :class:random.Random) or seed (an
int) may be passed, but not both. The Rust Rng is
constructed from a 63-bit derived seed when rng is given.
reset()
¶
Reset all four registers to their zero state in place.
Per-instruction primitives¶
exaqt.evolve(state, circuit, *, rng=None, callback=None)
¶
Evolve state through every instruction in circuit in place.
Returns (state, fidelity). Fidelity is always 1.0 for the
state-vector simulator.
callback, when given, is called as callback(i, total) after
each applied instruction — the execute driver turns this into the
per-step execution bar.
exaqt.apply_instruction(state, inst, rng=None)
¶
Apply a single circuit instruction to state in place.
Returns fidelity (1.0 — Exaqt is a noiseless simulator).
Raises :class:NotImplementedError for operations the rust core does
not support yet (Kraus channels, BondDim, SchmidtRank).
Compilation passes¶
exaqt.ExaqtReorderQubitsPass
¶
Bases: AbstractPass
Reorder qubits to land gates on the fastest kernels.
Uses the native optimize_ordering to minimise the estimated
runtime Σ weight · kernel_cost(slot) over qubit permutations,
where kernel_cost captures the two-qubit SIMD threshold (lower
index >= 2) and the slower 1-qubit slots 0/1/2.
Parameters¶
method:
"greedy" (default, deterministic) or "sa" /
"simulated_annealing". True is a back-compat shorthand
for "greedy".
seed:
Seed for "sa". None draws one from the pass-context RNG.
qubit_threshold:
Minimum qubit count for the pass to act; smaller circuits pass
through unchanged. 0 (the default) always reorders — the
gate is meant for the default pipeline, which sets it so
reordering only kicks in once the state vector outgrows the
cache.
spec()
¶
Identify this pass and its method / optional seed.
preserves(feature)
¶
Always True: relabelling preserves every circuit feature;
the pipeline maps the output cstate back to the user's frame.
apply(ctx, circuit)
¶
Run the optimiser and return the rewritten circuit plus the
permutation it chose (None if it left the circuit unchanged).
Random number generator — Rng¶
Rng(seed) is a seeded PRNG handle used by every measurement, sampling,
and noise-channel method. The same seed reproduces the same Xoshiro256++
stream across the Rust core and the Python and Julia wrappers.
Package metadata¶
| Member | Returns |
|---|---|
exaqt.__version__ |
str — installed version of the mimiq-exaqt distribution |
exaqt.docs_dir()
¶
Path to the HTML documentation bundled with the package.
Returns the directory holding the built documentation site
(index.html and assets) that ships inside the wheel. Open it with
the exaqt docs console command. The directory is absent in editable
maturin develop installs, which do not build the docs.
Exceptions¶
All exceptions subclass ExaqtError (itself a RuntimeError):
| Exception | Raised when |
|---|---|
GateShapeError |
a gate matrix has the wrong shape or layout |
QubitIndexError |
a qubit index is out of bounds, or duplicated |
DegenerateStateError |
sampling a state with zero / non-finite norm |
NonUnitaryError |
a non-unitary user matrix (only with the unitary-checks build feature) |