quantum-forge 2.7.0 → 3.0.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Real quantum mechanics for game developers. Superposition, entanglement, and interference powered by a compiled C++ quantum simulator running via WebAssembly.
4
4
 
5
- This is the core package WASM loader, quantum property manager, and Vite plugin. For the full game framework (engine, rendering, input, audio, etc.), install [`quantum-forge-engine`](https://www.npmjs.com/package/quantum-forge-engine) instead.
5
+ This is the core package: WASM loader, quantum property handles, and Vite plugin. For the full game framework (engine, rendering, input, audio, and more), install [`quantum-forge-engine`](https://www.npmjs.com/package/quantum-forge-engine) instead. It depends on this package, so one install gets both.
6
6
 
7
7
  ## Install
8
8
 
@@ -26,51 +26,60 @@ export default defineConfig({
26
26
 
27
27
  > **ESM note:** If using `vite.config.js` (not `.ts` or `.mjs`), add `"type": "module"` to your `package.json`.
28
28
 
29
- ### 2. Use Quantum Mechanics
29
+ ### 2. Declare a quantum property
30
30
 
31
31
  ```typescript
32
- import { ensureLoaded, QuantumPropertyManager } from "quantum-forge/quantum";
32
+ import { ensureLoaded, quantum } from "quantum-forge/quantum";
33
33
 
34
- // Initialize WASM (call once at startup)
35
- await ensureLoaded();
36
-
37
- // Create a property manager
38
- const qpm = new QuantumPropertyManager({ dimension: 2 });
34
+ await ensureLoaded(); // once, before the first quantum() call
39
35
 
40
- // Create a qubit and put it in superposition
41
- const qubit = qpm.acquireProperty();
42
- qpm.hadamard(qubit);
36
+ const color = quantum(["red", "green", "blue"]); // a qutrit, starts "red"
37
+ color.superpose(); // equal superposition (alias for hadamard())
43
38
 
44
- // Read probabilities (no collapse)
45
- const probs = qpm.probabilities(qubit); // [~0.5, ~0.5]
39
+ // Read without collapsing
40
+ color.probability("green"); // 1/3
41
+ color.probabilities();
42
+ // [{ value: "red", probability: 1/3 }, { value: "green", ... }, { value: "blue", ... }]
46
43
 
47
- // Measure (collapses to 0 or 1)
48
- const [value] = qpm.measureProperties([qubit]);
44
+ // Measure (collapses to one value)
45
+ const seen = color.measure(); // "red", "green" or "blue"
49
46
 
50
- // Recycle for reuse
51
- qpm.releaseProperty(qubit, value);
47
+ // End its life. dispose() measures, then frees the qudit for the next quantum() call.
48
+ color.dispose();
52
49
  ```
53
50
 
51
+ A property is declared by the values it can take, and the dimension is how many there are.
52
+ `quantum(3)` is the numeric form, with values `0`, `1`, `2`. A property needs at least two
53
+ values. A `using` declaration disposes the handle at the end of the scope; it needs TypeScript
54
+ 5.2 or newer with `"ESNext.Disposable"` in the tsconfig `lib`. Code that never writes `using`
55
+ needs neither.
56
+
54
57
  ### 3. Entanglement
55
58
 
59
+ Entanglement is what an interaction leaves behind. A gate that touches two properties is an
60
+ interaction:
61
+
56
62
  ```typescript
57
- const a = qpm.acquireProperty();
58
- const b = qpm.acquireProperty();
63
+ import { quantum, measure } from "quantum-forge/quantum";
59
64
 
60
- qpm.hadamard(a);
61
- qpm.iSwap(a, b); // Entangle measuring one determines the other
65
+ const a = quantum([false, true]).flip(); // a = true
66
+ const b = quantum([false, true]); // b = false
67
+ a.iSwap(b, 0.5); // half iSwap: exactly one of the pair ends up true
62
68
 
63
- const [va, vb] = qpm.measureProperties([a, b]);
64
- // va and vb are always anti-correlated
69
+ const [va, vb] = measure(a, b); // one step, both collapse
70
+ // va !== vb, every time
65
71
  ```
66
72
 
73
+ So is a gate whose predicate reads another property. `b.flip({ when: [a.is(true)] })` is a
74
+ CNOT: `b` flips only where `a` is `true`. `a.is(1)` builds the same predicate by index.
75
+
67
76
  ## Editions
68
77
 
69
78
  Two WASM builds are included:
70
79
 
71
80
  | Edition | Dimensions | Max Qudits | Use Case |
72
81
  |---------|-----------|------------|----------|
73
- | **Qutrit** (default) | 23 | 12 | Games using qutrits (3-state quantum digits) |
82
+ | **Qutrit** (default) | 2 to 3 | 12 | Games using qutrits (3-state quantum digits) |
74
83
  | **Qubit** | 2 | 20 | Games needing more qubits at dimension 2 |
75
84
 
76
85
  To use the Qubit Edition:
@@ -78,31 +87,58 @@ To use the Qubit Edition:
78
87
  ```typescript
79
88
  import { useQuantumForgeBuild, ensureLoaded } from "quantum-forge/quantum";
80
89
 
81
- useQuantumForgeBuild("qubit"); // Call before ensureLoaded()
90
+ useQuantumForgeBuild("qubit"); // before ensureLoaded()
91
+ await ensureLoaded();
92
+ ```
93
+
94
+ ## Node and headless use
95
+
96
+ The same code runs in Node (tests, servers, agents) with no Vite plugin. The loader finds the WASM inside the installed package, and `useQuantumForgeBuild("qubit")` picks the variant the same way. Point the loader elsewhere only when the WASM files live outside the package:
97
+
98
+ ```typescript
99
+ import { setWasmBasePath, ensureLoaded } from "quantum-forge/quantum";
100
+ import { pathToFileURL } from "node:url";
101
+
102
+ setWasmBasePath(pathToFileURL("/opt/wasm/quantum-forge-qubit").href);
82
103
  await ensureLoaded();
83
104
  ```
84
105
 
106
+ Node 22 or newer.
107
+
85
108
  ## Package Exports
86
109
 
87
110
  | Export | Contents |
88
111
  |--------|----------|
89
- | `quantum-forge/quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
112
+ | `quantum-forge/quantum` | `quantum`, `Quantum`, `measure`, `forcedMeasure`, `probabilities`, `densityMatrix`, `measureWhen`, `forcedMeasureWhen`, `probabilityWhen`, `phaseRotate`, `isQuantum`, `observeQuantum`, `clearQuantumCache`, `QuantumRecorder`, `ensureLoaded`, `startBackgroundLoad`, `isReady`, `useQuantumForgeBuild`, `setWasmBasePath`, `getVersion`, `getMaxDimension`, `getMaxQudits`, `getMaxStateSize`, `getAttribution`, `registerServiceWorker`, `OP` |
90
113
  | `quantum-forge/logging` | `Logger` |
91
114
  | `quantum-forge/vite-plugin` | `quantumForgeVitePlugin` |
92
115
 
93
116
  ## Gates
94
117
 
95
- | Gate | Qudits | Description |
96
- |------|--------|-------------|
97
- | `hadamard` | 1 | Equal superposition |
98
- | `cycle` | 1 | Deterministic rotation: \|0⟩→\|1⟩→\|2⟩→\|0⟩ |
99
- | `clock` (Z) | 1 | Phase rotation |
100
- | `shift` (X) | 1 | Value shift |
101
- | `y` | 1 | Y gate (qubit-only) |
102
- | `iSwap` | 2 | Entangling swap |
103
- | `swap` | 2 | Value swap |
104
-
105
- All gates support fractional application and conditional predicates.
118
+ Gates are methods on the handle. Each alias runs exactly the same gate as the physics name
119
+ beside it.
120
+
121
+ | Gate | Alias | Qudits | Description |
122
+ |------|-------|--------|-------------|
123
+ | `hadamard` | `superpose` | 1 | Equal superposition |
124
+ | `inverseHadamard` | | 1 | Undoes `hadamard` |
125
+ | `cycle` | `next`, and `flip` on qubits | 1 | \|0⟩→\|1⟩→\|2⟩→\|0⟩ (a NOT at dimension 2) |
126
+ | `shift` | `previous` | 1 | Inverse of cycle; same as cycle at dimension 2 |
127
+ | `clock` | `phase` | 1 | Phase rotation |
128
+ | `x`, `z` | | 1 | Pauli X (same as `shift`) and Pauli Z (same as `clock`) |
129
+ | `y` | | 1 | Pauli Y, dimension 2 only |
130
+ | `iSwap(other, fraction)` | | 2 | Entangling swap; `fraction` is required |
131
+ | `swap(other)` | | 2 | Value swap |
132
+ | `phaseRotate(angle, { when })` | | any | Free function: phase on the part of the state where the predicates hold |
133
+
134
+ Every gate except `inverseHadamard`, `swap` and `iSwap` takes an optional fraction first. An
135
+ omitted fraction, or exactly 1, is the discrete gate; any other number is the continuous
136
+ version, and 0.5 is the square root of the gate. Predicates go last, in `{ when: [...] }`.
137
+
138
+ Measurement: `prop.measure()`, `measure(...props)`, `measureWhen(preds)`, and the forced
139
+ variants for replays and tests, which throw if the forced value has zero probability.
140
+ Read-only: `prop.probability(value)`, `prop.probabilities()`,
141
+ `probabilities(...props)`, `probabilityWhen(preds)`, `densityMatrix(...props)`.
106
142
 
107
143
  ## Documentation
108
144
 
@@ -112,4 +148,4 @@ All gates support fractional application and conditional predicates.
112
148
 
113
149
  ## License
114
150
 
115
- TypeScript source: MIT (see [LICENSE.md](./LICENSE.md)). WASM binaries: proprietary free for apps under $100K annual revenue with attribution. See [dist/LICENSE-BINARY.md](./dist/LICENSE-BINARY.md) for binary terms.
151
+ TypeScript source: MIT (see [LICENSE.md](./LICENSE.md)). WASM binaries: proprietary, free for apps under $100K annual revenue with attribution. See [dist/LICENSE-BINARY.md](./dist/LICENSE-BINARY.md) for binary terms.