merlin-sim is a simulator for non-Clifford quantum error correction circuits. It supports circuits that can be decomposed into CNOT, T, Paulis, Z-measurements and certain state-dependent compatible X-measurements. It keeps track of a representation of the state and can sample computational basis states, as well as compute expectation values and amplitudes, all in polynomial time in the circuit depth and size. It is based on the paper Polynomial-time simulation of non-Clifford quantum error correction by Serban Cercelescu, Mark Koch and Arthur Pesah.
The documentation is available here.
Install merlin by running
pip install merlin-simThe Simulator class can be used to interactively apply gates and measurements:
from merlin_sim import Simulator
sim = Simulator(seed=1234)
sim.reset_x(0, 1)
sim.t(0)
sim.t_dag(1)
sim.cnot(0, 1)
result = sim.measure(0)
p_true = sim.peek_z_probability(1)
parity = sim.measure_observable("-XX", flip_probability=0.001)Note that merlin only supports certain kinds of X measurements such as flag or gauge measurements.
For unsupported measurements, the simulator will raise a MeasurementError:
# Measuring CCZ|+++> in X is not supported
sim = Simulator()
sim.reset_x(0, 1, 2)
sim.ccz(0, 1, 2)
sim.measure_x(0)Output:
MeasurementError: X measurement on qubit 0 is incompatible: non-affine zero set
The CircuitSampler class can be used to sample from circuits in an extended stim-like format:
from merlin_sim import CircuitSampler
sampler = CircuitSampler("""
RX 0
T 0
EXP_VAL X0 Z0
MX 0
R 0
X_ERROR(0.01) 0
M 0
DETECTOR rec[-1]
OBSERVABLE_INCLUDE(0) rec[-1]
""", seed=1234, postselect=[0])
result = sampler.sample(1000)
print(result.measurements)
print(result.exp_vals)
print(result.detectors)
print(result.observables)The full circuit specification is available on the documentation page.
The simulation core is also a standalone Rust library:
use merlin_sim::{
Circuit, CircuitSampler, Simulator, StateVectorEndian,
};
let mut sim = Simulator::with_seed(1234);
sim.reset_x(0);
sim.t(0);
assert_eq!(sim.state_vector(StateVectorEndian::Little).len(), 2);
let circuit = Circuit::parse("RX 0\nT 0\nMX 0");
let mut sampler = CircuitSampler::from_circuit_with_seed(&circuit, 1234)?;
let samples = sampler.sample(100)?;
assert_eq!(samples.measurements.len(), 100 * circuit.num_measurements());
let detector_circuit = Circuit::parse("M 0\nDETECTOR rec[-1]");
let mut detector_sampler = CircuitSampler::from_circuit_with_seed(&detector_circuit, 1234)?;
let detector_samples = detector_sampler.sample(100)?;
assert_eq!(detector_samples.accepted_shots, 100);
# Ok::<(), merlin_sim::MeasurementError>(())