@kirkelliott/kdfts 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kirk Elliott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # kdfts
2
+
3
+ [![CI](https://github.com/dmvjs/kdfts/actions/workflows/ci.yml/badge.svg)](https://github.com/dmvjs/kdfts/actions/workflows/ci.yml)
4
+ [![license](https://img.shields.io/github/license/dmvjs/kdfts)](LICENSE)
5
+
6
+ If you're storing passwords or deriving secrets that must survive a server compromise, the salt is what attackers try to precompute. A stolen PRNG seed, a weak entropy source, or a future algorithm break can reconstruct every salt you've ever generated. `kdfts` removes that attack surface by sourcing salt from the [ANU Quantum Random Number Generator](https://quantumnumbers.anu.edu.au) — photon shot noise at a beam splitter. Those bytes were never produced by an algorithm. There is no state to steal.
7
+
8
+ Falls back silently to `crypto.getRandomValues()` when the ANU API is unavailable.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @kirkelliott/kdfts
14
+ ```
15
+
16
+ Get a free ANU API key at [quantumnumbers.anu.edu.au](https://quantumnumbers.anu.edu.au):
17
+
18
+ ```bash
19
+ export ANU_API_KEY=your-key-here
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ **Derive and persist:**
25
+
26
+ ```ts
27
+ import { derive, formatCert } from '@kirkelliott/kdfts'
28
+
29
+ const { key, salt, certificate } = await derive('my-password', {
30
+ context: 'myapp:user42:session', // domain separation — optional but recommended
31
+ })
32
+
33
+ // Persist these — both are needed to verify later
34
+ const persistedSalt = salt.toString('hex')
35
+ const persistedKey = key.toString('hex')
36
+
37
+ console.log(formatCert(certificate))
38
+ // ════════════════════════════════════════════════════
39
+ // kdfts Key Certificate
40
+ // ════════════════════════════════════════════════════
41
+ // Source: ANU Quantum Vacuum (photon shot noise)
42
+ // Timestamp: 2026-03-07T04:00:00.000Z
43
+ // KDF: scrypt
44
+ // N / r / p: 16384 / 8 / 1
45
+ // Key length: 32 bytes (256 bits)
46
+ // Salt bytes: 32 bytes (256 bits)
47
+ // Context: myapp:user42:session
48
+ //
49
+ // Salt hash: 7bcaf7d45ef1191f9d72556b5f7f6e17...
50
+ // Key hash: f0d6b878bb864d5261d5ccd05353bfc1...
51
+ // ════════════════════════════════════════════════════
52
+ ```
53
+
54
+ **Verify later:**
55
+
56
+ ```ts
57
+ import { verify } from '@kirkelliott/kdfts'
58
+
59
+ const salt = Buffer.from(persistedSalt, 'hex')
60
+ const key = Buffer.from(persistedKey, 'hex')
61
+
62
+ const valid = await verify('my-password', salt, key, {
63
+ context: 'myapp:user42:session',
64
+ })
65
+ ```
66
+
67
+ **Reuse a source across multiple derivations** (saves one ANU round-trip):
68
+
69
+ ```ts
70
+ import { QuantumSource, derive } from '@kirkelliott/kdfts'
71
+
72
+ const source = await QuantumSource.create()
73
+ console.log(source.source) // 'anu' | 'crypto'
74
+
75
+ const [keyA, keyB] = await Promise.all([
76
+ derive(passwordA, { source }),
77
+ derive(passwordB, { source }),
78
+ ])
79
+ ```
80
+
81
+ ## Options
82
+
83
+ | Option | Type | Default | Description |
84
+ |---|---|---|---|
85
+ | `context` | `string` | — | Domain separation string. Mixed in as `password \|\| 0x00 \|\| context`. |
86
+ | `saltBytes` | `number` | `32` | Bytes of quantum entropy to fetch. |
87
+ | `keyLength` | `number` | `32` | Output key length in bytes. |
88
+ | `cost` | `{ N, r, p }` | `{ N: 16384, r: 8, p: 1 }` | scrypt parameters. Increase `N` for higher-value keys. |
89
+ | `source` | `QuantumSource` | — | Pre-created source for reuse or testing. |
90
+
91
+ ## Security notes
92
+
93
+ - Salt is fetched fresh for every `derive` call — never reused
94
+ - Context is domain-separated with a null byte: `password || 0x00 || context`
95
+ - Verification uses `crypto.timingSafeEqual` — no timing oracle on key comparison
96
+ - scrypt defaults (`N=16384, r=8, p=1`) follow [OWASP recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
97
+ - The certificate records only SHA-256 hashes of the salt and key, never the raw values
98
+ - Every claim in this README is validated by the [test suite](https://github.com/dmvjs/kdfts/actions/workflows/ci.yml)
99
+
100
+ ## License
101
+
102
+ MIT © Kirk Elliott
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runBenchmarks = runBenchmarks;
37
+ /**
38
+ * KwantumScrypt Boundary Tests
39
+ *
40
+ * These tests push the limits of the system and prove — with exact numbers —
41
+ * where and why things fail. Failures are not bugs; they are fundamental.
42
+ */
43
+ const circuit_1 = require("../lang/circuit");
44
+ const state_1 = require("../core/state");
45
+ const line = '─'.repeat(60);
46
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
47
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
48
+ const grn = (s) => `\x1b[32m${s}\x1b[0m`;
49
+ const ylw = (s) => `\x1b[33m${s}\x1b[0m`;
50
+ function fmt(bytes) {
51
+ if (bytes < 1024)
52
+ return `${bytes} B`;
53
+ if (bytes < 1024 ** 2)
54
+ return `${(bytes / 1024).toFixed(1)} KB`;
55
+ if (bytes < 1024 ** 3)
56
+ return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
57
+ return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
58
+ }
59
+ // ── Bell State Fidelity ────────────────────────────────────────────────────
60
+ //
61
+ // Prepares |Φ+⟩ = (|00⟩ + |11⟩)/√2, measures N shots.
62
+ // Ideal: exactly 50% |00⟩, 50% |11⟩.
63
+ // We show statistical deviation from ideal across shot counts.
64
+ //
65
+ async function testBellFidelity(ks) {
66
+ console.log(bold('\n[1] Bell State Fidelity — (|00⟩ + |11⟩)/√2'));
67
+ console.log(' Ideal: P(00) = P(11) = 0.5000');
68
+ console.log(line);
69
+ for (const shots of [10, 100, 1000, 10_000]) {
70
+ ks.reset().h(0).cx(0, 1);
71
+ const r = await ks.run(shots);
72
+ const p00 = (r.counts['00'] ?? 0) / shots;
73
+ const p11 = (r.counts['11'] ?? 0) / shots;
74
+ const deviation = Math.abs(p00 - 0.5);
75
+ const status = deviation < 0.05 ? grn('PASS') : ylw('DRIFT');
76
+ console.log(` shots=${shots.toString().padStart(6)} P(00)=${p00.toFixed(4)} P(11)=${p11.toFixed(4)} Δ=${deviation.toFixed(4)} ${status}`);
77
+ }
78
+ }
79
+ // ── GHZ State Scaling ──────────────────────────────────────────────────────
80
+ //
81
+ // GHZ_n = (|000...0⟩ + |111...1⟩)/√2 across n qubits.
82
+ // Tests entanglement fidelity as we scale up.
83
+ // Ideal: 50% all-zeros, 50% all-ones. Everything else = decoherence.
84
+ //
85
+ async function testGHZScaling() {
86
+ console.log(bold('\n[2] GHZ State Scaling — (|0...0⟩ + |1...1⟩)/√2'));
87
+ console.log(' Ideal: P(0...0) = P(1...1) = 0.5, all others = 0');
88
+ console.log(line);
89
+ const shots = 1000;
90
+ for (const n of [2, 3, 4, 5, 6, 8, 10, 12, 16, 20]) {
91
+ const ks = await circuit_1.KwantumScrypt.create(n);
92
+ ks.h(0);
93
+ for (let i = 1; i < n; i++)
94
+ ks.cx(0, i);
95
+ const r = await ks.run(shots);
96
+ const allZeros = '0'.repeat(n);
97
+ const allOnes = '1'.repeat(n);
98
+ const p0 = (r.counts[allZeros] ?? 0) / shots;
99
+ const p1 = (r.counts[allOnes] ?? 0) / shots;
100
+ const leaked = 1 - p0 - p1; // probability in unwanted states
101
+ const mem = fmt(new state_1.StateVector(n).memoryBytes());
102
+ const status = leaked < 0.01 ? grn('IDEAL') : leaked < 0.1 ? ylw('DRIFT') : red('LEAKING');
103
+ console.log(` n=${n.toString().padStart(2)} P(0s)=${p0.toFixed(3)} P(1s)=${p1.toFixed(3)} leak=${leaked.toFixed(3)} mem=${mem.padStart(8)} ${status}`);
104
+ }
105
+ }
106
+ // ── Memory Wall ────────────────────────────────────────────────────────────
107
+ //
108
+ // State vector size = 2^n × 16 bytes (two float64s per complex number).
109
+ // This is the fundamental reason classical quantum simulation is hard.
110
+ // At ~26–28 qubits we hit RAM limits on most machines.
111
+ //
112
+ function testMemoryWall() {
113
+ console.log(bold('\n[3] Memory Wall — State Vector Size = 2^n × 16 bytes'));
114
+ console.log(' This is WHY quantum computers can\'t be classically simulated');
115
+ console.log(line);
116
+ for (const n of [1, 2, 4, 8, 10, 12, 16, 20, 24, 26, 28, 30, 32]) {
117
+ const bytes = (1 << Math.min(n, 30)) * 2 * 8;
118
+ const feasible = n <= 28;
119
+ const status = n <= 20 ? grn('feasible') : n <= 26 ? ylw('slow/heavy') : red('FAILS — OOM');
120
+ console.log(` n=${n.toString().padStart(2)} states=${(Math.pow(2, n)).toExponential(2).padStart(10)} RAM=${fmt(bytes).padStart(10)} ${status}`);
121
+ }
122
+ console.log();
123
+ console.log(' ' + red('BOUNDARY: Classical simulation breaks at ~26–30 qubits.'));
124
+ console.log(' ' + red('IonQ Aria (25q) and Forte (36q) operate beyond this wall.'));
125
+ console.log(' ' + red('This is why real quantum hardware has value.'));
126
+ }
127
+ // ── Circuit Depth & Decoherence Simulation ────────────────────────────────
128
+ //
129
+ // In a perfect simulator, fidelity never degrades.
130
+ // But we can MODEL decoherence by applying small noise after each gate.
131
+ // This demonstrates what happens on real hardware (IonQ, IBM, etc.).
132
+ //
133
+ async function testDepthDecoherence() {
134
+ console.log(bold('\n[4] Circuit Depth — Decoherence Simulation'));
135
+ console.log(' Applying small depolarizing noise ε per gate layer');
136
+ console.log(' Simulates T2 coherence loss on real hardware');
137
+ console.log(line);
138
+ const n = 3;
139
+ const shots = 500;
140
+ const noise = 0.01; // 1% depolarizing noise per layer
141
+ for (const depth of [1, 2, 4, 8, 16, 32, 64]) {
142
+ const ks = await circuit_1.KwantumScrypt.create(n);
143
+ // Build a circuit of `depth` layers of H + CX
144
+ ks.h(0);
145
+ for (let d = 0; d < depth; d++) {
146
+ for (let i = 0; i < n - 1; i++)
147
+ ks.cx(i, i + 1);
148
+ for (let i = 0; i < n; i++)
149
+ ks.h(i);
150
+ }
151
+ ks.measure();
152
+ const state = ks.inspect();
153
+ const probs = state.probabilities();
154
+ // Simulate decoherence: after `depth` layers, amplitudes decay
155
+ // Real metric: entropy of the probability distribution
156
+ // Higher entropy = more mixed = more decoherence
157
+ const entropy = -probs.reduce((s, p) => p > 0 ? s + p * Math.log2(p) : s, 0);
158
+ const maxEntropy = n; // fully mixed state has entropy = n bits
159
+ const decoherence = entropy / maxEntropy;
160
+ // Apply noise model: each layer depolarizes by (1-ε)^depth
161
+ const noiseDecay = Math.pow(1 - noise, depth);
162
+ const modelFidelity = 0.5 + 0.5 * noiseDecay; // fidelity vs ideal state
163
+ const status = decoherence < 0.6 ? grn('coherent') : decoherence < 0.9 ? ylw('degrading') : red('fully mixed');
164
+ console.log(` depth=${depth.toString().padStart(3)} entropy=${entropy.toFixed(3)}/${maxEntropy} model_fidelity=${modelFidelity.toFixed(3)} ${status}`);
165
+ }
166
+ console.log();
167
+ console.log(` Noise model: ${noise * 100}% depolarizing per gate layer`);
168
+ console.log(' ' + red('On real IonQ hardware: T2 ≈ 1–10ms, ~100 gates before coherence loss'));
169
+ }
170
+ // ── KSIF Round-trip ───────────────────────────────────────────────────────
171
+ //
172
+ // Encode a circuit as binary bitstrings, decode it, verify it's identical.
173
+ //
174
+ async function testKSIFEncoding() {
175
+ console.log(bold('\n[5] KSIF Binary Encoding — Circuit ↔ Bitstring Round-trip'));
176
+ console.log(' Programs expressed as quantum bitstrings');
177
+ console.log(line);
178
+ const { decode } = await Promise.resolve().then(() => __importStar(require('../lang/encoder')));
179
+ const { CryptoQRNG } = await Promise.resolve().then(() => __importStar(require('../qrng/anu')));
180
+ const ks = circuit_1.KwantumScrypt.withQRNG(3, new CryptoQRNG(), 'crypto');
181
+ ks.h(0).cx(0, 1).cx(0, 2).rx(Math.PI / 4, 1).measure();
182
+ const ksif = ks.toKSIF();
183
+ console.log('\n Circuit encoded as KSIF bitstrings:');
184
+ ksif.split('\n').forEach(line => console.log(` ${line}`));
185
+ // Round-trip decode
186
+ const decoded = decode(ksif);
187
+ const reKs = circuit_1.KwantumScrypt.fromKSIF(ksif, 3, new CryptoQRNG(), 'crypto');
188
+ const r = await reKs.run(100);
189
+ console.log('\n GHZ state superposition before measurement:');
190
+ Object.entries(r.superposition)
191
+ .sort((a, b) => b[1] - a[1])
192
+ .forEach(([bits, p]) => console.log(` |${bits}⟩ ${(p * 100).toFixed(2)}%`));
193
+ console.log(`\n Decoded ${decoded.length} ops. Sample result: |${r.bitstring}⟩ ${grn('✓')}`);
194
+ }
195
+ // ── Quantum Notation Test ─────────────────────────────────────────────────
196
+ async function testQuantumNotation() {
197
+ console.log(bold('\n[6] Quantum Notation DSL — ks.bs("H|0⟩")'));
198
+ console.log(line);
199
+ const { CryptoQRNG } = await Promise.resolve().then(() => __importStar(require('../qrng/anu')));
200
+ const ks = circuit_1.KwantumScrypt.withQRNG(3, new CryptoQRNG(), 'crypto');
201
+ // Build Bell state using notation
202
+ ks.bs('H|0⟩')
203
+ .bs('CX|0→1⟩')
204
+ .bs('CX|0→2⟩')
205
+ .bs('M|*⟩');
206
+ const r = await ks.run(200);
207
+ console.log('\n Circuit: H|0⟩ → CX|0→1⟩ → CX|0→2⟩ → M|*⟩');
208
+ console.log(' GHZ state |000⟩ + |111⟩:');
209
+ Object.entries(r.counts)
210
+ .sort((a, b) => b[1] - a[1])
211
+ .forEach(([bits, count]) => {
212
+ const bar = '█'.repeat(Math.round(count / 200 * 30));
213
+ console.log(` |${bits}⟩ ${count.toString().padStart(4)} shots ${bar}`);
214
+ });
215
+ }
216
+ // ── Main ──────────────────────────────────────────────────────────────────
217
+ async function runBenchmarks() {
218
+ console.log(bold('\n╔══════════════════════════════════════════════════════════╗'));
219
+ console.log(bold('║ KwantumScrypt — Boundary Tests ║'));
220
+ console.log(bold('╚══════════════════════════════════════════════════════════╝'));
221
+ const ks2 = await circuit_1.KwantumScrypt.create(2);
222
+ const source = bold(ks2['qrngSource'] === 'anu' ? grn('ANU quantum vacuum') : ylw('crypto.getRandomValues'));
223
+ console.log(`\nRandom source: ${source}`);
224
+ await testBellFidelity(ks2);
225
+ await testGHZScaling();
226
+ testMemoryWall();
227
+ await testDepthDecoherence();
228
+ await testKSIFEncoding();
229
+ await testQuantumNotation();
230
+ console.log(bold('\n' + line));
231
+ console.log('Done.\n');
232
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Complex = void 0;
4
+ /** Immutable complex number */
5
+ class Complex {
6
+ re;
7
+ im;
8
+ constructor(re, im = 0) {
9
+ this.re = re;
10
+ this.im = im;
11
+ }
12
+ add(b) { return new Complex(this.re + b.re, this.im + b.im); }
13
+ sub(b) { return new Complex(this.re - b.re, this.im - b.im); }
14
+ mul(b) {
15
+ return new Complex(this.re * b.re - this.im * b.im, this.re * b.im + this.im * b.re);
16
+ }
17
+ scale(s) { return new Complex(this.re * s, this.im * s); }
18
+ conj() { return new Complex(this.re, -this.im); }
19
+ abs2() { return this.re * this.re + this.im * this.im; }
20
+ abs() { return Math.sqrt(this.abs2()); }
21
+ toString() {
22
+ if (Math.abs(this.im) < 1e-10)
23
+ return this.re.toFixed(6);
24
+ if (Math.abs(this.re) < 1e-10)
25
+ return `${this.im.toFixed(6)}i`;
26
+ const sign = this.im >= 0 ? '+' : '';
27
+ return `${this.re.toFixed(6)}${sign}${this.im.toFixed(6)}i`;
28
+ }
29
+ static ZERO = new Complex(0, 0);
30
+ static ONE = new Complex(1, 0);
31
+ static I = new Complex(0, 1);
32
+ /** e^(iθ) */
33
+ static phase(theta) {
34
+ return new Complex(Math.cos(theta), Math.sin(theta));
35
+ }
36
+ }
37
+ exports.Complex = Complex;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GATE_MATRICES = void 0;
4
+ exports.rx = rx;
5
+ exports.ry = ry;
6
+ exports.rz = rz;
7
+ exports.applySingleQubit = applySingleQubit;
8
+ exports.applyCX = applyCX;
9
+ exports.applySWAP = applySWAP;
10
+ exports.applyXX = applyXX;
11
+ exports.applyGPi = applyGPi;
12
+ exports.applyGPi2 = applyGPi2;
13
+ const complex_1 = require("./complex");
14
+ const { ZERO: Z, ONE: O, I } = complex_1.Complex;
15
+ /** Standard gate matrices */
16
+ exports.GATE_MATRICES = {
17
+ H: [[O.scale(Math.SQRT1_2), O.scale(Math.SQRT1_2)],
18
+ [O.scale(Math.SQRT1_2), O.scale(-Math.SQRT1_2)]],
19
+ X: [[Z, O], [O, Z]],
20
+ Y: [[Z, I.scale(-1)], [I, Z]],
21
+ Z: [[O, Z], [Z, O.scale(-1)]],
22
+ S: [[O, Z], [Z, new complex_1.Complex(0, 1)]],
23
+ SI: [[O, Z], [Z, new complex_1.Complex(0, -1)]],
24
+ T: [[O, Z], [Z, complex_1.Complex.phase(Math.PI / 4)]],
25
+ TI: [[O, Z], [Z, complex_1.Complex.phase(-Math.PI / 4)]],
26
+ };
27
+ /** Rotation gate matrices */
28
+ function rx(theta) {
29
+ const c = new complex_1.Complex(Math.cos(theta / 2));
30
+ const s = new complex_1.Complex(0, -Math.sin(theta / 2));
31
+ return [[c, s], [s, c]];
32
+ }
33
+ function ry(theta) {
34
+ const c = new complex_1.Complex(Math.cos(theta / 2));
35
+ const sp = new complex_1.Complex(Math.sin(theta / 2));
36
+ const sn = new complex_1.Complex(-Math.sin(theta / 2));
37
+ return [[c, sn], [sp, c]];
38
+ }
39
+ function rz(theta) {
40
+ return [
41
+ [complex_1.Complex.phase(-theta / 2), Z],
42
+ [Z, complex_1.Complex.phase(theta / 2)]
43
+ ];
44
+ }
45
+ /**
46
+ * Apply a single-qubit gate to `target` qubit.
47
+ * Modifies state in-place.
48
+ * Complexity: O(2^n)
49
+ */
50
+ function applySingleQubit(state, target, mat) {
51
+ const size = state.size;
52
+ for (let i = 0; i < size; i++) {
53
+ // Only process pairs where bit `target` = 0
54
+ if ((i >> target) & 1)
55
+ continue;
56
+ const j = i | (1 << target);
57
+ const a = state.amp[i];
58
+ const b = state.amp[j];
59
+ state.amp[i] = mat[0][0].mul(a).add(mat[0][1].mul(b));
60
+ state.amp[j] = mat[1][0].mul(a).add(mat[1][1].mul(b));
61
+ }
62
+ }
63
+ /**
64
+ * Apply CNOT (CX) gate.
65
+ * Flips `target` when `control` = 1.
66
+ * Modifies state in-place.
67
+ */
68
+ function applyCX(state, control, target) {
69
+ const size = state.size;
70
+ for (let i = 0; i < size; i++) {
71
+ if (!((i >> control) & 1))
72
+ continue; // control must be 1
73
+ if ((i >> target) & 1)
74
+ continue; // target must be 0
75
+ const j = i | (1 << target);
76
+ const tmp = state.amp[i];
77
+ state.amp[i] = state.amp[j];
78
+ state.amp[j] = tmp;
79
+ }
80
+ }
81
+ /**
82
+ * Apply SWAP gate.
83
+ * Modifies state in-place.
84
+ */
85
+ function applySWAP(state, a, b) {
86
+ const size = state.size;
87
+ for (let i = 0; i < size; i++) {
88
+ const bitA = (i >> a) & 1;
89
+ const bitB = (i >> b) & 1;
90
+ if (bitA === bitB)
91
+ continue;
92
+ // Swap only when bits differ and bitA=0, bitB=1 (process each pair once)
93
+ if (bitA !== 0)
94
+ continue;
95
+ const j = (i ^ (1 << a)) | (1 << b);
96
+ const tmp = state.amp[i];
97
+ state.amp[i] = state.amp[j];
98
+ state.amp[j] = tmp;
99
+ }
100
+ }
101
+ /**
102
+ * Mølmer–Sørensen XX(θ) gate — IonQ's native 2-qubit gate.
103
+ * XX(θ) = exp(-i θ X⊗X / 2)
104
+ */
105
+ function applyXX(state, a, b, theta) {
106
+ const c = Math.cos(theta / 2);
107
+ const s = Math.sin(theta / 2);
108
+ const size = state.size;
109
+ for (let i = 0; i < size; i++) {
110
+ const ba = (i >> a) & 1;
111
+ const bb = (i >> b) & 1;
112
+ if (ba !== 0 || bb !== 0)
113
+ continue; // process each group once
114
+ const i01 = i | (1 << b);
115
+ const i10 = i | (1 << a);
116
+ const i11 = i | (1 << a) | (1 << b);
117
+ const [v00, v01, v10, v11] = [state.amp[i], state.amp[i01], state.amp[i10], state.amp[i11]];
118
+ const ni = new complex_1.Complex(0, -s);
119
+ state.amp[i] = v00.scale(c).add(v11.mul(new complex_1.Complex(0, -s)));
120
+ state.amp[i01] = v01.scale(c).add(v10.mul(new complex_1.Complex(0, -s)));
121
+ state.amp[i10] = v10.scale(c).add(v01.mul(new complex_1.Complex(0, -s)));
122
+ state.amp[i11] = v11.scale(c).add(v00.mul(new complex_1.Complex(0, -s)));
123
+ void ni; // suppress lint
124
+ }
125
+ }
126
+ /**
127
+ * IonQ native GPi gate (single-qubit).
128
+ * GPi(φ) = [[0, e^(-iφ)], [e^(iφ), 0]]
129
+ */
130
+ function applyGPi(state, target, phase) {
131
+ const mat = [
132
+ [complex_1.Complex.ZERO, complex_1.Complex.phase(-phase)],
133
+ [complex_1.Complex.phase(phase), complex_1.Complex.ZERO]
134
+ ];
135
+ applySingleQubit(state, target, mat);
136
+ }
137
+ /**
138
+ * IonQ native GPi2 gate (single-qubit).
139
+ * GPi2(φ) = 1/√2 * [[1, -i·e^(-iφ)], [-i·e^(iφ), 1]]
140
+ */
141
+ function applyGPi2(state, target, phase) {
142
+ const s = Math.SQRT1_2;
143
+ const mat = [
144
+ [new complex_1.Complex(s, 0), complex_1.Complex.phase(-phase).mul(new complex_1.Complex(0, -s))],
145
+ [complex_1.Complex.phase(phase).mul(new complex_1.Complex(0, -s)), new complex_1.Complex(s, 0)]
146
+ ];
147
+ applySingleQubit(state, target, mat);
148
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StateVector = void 0;
4
+ const complex_1 = require("./complex");
5
+ /**
6
+ * Quantum state vector for n qubits.
7
+ * Size = 2^n complex amplitudes.
8
+ * Qubit 0 = least significant bit (matching IonQ convention).
9
+ */
10
+ class StateVector {
11
+ numQubits;
12
+ size;
13
+ amp;
14
+ constructor(numQubits, amplitudes) {
15
+ this.numQubits = numQubits;
16
+ this.size = 1 << numQubits;
17
+ if (amplitudes) {
18
+ this.amp = amplitudes;
19
+ }
20
+ else {
21
+ // |00...0⟩
22
+ this.amp = new Array(this.size).fill(complex_1.Complex.ZERO);
23
+ this.amp[0] = complex_1.Complex.ONE;
24
+ }
25
+ }
26
+ /** Probability of basis state i (Born rule) */
27
+ prob(i) {
28
+ return this.amp[i].abs2();
29
+ }
30
+ /** All probabilities summed = 1 */
31
+ probabilities() {
32
+ return this.amp.map(a => a.abs2());
33
+ }
34
+ /** Nonzero terms as { bitstring → probability } */
35
+ toBitstrings() {
36
+ const result = {};
37
+ for (let i = 0; i < this.size; i++) {
38
+ const p = this.prob(i);
39
+ if (p > 1e-10) {
40
+ result[i.toString(2).padStart(this.numQubits, '0')] = p;
41
+ }
42
+ }
43
+ return result;
44
+ }
45
+ /** Collapse to index `outcome`, zero all other amplitudes */
46
+ collapse(outcome) {
47
+ for (let i = 0; i < this.size; i++) {
48
+ this.amp[i] = i === outcome ? complex_1.Complex.ONE : complex_1.Complex.ZERO;
49
+ }
50
+ }
51
+ /** Dirac notation */
52
+ toString() {
53
+ const terms = [];
54
+ for (let i = 0; i < this.size; i++) {
55
+ const a = this.amp[i];
56
+ if (a.abs2() > 1e-10) {
57
+ const bits = i.toString(2).padStart(this.numQubits, '0');
58
+ terms.push(`(${a})|${bits}⟩`);
59
+ }
60
+ }
61
+ return terms.join(' + ') || '|0⟩';
62
+ }
63
+ /**
64
+ * Memory footprint in bytes.
65
+ * Each complex number = 2 × 8 bytes (two float64s).
66
+ */
67
+ memoryBytes() {
68
+ return this.size * 2 * 8;
69
+ }
70
+ clone() {
71
+ return new StateVector(this.numQubits, [...this.amp]);
72
+ }
73
+ }
74
+ exports.StateVector = StateVector;