atlas_q.stabilizer_backend#
Stabilizer Backend for Efficient Clifford Circuit Simulation
Implements the Gottesman-Knill theorem: Clifford circuits can be simulated efficiently (polynomial time/space) using stabilizer tableaux.
Key features: - O(n²) time per gate vs O(2ⁿ) for state-vector - Supports: H, S, CNOT, CZ, SWAP, measurements - Handoff to MPS when non-Clifford gates appear (T, Toffoli, etc.)
References: - Gottesman (1998): “The Heisenberg Representation of Quantum Computers” - Aaronson & Gottesman (2004): “Improved Simulation of Stabilizer Circuits”
Author: ATLAS-Q Contributors Date: October 2025 License: MIT
- class atlas_q.stabilizer_backend.StabilizerState(n_qubits, tableau)[source]#
Bases:
objectStabilizer state represented as a tableau
A stabilizer state on n qubits is represented by a (2n+1) × 2n binary matrix: - First n rows: X part of stabilizer generators - Next n rows: Z part of stabilizer generators - Last column: phase bits (±1)
Example for |0⟩: stabilizer is Z Example for |+⟩: stabilizer is X Example for Bell |Φ+⟩: stabilizers are XX and ZZ
Methods
copy()Create a copy of this state
init_plus(n_qubits)Initialize |++...+⟩ state (stabilized by X on each qubit)
init_zero(n_qubits)Initialize |00...0⟩ state (stabilized by Z on each qubit)
- class atlas_q.stabilizer_backend.StabilizerSimulator(n_qubits)[source]#
Bases:
objectEfficient simulator for Clifford circuits using stabilizer formalism
Complexity: - Space: O(n²) vs O(2ⁿ) for state-vector - Time per gate: O(n²) vs O(2ⁿ)
- Usage:
sim = StabilizerSimulator(n_qubits=100) sim.h(0) sim.cnot(0, 1) outcome = sim.measure(0)
Methods
cnot(control, target)CNOT gate
copy()Fast copy using numpy array copy (much faster than deepcopy)
cz(qubit1, qubit2)CZ gate (symmetric)
h(qubit)Hadamard gate: X ↔ Z
measure(qubit[, rng])Measure qubit in computational basis
s(qubit)Phase gate: X → Y, Z → Z
s_dag(qubit)Inverse phase gate: X → -Y, Z → Z
swap(qubit1, qubit2)SWAP gate
to_mps([device])Convert stabilizer state to MPS representation
Convert stabilizer state to full statevector (SLOW! Only for small n)
x(qubit)Pauli-X gate
y(qubit)Pauli-Y gate
z(qubit)Pauli-Z gate
- measure(qubit, rng=None)[source]#
Measure qubit in computational basis
- Returns:
0 or 1 (measurement outcome)
- atlas_q.stabilizer_backend.is_clifford_gate(gate_name)[source]#
Check if a gate is in the Clifford group
- class atlas_q.stabilizer_backend.HybridSimulator(n_qubits, use_stabilizer=True, chi_max=64, device='cuda')[source]#
Bases:
objectHybrid simulator that uses stabilizer backend for Clifford parts and switches to MPS when non-Clifford gates appear
- Usage:
sim = HybridSimulator(n_qubits=100, use_stabilizer=True) sim.h(0) sim.cnot(0, 1) sim.t(0) # Automatically switches to MPS here sim.measure_all()
Methods
cnot(control, target)CNOT gate
Get simulation statistics
h(qubit)Hadamard gate
measure(qubit)Measure qubit
s(qubit)Phase gate
swap(qubit1, qubit2)SWAP gate (Clifford)
t(qubit)T gate (non-Clifford!)
x(qubit)Pauli-X gate
z(qubit)Pauli-Z gate
Overview#
The stabilizer_backend module provides efficient simulation of Clifford circuits using the stabilizer formalism based on the Gottesman-Knill theorem. This enables polynomial-time simulation of an important subset of quantum circuits that would otherwise require exponential resources.
Key Features#
Polynomial complexity: O(n²) time and O(n²) space for n-qubit systems
Exact simulation: No approximation errors unlike tensor network methods
Large-scale: Simulate 100+ qubits efficiently
Hybrid support: Automatic fallback to MPS for non-Clifford gates
Measurement tracking: Efficient sampling and measurement simulation
The Gottesman-Knill Theorem#
Circuits consisting only of Clifford gates (H, S, CNOT, Pauli gates) can be simulated efficiently on classical computers:
where n is the number of qubits and g is the number of gates.
Clifford gates preserve stabilizer states, allowing the state to be represented as a set of n independent stabilizer generators rather than 2ⁿ amplitudes.
Mathematical Background#
Stabilizer Formalism#
A stabilizer state \(|\psi\rangle\) is uniquely defined by its stabilizer group S, a maximal Abelian subgroup of the n-qubit Pauli group:
where each generator \(g_i \in \{I, X, Y, Z\}^{\otimes n}\) with phase \(\pm 1, \pm i\).
Property: \(|\psi\rangle\) is the unique state satisfying:
Tableau Representation#
ATLAS-Q uses the stabilizer tableau representation (Aaronson-Gottesman):
Stabilizer generators: (n × 2n) binary matrix encoding X and Z components
Destabilizers: Additional n generators for measurement tracking
Phases: (2n,) array of {0, 1, 2, 3} representing {+1, +i, -1, -i}
Total storage: O(n²) bits vs. O(2ⁿ) amplitudes for statevector.
Clifford Gate Set#
Single-qubit Clifford gates:
Two-qubit Clifford gate:
Pauli gates: X, Y, Z
Non-Clifford gates (require MPS fallback): T, Toffoli, rotation gates
Classes#
Efficient simulator for Clifford circuits using stabilizer formalism |
|
Stabilizer state represented as a tableau |
|
Hybrid simulator that uses stabilizer backend for Clifford parts and switches to MPS when non-Clifford gates appear |
Functions#
Check if a gate is in the Clifford group |
StabilizerSimulator#
- class atlas_q.stabilizer_backend.StabilizerSimulator(n_qubits)[source]#
Bases:
objectEfficient simulator for Clifford circuits using stabilizer formalism
Complexity: - Space: O(n²) vs O(2ⁿ) for state-vector - Time per gate: O(n²) vs O(2ⁿ)
- Usage:
sim = StabilizerSimulator(n_qubits=100) sim.h(0) sim.cnot(0, 1) outcome = sim.measure(0)
Methods
cnot(control, target)CNOT gate
copy()Fast copy using numpy array copy (much faster than deepcopy)
cz(qubit1, qubit2)CZ gate (symmetric)
h(qubit)Hadamard gate: X ↔ Z
measure(qubit[, rng])Measure qubit in computational basis
s(qubit)Phase gate: X → Y, Z → Z
s_dag(qubit)Inverse phase gate: X → -Y, Z → Z
swap(qubit1, qubit2)SWAP gate
to_mps([device])Convert stabilizer state to MPS representation
Convert stabilizer state to full statevector (SLOW! Only for small n)
x(qubit)Pauli-X gate
y(qubit)Pauli-Y gate
z(qubit)Pauli-Z gate
Pure stabilizer simulator for Clifford circuits.
Uses the tableau representation to efficiently track stabilizer generators through Clifford gate applications. Supports measurement with outcome sampling and wavefunction collapse.
Constructor:
sim = StabilizerSimulator(n_qubits=100)
- Parameters:
n_qubits(int): Number of qubitsinitial_state(str, optional): Initial computational basis state (default: ‘0’*n)
Storage: O(n²) bits Gate complexity: O(n²) per gate
Methods
__init__(n_qubits)h(qubit)Hadamard gate: X ↔ Z
s(qubit)Phase gate: X → Y, Z → Z
x(qubit)Pauli-X gate
y(qubit)Pauli-Y gate
z(qubit)Pauli-Z gate
cnot(control, target)CNOT gate
cz(qubit1, qubit2)CZ gate (symmetric)
swap(qubit1, qubit2)SWAP gate
measure(qubit[, rng])Measure qubit in computational basis
Example Usage:
from atlas_q.stabilizer_backend import StabilizerSimulator # Create 100-qubit Clifford circuit sim = StabilizerSimulator(n_qubits=100) # Bell state preparation sim.h(0) sim.cnot(0, 1) # GHZ state (100 qubits) sim.h(0) for i in range(99): sim.cnot(i, i+1) # Measurement outcome = sim.measure(0) print(f"Qubit 0 measured: {outcome}") # All measurements collapse to same outcome for GHZ all_outcomes = sim.measure_all() print(f"All qubits: {all_outcomes}") # All 0s or all 1s
- measure(qubit, rng=None)[source]#
Measure qubit in computational basis
- Returns:
0 or 1 (measurement outcome)
StabilizerState#
- class atlas_q.stabilizer_backend.StabilizerState(n_qubits, tableau)[source]#
Bases:
objectStabilizer state represented as a tableau
A stabilizer state on n qubits is represented by a (2n+1) × 2n binary matrix: - First n rows: X part of stabilizer generators - Next n rows: Z part of stabilizer generators - Last column: phase bits (±1)
Example for |0⟩: stabilizer is Z Example for |+⟩: stabilizer is X Example for Bell |Φ+⟩: stabilizers are XX and ZZ
Methods
copy()Create a copy of this state
init_plus(n_qubits)Initialize |++...+⟩ state (stabilized by X on each qubit)
init_zero(n_qubits)Initialize |00...0⟩ state (stabilized by Z on each qubit)
Low-level stabilizer state representation using tableau format.
Stores stabilizer and destabilizer generators with phase information. Provides operations for:
Gate application (updates tableau)
Measurement (Pauli measurements with outcome probabilities)
State verification (check if state is stabilizer state)
Internal representation:
stabilizers: (n, 2n) binary array [X part | Z part]destabilizers: (n, 2n) binary arrayphases: (2n,) phase array {0, 1, 2, 3} → {+1, +i, -1, -i}
Advanced Usage:
from atlas_q.stabilizer_backend import StabilizerState state = StabilizerState(n_qubits=10) # Apply gates by updating tableau state.apply_h(0) state.apply_cnot(0, 1) # Check stabilizer generators print("Stabilizers:", state.stabilizers) print("Phases:", state.phases) # Measure in Pauli basis outcome, prob = state.measure_pauli('Z', qubit=0)
HybridSimulator#
- class atlas_q.stabilizer_backend.HybridSimulator(n_qubits, use_stabilizer=True, chi_max=64, device='cuda')[source]#
Bases:
objectHybrid simulator that uses stabilizer backend for Clifford parts and switches to MPS when non-Clifford gates appear
- Usage:
sim = HybridSimulator(n_qubits=100, use_stabilizer=True) sim.h(0) sim.cnot(0, 1) sim.t(0) # Automatically switches to MPS here sim.measure_all()
Methods
cnot(control, target)CNOT gate
Get simulation statistics
h(qubit)Hadamard gate
measure(qubit)Measure qubit
s(qubit)Phase gate
swap(qubit1, qubit2)SWAP gate (Clifford)
t(qubit)T gate (non-Clifford!)
x(qubit)Pauli-X gate
z(qubit)Pauli-Z gate
Automatic hybrid backend switching between stabilizer and MPS.
Starts with stabilizer formalism for Clifford gates (20× speedup), then switches to MPS when non-Clifford gates (T, rotation gates) are encountered.
Strategy:
Use stabilizer backend while circuit is Clifford-only
Convert to MPS state when first non-Clifford gate appears
Continue with MPS for remaining gates
Report hybrid statistics (Clifford gate count, conversion point)
Constructor:
sim = HybridSimulator( n_qubits=50, bond_dim=128, # MPS bond dimension (used after conversion) device='cuda' )
- Parameters:
n_qubits(int): Number of qubitsbond_dim(int): Bond dimension for MPS backenddevice(str): ‘cpu’ or ‘cuda’
Example:
from atlas_q.stabilizer_backend import HybridSimulator sim = HybridSimulator(n_qubits=30, bond_dim=64, device='cuda') # Clifford gates: stabilizer backend (fast) sim.h(0) for i in range(29): sim.cnot(i, i+1) sim.s(10) sim.cz(5, 15) print(f"Backend: {sim.current_backend}") # 'stabilizer' # Non-Clifford gate: automatic conversion to MPS sim.t(0) # Triggers conversion print(f"Backend: {sim.current_backend}") # 'mps' print(f"Clifford gate count: {sim.clifford_gate_count}") # Continue with MPS sim.rx(1, 0.5) sim.measure_all(shots=1000)
Performance:
Circuit Type
Hybrid Backend
Pure MPS
100% Clifford
0.05s
1.0s (20×)
90% Clifford + 10% T
0.12s
1.0s (8×)
50% Clifford + 50% T
0.55s
1.0s (1.8×)
Functions#
is_clifford_gate#
- atlas_q.stabilizer_backend.is_clifford_gate(gate_name)[source]#
Check if a gate is in the Clifford group
Checks if a gate name or matrix is in the Clifford group.
- Parameters:
gate(str or np.ndarray): Gate name (‘H’, ‘S’, ‘CNOT’, ‘CZ’, etc.) or gate matrix
- Returns:
bool: True if gate is Clifford
Example:
from atlas_q.stabilizer_backend import is_clifford_gate
print(is_clifford_gate('H')) # True
print(is_clifford_gate('CNOT')) # True
print(is_clifford_gate('T')) # False
print(is_clifford_gate('RX')) # False
# Check custom matrix
import numpy as np
hadamard = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
print(is_clifford_gate(hadamard)) # True
Performance Characteristics#
Complexity Analysis#
Operation |
Stabilizer |
Statevector |
Speedup |
|---|---|---|---|
Time |
O(n²) |
O(2ⁿ) |
Exponential |
Space |
O(n²) |
O(2ⁿ) |
Exponential |
Gate (single) |
O(n) |
O(2ⁿ) |
2ⁿ/n |
Gate (two) |
O(n) |
O(2ⁿ) |
2ⁿ/n |
Measurement |
O(n²) |
O(2ⁿ) |
2ⁿ/n² |
Benchmark Results#
From scripts/benchmarks/validate_all_features.py:
# 50-qubit Clifford circuit (1000 gates)
Stabilizer: 0.08 sec
MPS (χ=128): 1.6 sec
Speedup: 20×
# 100-qubit Clifford circuit (2000 gates)
Stabilizer: 0.35 sec
MPS (χ=128): 7.2 sec
Speedup: 21×
# Memory usage (100 qubits)
Stabilizer: 0.02 MB
MPS (χ=64): 1.2 MB
Statevector: 32 PB (impossible)
Use Cases#
When to use stabilizer backend:
Clifford-only circuits: Error correction codes, syndrome extraction
Large-scale simulation: 100+ qubits where MPS bond dimension becomes prohibitive
Quantum error correction: Stabilizer codes (surface code, toric code)
Randomized benchmarking: Clifford group sampling
Graph state preparation: Cluster states for MBQC
When NOT to use:
Non-Clifford gates required (T, rotation gates) - use HybridSimulator instead
Arbitrary quantum states - use MPS for general-purpose simulation
Need approximate results - stabilizer is exact or fails
Examples#
Bell State Creation and Measurement#
from atlas_q.stabilizer_backend import StabilizerSimulator
sim = StabilizerSimulator(n_qubits=2)
# Create Bell state |Φ⁺⟩ = (|00⟩ + |11⟩)/√2
sim.h(0)
sim.cnot(0, 1)
# Measure both qubits (always same outcome)
results = []
for _ in range(100):
sim_copy = StabilizerSimulator(n_qubits=2)
sim_copy.h(0)
sim_copy.cnot(0, 1)
outcome0 = sim_copy.measure(0)
outcome1 = sim_copy.measure(1)
results.append((outcome0, outcome1))
print("Measurement outcomes:", results)
# Output: [(0, 0), (1, 1), (0, 0), (1, 1), ...] - always correlated
GHZ State (100 qubits)#
from atlas_q.stabilizer_backend import StabilizerSimulator
n_qubits = 100
sim = StabilizerSimulator(n_qubits=n_qubits)
# GHZ state: |0...0⟩ + |1...1⟩
sim.h(0)
for i in range(n_qubits - 1):
sim.cnot(i, i+1)
# Measure all qubits
outcomes = sim.measure_all()
print(f"All {n_qubits} qubits measured:", outcomes)
# Output: Either all 0s or all 1s (maximally entangled)
# Pauli expectation value
Z_exp = sim.expectation_pauli('Z' * n_qubits)
print(f"⟨Z⊗Z⊗...⊗Z⟩ = {Z_exp}") # 0.0 (equal superposition)
Quantum Error Correction (Bit Flip Code)#
from atlas_q.stabilizer_backend import StabilizerSimulator
# 3-qubit bit flip code: |ψ⟩ → |ψψψ⟩
sim = StabilizerSimulator(n_qubits=3)
# Encode: |ψ⟩|00⟩ → (α|000⟩ + β|111⟩)
# Assume |ψ⟩ = (|0⟩ + |1⟩)/√2
sim.h(0)
sim.cnot(0, 1)
sim.cnot(0, 2)
# Simulate bit flip error on qubit 1
sim.x(1)
# Syndrome measurement (stabilizers: Z₀Z₁, Z₁Z₂)
# Measure ancilla qubits to detect error
# ... (error correction logic)
# Decode: Correct error and recover |ψ⟩
Hybrid Simulation (Clifford + T gates)#
from atlas_q.stabilizer_backend import HybridSimulator
sim = HybridSimulator(n_qubits=20, bond_dim=64, device='cuda')
# Phase 1: Clifford gates (stabilizer backend)
sim.h(0)
for i in range(19):
sim.cnot(i, i+1)
sim.s(5)
sim.cz(3, 7)
print(f"Backend: {sim.current_backend}") # 'stabilizer'
print(f"Clifford gates: {sim.clifford_gate_count}")
# Phase 2: Add T gate (non-Clifford) → automatic switch to MPS
sim.t(10)
print(f"Backend: {sim.current_backend}") # 'mps'
# Phase 3: Continue with arbitrary gates
sim.rx(11, 0.7)
sim.ry(12, 1.2)
# Measure
outcomes = sim.measure_all(shots=1000)
print(f"Measurement histogram: {outcomes}")
Cross-References#
See Also#
Algorithms - Stabilizer formalism theory
How to Optimize Performance - Hybrid backend optimization
atlas_q.quantum_hybrid_system - Automatic backend selection
Advanced Features Tutorial - Hybrid simulation tutorial
References#
Key papers:
Gottesman, D. (1998). “The Heisenberg Representation of Quantum Computers.” arXiv:quant-ph/9807006
Aaronson, S. & Gottesman, D. (2004). “Improved Simulation of Stabilizer Circuits.” Physical Review A, 70(5), 052328.
Nielsen, M. A. & Chuang, I. L. (2010). “Quantum Computation and Quantum Information.” Cambridge University Press. Chapter 10.5.