Author: Matthew A. Combatti · Organization: Simulanics Technologies
License: MIT
QSM is a research-grade interpreter for a minimalist quantum scripting language (QSML) that lets you prototype quantum algorithms, open-system noise, qudit (d-level) logic, and oscillator (bosonic) algebras quickly. It emphasizes clarity and hackability over low-level optimization.
⚠️ Scope: This is a dense, explicit linear-algebra engine for education, ideation, and early-stage research—not a full-stack accelerator or hardware runtime.
- Qubits & Qudits:
qudit d,init(|0>,|1>,|+>,|->),initd(|k>, d),Xd(d),Zd(d),Id(d). - Oscillators (bosons):
oscillator N, ladder opsa(N),adag(N), numberNop(N),coherent(alpha, N), normal ordering for polynomials ina, a†with CCR. - Channels:
- Built-in qubit:
depolarize(p, n_qubits),amplitude_damp(gamma, n_qubits) - D-ary:
depolarizeD(p, D)(affine map) - Custom:
unitary(OP),kraus({K1, K2, ...})
- Built-in qubit:
- Algebra & Rewrites: Declare algebras and relations (e.g., CCR), and have expressions rewritten before evaluation.
- Measurements & Expectation:
measure(state, A),expect(state, A). - QEC Demo: Didactic [[5,1,3]] toy recovery mapping for single-Pauli errors.
Requires Python 3.9+.
pip install numpyPlace qsm.py in a folder. Optionally create a scripts/ directory for examples.
Run the REPL:
python qsm.pyRun a script:
python qsm.py ./scripts/demo_bell.qsmBatch (Bash):
PYTHON_BIN=python ./run_all_scripts.shBatch (Windows):
set PYTHON_BIN=python
run_all_scripts.bat// Set default d for Xd/Zd/Id
qudit 3
// Set oscillator cutoff N for a/adag/Nop and normal_order
oscillator 20
// Seed RNG for reproducible measurement sampling
seed 42
state s = init(|0>) // qubit |0>
state t = init(|+>) // qubit |+> = H|0>
state u = initd(|2>, 5) // qudit d=5 in |2>
state coh = coherent(1.2, 30) // bosonic coherent state |α=1.2>, cutoff=30
state s2 = s // copy state
psi = tensor(s, t) // ρ_psi = ρ_s ⊗ ρ_t
All states are represented internally as density matrices and auto-normalized.
Operators are matrices. QSM includes:
- Qubit single-qubit:
I,X,Y,Z,H,S,T, parametricRX(θ),RY(θ),RZ(θ). - Two-qubit:
CNOT(already 4×4; do not tensor an extraIdunless you truly want 3+ qubits). - Qudit:
Xd(d)(shift),Zd(d)(clock),Id(d); bareXd,Zd,Iduse currentqudit d. - Oscillator (cutoff N):
a(N),adag(N),Nop(N); barea,adag,Nopuse currentoscillator N.
Define operators:
operator U = H otimes Id // tensor product
operator R = RX(1.234) // parametric rotation
operator W = Xd(7) + 0.2 * Zd(7) // sums & scalar multiples
operator NO = normal_order(adag(20)*a(20)*adag(20)*a(20), 20)
Token note:
Iis a scalar 1 (promotion to identity occurs in sums). For tensor products, useId(orId(d)), notI.
-
Atoms: operator names or calls (
H,CNOT,RX(θ),Xd(5),a(20)), reals/complex scalars,I(scalar). -
Binary ops:
A * B(matrix product / scalar scaling)A + B,A - B(sum/difference; auto-promotes scalar to identity of matching dim)A ⊗ BorA otimes B(tensor product; both sides must be operators)
-
Parentheses for grouping.
Examples:
operator K = (RZ(0.3) * RX(0.7)) + 0.1 * Id
operator T3 = Xd(3) ⊗ Zd(3)
operator NO = normal_order(EXPR[, N])
- Input
EXPRmay containa(N),adag(N),I, scalars,+ - * ( ). - Uses CCR (
[a, adag] = I) to rewrite to normal-ordered form (alladagto the left ofa), then evaluates as a matrix at cutoffN. - If
Nomitted, inferred from the first ladder term or currentoscillator.
Declare a named algebra with generators and relations:
algebra Osc = { a, adag, I | CCR } // expands to a*adag = adag*a + I
algebra MyAlg = { A, B | comm(A,B) = 2*I } // A*B = B*A + 2*I
algebra Foo = { X, Y | X*Y = Y*X + Z, Z*X = X*Z } // general equalities
These relations are turned into directional rewrite rules and applied to operator expressions before evaluation.
Define channels:
chan UH = unitary(U) // U must be unitary
chan AD = amplitude_damp(0.1, 2) // qubit amplitude damping on first qubit of 2-qubit state
chan D1 = depolarize(0.2, 3) // qubit depolarizing on first qubit of 3-qubit register
chan Dany = depolarizeD(0.35, 7) // D-ary (dimension=7) depolarizing ρ ↦ (1-p)ρ + p I/D
// Custom dephasing via Kraus (example p=0.25):
operator I1 = Id
operator Z1 = Z
chan PD = kraus({ 0.8660254037844386 * I1, 0.5 * Z1 }) // sqrt(0.75), sqrt(0.25)
Apply channels:
psi = apply(psi, UH)
psi = apply(psi, D1)
Dimensions must match the state. For Kraus,
Σ K†K = Iis validated.
expect val = expect(psi, Z otimes Z) // prints [EXPECT] val := (x+yj)
r = measure(psi, Z) // Hermitian only; prints eigenvalue and probability
qrec = error_correct(q, [[5,1,3]]) // demo recovery for single-Pauli errors on 5-qubit register
// Build Bell state
seed 7
qudit 2
state a = init(|0>)
state b = init(|0>)
psi = tensor(a, b)
chan UH = unitary(H otimes Id)
psi = apply(psi, UH)
chan UCN = unitary(CNOT) // CNOT is 4x4 already
psi = apply(psi, UCN)
// Baseline correlations
operator XX = X otimes X
operator ZZ = Z otimes Z
expect xx0 = expect(psi, XX)
expect zz0 = expect(psi, ZZ)
// Noise on qubit-1 (first in register)
chan D1 = depolarize(0.2, 2)
psi = apply(psi, D1)
expect xx1 = expect(psi, XX)
expect zz1 = expect(psi, ZZ)
qudit 3
state s = initd(|1>, 3)
chan Dany = depolarizeD(0.35, 3)
s = apply(s, Dany)
expect z = expect(s, Zd(3))
oscillator 25
operator NO = normal_order(adag(25)*a(25)*adag(25)*a(25), 25)
state coh = coherent(0.8, 25)
expect v = expect(coh, NO)
Windows (run_all_scripts.bat):
Bash (run_all_scripts.sh):
-
“Tensor product requires operators, not bare scalars.” You used
I otimes .... UseId(orId(d)) for operator identity in tensors;Iis scalar 1. -
“Unitary dimension does not match state.” Check you didn’t write
CNOT otimes Idfor a 2-qubit state.CNOTis already 4×4. -
“Kraus set is not trace-preserving.” Verify
Σ K†K = I. Coefficients must be the square roots of probabilities.
- Sparse backends for larger Hilbert spaces.
- Native stabilizer simulator for Clifford circuits.
- Noise library expansion (phase damping, thermalization).
- Symbolic engine improvements (non-commutative term ordering ↔ evaluator).
MIT — see LICENSE.
Copyright © 2025 Matthew A. Combatti — Simulanics Technologies