Quantum circuit simulation for TypeScript · seven exact backends · 14 interchange formats · zero dependencies · 2,456 tests · v0.9.1
A general-purpose quantum circuit simulator for TypeScript and JavaScript. Seven backends, 14 import/export formats, and no dependencies — install it and a Bell state is three lines away.
npm install @kirkelliott/ketNode ≥ 22, or straight from a CDN in the browser. No Python, no build step, no toolchain.
Circuit. Compose and branch without defensive copying..d.ts bolted on.RangeError at construction, not 200 gates later.
You don't have to know which representation fits your circuit. simulate() inspects it and
routes to the cheapest exact backend. Same call, three very different circuits:
| Circuit | Qubits | Routed to | Time |
|---|---|---|---|
| GHZ-8, Clifford-only gates | 8 | clifford | 3ms |
| QFT-10, non-Clifford phases | 10 | statevector | 2ms |
| 40 qubits, non-Clifford phases | 40 | mps | 4ms |
Exact in every case — routing changes the cost, never the answer.
ket matches its representation to the circuit instead of committing to one, so the same API stays efficient across shapes that usually need different tools. Pick a backend explicitly when you want to.
| Backend | Call | Memory | Best for | Measured |
|---|---|---|---|---|
| Statevector | run() | sparse → dense, automatic | Exact amplitudes to ~20 qubits | GHZ-20, 1024 shots — <1ms |
| MPS / tensor | runMps() | O(n·χ²), χ grows on demand | Low-entanglement circuits, 50+ qubits | GHZ-127, 1024 shots — 10ms |
| Density matrix | dm() | sparse → dense, automatic | Mixed states and exact noise channels | GHZ-8 + noise — <1ms |
| Clifford | runClifford() | O(n²) tableau | Clifford circuits, QEC thresholds | GHZ-1024 — 4ms evolve + 57ms/shot |
| Stabilizer rank | runStabilizerRank() | O(20.228t·n²/8) | Clifford+T beyond statevector width | 100q, 50 T — 5.6s build + 43ms/shot |
| Tensor network | amplitudeByContraction() | O(2width), width = treewidth not n | Wide, shallow circuits at any qubit count | 400q depth 4 — 362ms |
| Pauli path | pauliPathExpectation() | O(terms), set by the light cone | ⟨O⟩ without ever building a state | 120q QAOA layer — 28ms |
The statevector starts as a sparse map and promotes itself to a flat Float64Array past 1/64 fill.
MPS bond dimension χ is exact by default and grows on demand — maxBond is an initial
allocation, not a cap.
Backends built on unrelated representations — amplitudes, density matrices, tensor trains, stabilizer tableaux — have to agree. Where a circuit can be expressed in several, ket checks that they do, and the suite is built on analytic expectations rather than snapshots. These numbers are computed while this page builds:
| Outcome | Statevector (exact) | Density matrix | MPS | Clifford |
|---|---|---|---|---|
|000000⟩ | 0.500000 | 0.500000 | 0.499870 | 0.499870 |
|111111⟩ | 0.500000 | 0.500000 | 0.500130 | 0.500130 |
The density matrix backend is exact, so it matches to floating-point precision. MPS and Clifford sample, so they land within shot noise of the analytic answer — which is the correct result, not a tolerance that was widened until it passed.
Every circuit can hand you its full 2ⁿ×2ⁿ unitary via circuitMatrix(). For a five-gate circuit mixing Clifford and non-Clifford rotations, U†U − I is:
Machine epsilon. The suite asserts this for every gate in the library, not just this one circuit.
A serious simulator tells you when its answer is approximate. MPS runs set
truncated the moment a physically significant singular value is discarded, and
report the peak bond dimension actually reached:
Exact by default. Approximation is something you opt into with truncErr or
maxChi, and it is always flagged.
A simulator you can't get circuits into or out of is a dead end. ket reads and writes the formats the field actually uses, with round-trips covered by the test suite for every one of them.
Not an oracle mock-up. The full Beauregard circuit, modular exponentiation compiled down to primitive gates, simulated exactly — then the period recovered from phase estimation by continued fractions.
| r.factors | [5n, 3n] |
| r.period | 4n |
| r.method | 'quantum' |
| r.qubits | 19 |
At these sizes classical shortcuts hit often — an even N, or a base sharing a factor with N,
falls to gcd with no circuit at all. method reports which path ran, so a
demo can prove the quantum one did.
This does not reach cryptographic sizes — no classical simulator does. What you get is the real circuit, exactly simulated, at sizes you can inspect.
IonQ's native gate set is built in, not bolted on. gpi, gpi2, ms,
and vz are first-class gates you can write directly, compile(device) transpiles
an abstract circuit into them, and toIonQ() emits submit-ready JSON.
A textbook Bell pair, rewritten into what the trap actually executes — GPI2 and MS, with virtual-Z frame rotations that cost nothing on hardware.
toIonQ() produces the API payload directly — no adapter, no Python bridge.
The client uses the global fetch, so submission adds no dependency. Algorithm circuits pass through toIonQBasis() first — QFT and modular-exponentiation circuits are built almost entirely from cu1, which IonQ has no direct representation for.
Run on IonQ's simulator, this factors 15: the counting register returns 0, 2, 4 and 6 at 25 % each — period 4, giving 15 = 3 × 5 — with the work register on the orbit of 7 mod 15. The discrete-logarithm circuit recovers its exponent the same way.
Devices currently accepting jobs, so a noisy run is one option away — run({ noise: 'forte-1' }). The Source column is load-bearing: vendor-published means IonQ’s own r_1q/r_2q parameters, estimated means it is not their figure. Every readout rate is estimated — no vendor here publishes one — and Forte’s post-2025-09 model is unpublished, so these are the earlier parameters. Regenerate with scripts/refresh-ionq-devices.ts.
| Device | Qubits | Native gates | 1Q error | 2Q error | Readout | Source |
|---|---|---|---|---|---|---|
| forte-1 forte-enterprise-1 — same figures | 36 | gpi, gpi2, zz | 0.027% | 0.49% | 0.20% | vendor-published enterprise: estimated |
No screenshots and no mock data — every chart, circuit diagram, and number on the rest of this page is produced by executing ket when the page is generated.
A 4-cycle graph has two optimal bipartitions: {0,2} vs {1,3} and {1,3} vs {0,2}, each cutting all 4 edges. QAOA p=1 finds them — the two dominant peaks are the correct answers.
toSVG() · blochSphere(q)Self-contained SVG string — no external fonts or stylesheets. Same column layout as draw(). Embed directly in HTML, save to a file, or use in notebooks.
300×300 SVG for a single qubit's state using cavalier projection. Partial-traces over all other qubits — works on any circuit, including entangled states.
The same 3-qubit Grover circuit (target: |101⟩), noiseless and under IonQ Forte-1’s published error rates, 4,096 shots each. Bars are all eight basis states; the labelled one is the search target. A depolarizing model from IonQ’s published rates — not a hardware run.
A 1,024-qubit GHZ state built with one H gate + 1,023 CNOTs.
Classically infeasible with statevector simulation (21024 amplitudes),
but the Gottesman-Knill theorem lets us track it exactly in O(n²) space.
Each row is a stabilizer generator; each column is a qubit.
Row 0 (bright stripe): X⊗¹⁰²⁴ — all 1,024 qubits correlated via X. ·
Rows 1–1023 (diagonal): ZZ at adjacent qubit positions.
O(n²) per gate — statevector needs 2n complex numbers, impossible beyond ~50 qubits.
| Qubits | Time | Statevector RAM |
|---|---|---|
| 64 | <1ms | >16 PB |
| 128 | <1ms | > universe |
| 256 | 1ms | > universe |
| 512 | 1ms | > universe |
| 1,024 | 7ms | > universe |
| 2,048 | 25ms | > universe |
| 4,096 | 117ms | > universe |
Every qubit collapses to the same value — perfect quantum correlation across all 1,024 qubits.
The trajectory-MPS backend simulates noisy circuits at 127+ qubits by representing the quantum state
as a product of rank-3 tensors. Bond dimension χ tracks entanglement, not qubit count —
a 127-qubit GHZ state maintains χ = 2 throughout, making it as cheap as a 3-qubit simulation.
Noisy trajectories are embarrassingly parallel: pass workers: N for N× speedup.
How often an n-qubit GHZ run finishes with no gate error at all. The circuit is
one H and n − 1 CNOTs, and each gate survives with probability 1 − p, so the
curve is that product as n grows. At n = 127 roughly half of all runs come back clean.
Landing on one of the two ideal answers is rarer still — 46.9 % ± 1.5 % over
4,096 shots — because an error usually moves the result off |0…0⟩ and |1…1⟩ rather than back onto them.
All of this is the hardware, not the simulator: bond dimension stays at χ = 2 for every n,
so ket adds no approximation of its own. Forte-1 holds 36 qubits, so past n = 36 the curve
extrapolates its published error rates to a machine that does not exist.
Statevector simulation of a 127-qubit circuit would require 2127 complex amplitudes — roughly 1020× more RAM than all data ever created. MPS stores O(n · χ²) numbers — linear in qubit count.
256 shots, IonQ Forte-1 error rates as the depolarizing model, single-threaded. Time scales linearly with n because χ = 2 throughout the GHZ circuit.
| Qubits | Time | χ | Statevector RAM |
|---|---|---|---|
| 10 | 3ms | 2 | 16 KB |
| 20 | 2ms | 2 | 16 MB |
| 50 | 4ms | 2 | >16 PB |
| 100 | 9ms | 2 | > universe |
| 127 | 10ms | 2 | > universe |
Each noisy trajectory is an independent simulation — distribute across OS threads with workers: N. Requires built bundle and Node.js ≥ 22. Measured at 3.8× with 4 workers and 7.1× with 8 on a 16-core machine (50 qubits, 32k shots).
Variational quantum eigensolver on the 12-qubit antiferromagnetic Heisenberg chain (H = Σ XiXi+1 + YiYi+1 + ZiZi+1) using MPS-based parameter-shift gradients. A 12-qubit statevector needs 128 KB — manageable — but 50+ qubits needs petabytes. MPS VQE scales to hundreds of qubits at low entanglement.
Parameter-shift gradients computed via MPS — same accuracy as statevector but O(n·χ²) memory. Each step: 2 × 36 MPS evaluations.
Bond entropies of the optimized ground state — the area-law profile confirms the Heisenberg chain has limited entanglement, making MPS the ideal representation.