@quantumplayed/quantum-game-engine 1.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 +314 -0
- package/dist/CircuitLog.d.ts +54 -0
- package/dist/CircuitLog.js +80 -0
- package/dist/CircuitLog.js.map +1 -0
- package/dist/Complex.d.ts +13 -0
- package/dist/Complex.js +49 -0
- package/dist/Complex.js.map +1 -0
- package/dist/EntanglementEngine.d.ts +48 -0
- package/dist/EntanglementEngine.js +456 -0
- package/dist/EntanglementEngine.js.map +1 -0
- package/dist/Operations.d.ts +18 -0
- package/dist/Operations.js +107 -0
- package/dist/Operations.js.map +1 -0
- package/dist/QuantumEntity.d.ts +26 -0
- package/dist/QuantumEntity.js +142 -0
- package/dist/QuantumEntity.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/src/CircuitLog.d.ts +54 -0
- package/dist/src/CircuitLog.js +80 -0
- package/dist/src/CircuitLog.js.map +1 -0
- package/dist/src/Complex.d.ts +13 -0
- package/dist/src/Complex.d.ts.map +1 -0
- package/dist/src/Complex.js +49 -0
- package/dist/src/Complex.js.map +1 -0
- package/dist/src/EntanglementEngine.d.ts +48 -0
- package/dist/src/EntanglementEngine.d.ts.map +1 -0
- package/dist/src/EntanglementEngine.js +456 -0
- package/dist/src/EntanglementEngine.js.map +1 -0
- package/dist/src/Operations.d.ts +18 -0
- package/dist/src/Operations.d.ts.map +1 -0
- package/dist/src/Operations.js +107 -0
- package/dist/src/Operations.js.map +1 -0
- package/dist/src/QuantumEntity.d.ts +26 -0
- package/dist/src/QuantumEntity.d.ts.map +1 -0
- package/dist/src/QuantumEntity.js +142 -0
- package/dist/src/QuantumEntity.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/tests/DensityMatrix.test.d.ts +1 -0
- package/dist/tests/DensityMatrix.test.d.ts.map +1 -0
- package/dist/tests/DensityMatrix.test.js +41 -0
- package/dist/tests/DensityMatrix.test.js.map +1 -0
- package/dist/tests/EntanglementEngine.test.d.ts +1 -0
- package/dist/tests/EntanglementEngine.test.d.ts.map +1 -0
- package/dist/tests/EntanglementEngine.test.js +72 -0
- package/dist/tests/EntanglementEngine.test.js.map +1 -0
- package/dist/tests/QuantumEntity.test.d.ts +1 -0
- package/dist/tests/QuantumEntity.test.d.ts.map +1 -0
- package/dist/tests/QuantumEntity.test.js +60 -0
- package/dist/tests/QuantumEntity.test.js.map +1 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Quantum Game Engine
|
|
2
|
+
|
|
3
|
+
A generalized, sparse-matrix quantum simulation library designed specifically for game developers.
|
|
4
|
+
|
|
5
|
+
Unlike traditional quantum SDKs (like Qiskit or Cirq) that focus on low-level circuit design and dense matrices, this engine abstracts Away the complex math. It uses "Second Quantization" and sparse amplitude dictionaries to allow you to easily attach quantum mechanics (superposition, entanglement, interference) directly to high-dimensional objects in your game (like an $(x,y)$ coordinate grid, a character's inventory, or a branching dialogue tree).
|
|
6
|
+
|
|
7
|
+
## Core Concepts
|
|
8
|
+
|
|
9
|
+
* **`QuantumEntity`**: Represents any quantum object in your game (a player, a coin, a chess piece). It manages its own state, but if it becomes entangled, it defers mathematically to a joint system behind the scenes.
|
|
10
|
+
* **`Operations`**: A suite of generalized quantum gates (Shift, Phase, Hadamard) that work on *qudits* of any dimension, not just qubits.
|
|
11
|
+
* **`EntanglementEngine`**: A powerful manager that tracks interactions between different `QuantumEntity` objects. When entities interact conditionally (e.g., "IF player is on square 5, open the door"), their states become mathematically linked.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Creating and Modifying Entities
|
|
16
|
+
|
|
17
|
+
A `QuantumEntity` requires a `dimension` (how many possible classical states it can have) and an initial state index.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { QuantumEntity, Operations } from 'quantum-game-engine';
|
|
21
|
+
|
|
22
|
+
// A coin has dimension 2 (Heads=0, Tails=1)
|
|
23
|
+
const coin = new QuantumEntity({ dimension: 2, initialState: 0 });
|
|
24
|
+
|
|
25
|
+
// A character moving on a 10x10 grid has dimension 100
|
|
26
|
+
const player = new QuantumEntity({ dimension: 100, initialState: 45 });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Applying Operations
|
|
30
|
+
|
|
31
|
+
You can mutate the state of an entity using `apply()`.
|
|
32
|
+
|
|
33
|
+
**1. Shift (Generalized Pauli-X / Clock)**
|
|
34
|
+
Shifts the classical state by a certain amount modulo the dimension.
|
|
35
|
+
```typescript
|
|
36
|
+
player.apply(Operations.Shift(1)); // Moves player from 45 to 46
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**2. Phase (Generalized Pauli-Z)**
|
|
40
|
+
Applies a phase rotation to the state. Crucial for interference effects.
|
|
41
|
+
```typescript
|
|
42
|
+
coin.apply(Operations.Phase(Math.PI));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**3. Hadamard (Quantum Fourier Transform)**
|
|
46
|
+
Puts the entity into a uniform superposition of all its possible states.
|
|
47
|
+
```typescript
|
|
48
|
+
coin.apply(Operations.Hadamard());
|
|
49
|
+
// Coin is now 50% Heads, 50% Tails
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**4. SparseTransition**
|
|
53
|
+
For complex game logic (like a "Quantum Split" move in Checkers), you can define a custom transition matrix. You only need to provide the non-zero mappings.
|
|
54
|
+
```typescript
|
|
55
|
+
const splitMove = Operations.SparseTransition(dimension, {
|
|
56
|
+
45: [
|
|
57
|
+
{ state: 54, amplitude: { re: 0.707, im: 0 } }, // 50% chance to move left
|
|
58
|
+
{ state: 56, amplitude: { re: 0.707, im: 0 } } // 50% chance to move right
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
player.apply(splitMove);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. Entanglement and Interactions
|
|
67
|
+
|
|
68
|
+
The true power of the engine is the `EntanglementEngine`. In games, things interact. If a quantum player steps on a quantum button, they become entangled. Measuring one affects the other.
|
|
69
|
+
|
|
70
|
+
Use `conditionalInteract` to apply operations to a `target` *only if* the `control` entity is in a specific state.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { EntanglementEngine } from 'quantum-game-engine';
|
|
74
|
+
|
|
75
|
+
const player = new QuantumEntity({ dimension: 10, initialState: 5 });
|
|
76
|
+
const door = new QuantumEntity({ dimension: 2, initialState: 0 }); // 0=Closed, 1=Open
|
|
77
|
+
|
|
78
|
+
// Player steps left and right at the same time
|
|
79
|
+
player.apply(Operations.Hadamard());
|
|
80
|
+
|
|
81
|
+
// If the player is on square 6, they open the door.
|
|
82
|
+
EntanglementEngine.conditionalInteract({
|
|
83
|
+
controlEntities: [{ entity: player, state: 6 }],
|
|
84
|
+
targets: [{ entity: door, operation: Operations.Shift(1) }]
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// The Player and the Door are now ENTANGLED.
|
|
88
|
+
// - In a universe where player is at 6, the door is Open (1).
|
|
89
|
+
// - In a universe where player is at 5, the door is Closed (0).
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 3. Observation and Rendering
|
|
95
|
+
|
|
96
|
+
### Extracting Probabilities (For UI)
|
|
97
|
+
|
|
98
|
+
To render the game to the screen, you need to know the likelihood of an entity being in a certain state. Use `getProbabilities()` to get a classical probability distribution (0.0 to 1.0).
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const probs = player.getProbabilities();
|
|
102
|
+
// Returns: { '5': 0.5, '6': 0.5 }
|
|
103
|
+
|
|
104
|
+
// If entangled, getProbabilities() automatically calculates the
|
|
105
|
+
// marginal probabilities of just this entity from the joint system!
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Advanced Physics: Reduced Density Matrices**
|
|
109
|
+
If your game logic requires knowing whether an entity has "decohered" due to entanglement (losing its quantum interference capability), you can calculate its One-Body Reduced Density Matrix:
|
|
110
|
+
```typescript
|
|
111
|
+
const rho = player.getReducedDensityMatrix();
|
|
112
|
+
// Returns a 2D map of Complex numbers.
|
|
113
|
+
// rho[x][x] = Probability of state x
|
|
114
|
+
// rho[x][y] = Coherence between state x and y
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Measuring (Collapsing the Wavefunction)
|
|
118
|
+
|
|
119
|
+
When gameplay requires an entity to commit to a single reality (e.g., an enemy attacks the player, forcing them to be in one specific location), use `measure()`.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const finalPosition = player.measure();
|
|
123
|
+
// Returns exactly 5 or exactly 6.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**The Magic of Entanglement:**
|
|
127
|
+
If `player` and `door` are entangled (as in the example above), measuring the `player` will **instantly collapse the `door` as well**.
|
|
128
|
+
If `player.measure()` returns `6`, any subsequent call to `door.measure()` is mathematically guaranteed to return `1` (Open).
|
|
129
|
+
|
|
130
|
+
### Forced Measurements (Deterministic Replay)
|
|
131
|
+
|
|
132
|
+
Pass an optional `forcedOutcome` to `measure()` to force a specific collapse state. This bypasses `Math.random()` entirely.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const outcome = player.measure(0); // Always collapses to state 0
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This works with entangled systems too — the forced outcome propagates through the joint system, collapsing entangled partners accordingly. Use this for:
|
|
139
|
+
- **Game history replay** — reproduce recorded measurement outcomes exactly
|
|
140
|
+
- **Undo/redo** — re-apply the same measurement results
|
|
141
|
+
- **Testing** — verify specific branches without randomness
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 4. Circuit Logging
|
|
146
|
+
|
|
147
|
+
The engine includes a toggleable `CircuitLogger` that records every state-mutating operation as an append-only log. Recording is **off by default** — zero overhead during normal gameplay.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { CircuitLogger } from 'quantum-game-engine';
|
|
151
|
+
|
|
152
|
+
CircuitLogger.startRecording();
|
|
153
|
+
|
|
154
|
+
// ... game operations (apply, interactMany, conditionalInteract, measure) ...
|
|
155
|
+
|
|
156
|
+
const log = CircuitLogger.stopRecording(); // CircuitEntry[]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Each entry is either a `GateEntry` (for `apply`, `interactMany`, `conditionalInteract`) or a `MeasurementEntry` (for `measure`), tagged with entity IDs, dimensions, and an auto-incrementing timestamp.
|
|
160
|
+
|
|
161
|
+
| Method | Description |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `startRecording()` | Begin recording (resets log) |
|
|
164
|
+
| `stopRecording()` | Stop and return `CircuitEntry[]` |
|
|
165
|
+
| `isRecording()` | Fast guard for hot path |
|
|
166
|
+
| `getLog()` | Get log copy while recording |
|
|
167
|
+
| `getLogForEntities(ids)` | Filter by entity IDs |
|
|
168
|
+
| `reset()` | Clear log and counter |
|
|
169
|
+
|
|
170
|
+
Every `QuantumEntity` has a unique readonly `id: number` (auto-incrementing), used to identify entities in log entries.
|
|
171
|
+
|
|
172
|
+
## Engine Boundaries and Limitations
|
|
173
|
+
|
|
174
|
+
* **Joint Dimension Limit:** When entities entangle, their state spaces multiply (a dimension-10 player interacting with a dimension-10 enemy creates a dimension-100 joint system). The `EntanglementEngine` currently enforces a hard limit of `4096` total joint dimensions to prevent out-of-memory crashes on the browser's main thread. Design your game's entanglement mechanics to collapse frequently!
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 5. Full API Usage Examples
|
|
180
|
+
|
|
181
|
+
### Example 1: A Quantum Platformer (Schrödinger's Jump)
|
|
182
|
+
|
|
183
|
+
**The Scenario:** You have a platformer game. The player (`playerX`) reaches a gap. They can press a "Quantum Jump" button to simultaneously jump *and* not jump, putting their position in a superposition. Later, they encounter a "Quantum Door" that only opens if the player is standing on a specific button.
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
import { QuantumEntity, Operations, EntanglementEngine } from 'quantum-game-engine';
|
|
187
|
+
|
|
188
|
+
// 1. Initialize the player's X position as a qudit (e.g., 100 possible tiles)
|
|
189
|
+
// We start purely classical: 100% chance of being at tile 5.
|
|
190
|
+
const playerX = new QuantumEntity({ dimension: 100, initialState: 5 });
|
|
191
|
+
|
|
192
|
+
// --- THE QUANTUM JUMP ---
|
|
193
|
+
// The player hits the "Quantum Jump" button!
|
|
194
|
+
// We want an equal 50/50 superposition of staying at 5 or jumping to 10.
|
|
195
|
+
// We apply a custom sparse unitary operation (a basic split/Hadamard variant).
|
|
196
|
+
const jumpOp = Operations.SparseTransition(100, {
|
|
197
|
+
5: [
|
|
198
|
+
{ state: 5, amplitude: { re: 0.707, im: 0 } }, // Branch A: didn't jump
|
|
199
|
+
{ state: 10, amplitude: { re: 0.707, im: 0 } } // Branch B: did jump
|
|
200
|
+
]
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
playerX.apply(jumpOp);
|
|
204
|
+
|
|
205
|
+
// playerX.amplitudes is now roughly:
|
|
206
|
+
// { 5: {re: 0.707, im: 0}, 10: {re: 0.707, im: 0} }
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
// --- ENTANGLEMENT ---
|
|
210
|
+
// A Quantum Door (qudit: 0=Closed, 1=Open)
|
|
211
|
+
const door = new QuantumEntity({ dimension: 2, initialState: 0 });
|
|
212
|
+
|
|
213
|
+
// If the player landed on the button at tile 10, the door opens.
|
|
214
|
+
EntanglementEngine.conditionalInteract({
|
|
215
|
+
controlEntities: [{ entity: playerX, state: 10 }],
|
|
216
|
+
targets: [{ entity: door, operation: Operations.Shift(1) }]
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Since playerX was in superposition, the Door is now entangled!
|
|
220
|
+
// The joint amplitudes (PlayerX ⊗ Door):
|
|
221
|
+
// {
|
|
222
|
+
// "5,0": {re: 0.707, im: 0}, // Player at 5, Door is Closed
|
|
223
|
+
// "10,1": {re: 0.707, im: 0} // Player at 10, Door is Open
|
|
224
|
+
// }
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
// --- MEASUREMENT / COLLAPSE ---
|
|
228
|
+
// The player walks into a "Camera / Observation Zone"
|
|
229
|
+
// We force a collapse of the player's position.
|
|
230
|
+
const result = playerX.measure();
|
|
231
|
+
|
|
232
|
+
// Let's say the engine rolled the 50% chance and returned '10'.
|
|
233
|
+
// Because of the Entanglement Engine, the Door's state automatically
|
|
234
|
+
// collapses to '1' (Open)! The universe branch where the player didn't
|
|
235
|
+
// jump is pruned (amplitudes deleted).
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### Example 2: Quantum Checkers (Squares as Qubits)
|
|
241
|
+
|
|
242
|
+
**The Scenario:** A player wants to move a piece from square 12. Instead of a normal move, they do a `SplitMove` to squares 16 and 17. Later, an opponent tries to take the piece on square 16.
|
|
243
|
+
|
|
244
|
+
*Note: In Cheqqers, instead of tracking a "Piece" as a 32-dimension entity (First Quantization), it is much more natural to track each "Square" as a 2-dimension Qubit (Second Quantization), where `0 = Empty` and `1 = Occupied`.*
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
import { QuantumEntity, Operations, EntanglementEngine } from 'quantum-game-engine';
|
|
248
|
+
|
|
249
|
+
// We represent squares as individual Qubits.
|
|
250
|
+
// Initial state: Square 12 is Occupied (1), Squares 16 and 17 are Empty (0).
|
|
251
|
+
const sq12 = new QuantumEntity({ dimension: 2, initialState: 1 });
|
|
252
|
+
const sq16 = new QuantumEntity({ dimension: 2, initialState: 0 });
|
|
253
|
+
const sq17 = new QuantumEntity({ dimension: 2, initialState: 0 });
|
|
254
|
+
|
|
255
|
+
// --- THE SPLIT MOVE ---
|
|
256
|
+
// The "Quantum Chess Split" matrix is an 8x8 matrix that acts on 3 qubits
|
|
257
|
+
// (From, To1, To2). Our engine provides a helper to apply this sparse sub-matrix.
|
|
258
|
+
// Because it involves 3 entities, the EntanglementEngine automatically merges them
|
|
259
|
+
// into an 8-dimension joint Hilbert space (2x2x2).
|
|
260
|
+
EntanglementEngine.interactMany([sq12, sq16, sq17], Operations.SparseTransition(8, {
|
|
261
|
+
// Binary 100 (state 4) -> 010 (state 2) + 001 (state 1)
|
|
262
|
+
4: [
|
|
263
|
+
{ state: 2, amplitude: { re: 0.707, im: 0 } },
|
|
264
|
+
{ state: 1, amplitude: { re: 0.707, im: 0 } }
|
|
265
|
+
]
|
|
266
|
+
}));
|
|
267
|
+
|
|
268
|
+
// After the split:
|
|
269
|
+
// sq12 is now definitely Empty (|0>).
|
|
270
|
+
// The system of (sq16 ⊗ sq17) is in a superposition:
|
|
271
|
+
// 50% chance of |1, 0> (Piece is on 16)
|
|
272
|
+
// 50% chance of |0, 1> (Piece is on 17)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
// --- THE PASSAGE OF TIME (PHASE) ---
|
|
276
|
+
// In Cheqqers, time/turns applies a phase shift to superpositions.
|
|
277
|
+
EntanglementEngine.conditionalInteract({
|
|
278
|
+
controlEntities: [{ entity: sq16, state: 1 }],
|
|
279
|
+
targets: [{ entity: sq16, operation: Operations.Phase(Math.PI / 4) }]
|
|
280
|
+
});
|
|
281
|
+
EntanglementEngine.conditionalInteract({
|
|
282
|
+
controlEntities: [{ entity: sq17, state: 1 }],
|
|
283
|
+
targets: [{ entity: sq17, operation: Operations.Phase(Math.PI / 4) }]
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
// --- CONDITIONAL CAPTURE (ENTANGLEMENT) ---
|
|
288
|
+
// Opponent moves a black piece from square 20 to square 12, capturing 16.
|
|
289
|
+
const sq20 = new QuantumEntity({ dimension: 2, initialState: 1 }); // Black piece
|
|
290
|
+
|
|
291
|
+
// The capture logic:
|
|
292
|
+
// IF Black is on 20 (it is) AND White is on 16 (it's in superposition):
|
|
293
|
+
// 1. Black moves to 12 (sq20 -> 0, sq12 -> 1)
|
|
294
|
+
// 2. White is captured (sq16 -> 0)
|
|
295
|
+
// This translates to a Toffoli (CCX) and CNOT (CX) sequence in quantum circuits.
|
|
296
|
+
|
|
297
|
+
EntanglementEngine.conditionalInteract({
|
|
298
|
+
controlEntities: [{ entity: sq20, state: 1 }, { entity: sq16, state: 1 }],
|
|
299
|
+
targets: [
|
|
300
|
+
{ entity: sq20, operation: Operations.Shift(1) }, // 1 -> 0 (wrapping)
|
|
301
|
+
{ entity: sq12, operation: Operations.Shift(1) }, // 0 -> 1
|
|
302
|
+
{ entity: sq16, operation: Operations.Shift(1) } // 1 -> 0 (captured)
|
|
303
|
+
]
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// The Joint Hilbert space now tracks the entangled state of shapes.
|
|
307
|
+
// Branch A: (White was on 17).
|
|
308
|
+
// sq16 resolves to 0. The capture condition fails! Black stays on 20.
|
|
309
|
+
// Branch B: (White was on 16).
|
|
310
|
+
// sq16 resolves to 1. Capture fires! Black moves to 12, sq16 flips to 0.
|
|
311
|
+
|
|
312
|
+
// Because everything is sparse, branches where mathematically invalid things happen
|
|
313
|
+
// (like trying to capture an empty square) don't consume memory.
|
|
314
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CircuitLog — toggleable, append-only log of quantum operations.
|
|
3
|
+
*
|
|
4
|
+
* When recording is active, every state-mutating call (gate or measurement)
|
|
5
|
+
* appends an entry. The log is designed for future incremental OpenQASM export:
|
|
6
|
+
* each new entry maps to one circuit instruction, so the export never rebuilds.
|
|
7
|
+
*/
|
|
8
|
+
export interface GateEntry {
|
|
9
|
+
type: 'gate';
|
|
10
|
+
name: string;
|
|
11
|
+
entityIds: number[];
|
|
12
|
+
dimensions: number[];
|
|
13
|
+
controls?: {
|
|
14
|
+
entityId: number;
|
|
15
|
+
state: number;
|
|
16
|
+
}[];
|
|
17
|
+
params?: Record<string, any>;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
export interface MeasurementEntry {
|
|
21
|
+
type: 'measurement';
|
|
22
|
+
entityId: number;
|
|
23
|
+
dimension: number;
|
|
24
|
+
outcome: number;
|
|
25
|
+
timestamp: number;
|
|
26
|
+
}
|
|
27
|
+
export type CircuitEntry = GateEntry | MeasurementEntry;
|
|
28
|
+
declare class CircuitLoggerImpl {
|
|
29
|
+
private _recording;
|
|
30
|
+
private _log;
|
|
31
|
+
private _counter;
|
|
32
|
+
/** Begin recording. Resets the existing log. */
|
|
33
|
+
startRecording(): void;
|
|
34
|
+
/** Stop recording and return the captured log. */
|
|
35
|
+
stopRecording(): CircuitEntry[];
|
|
36
|
+
/** Fast guard — check before constructing entry objects on the hot path. */
|
|
37
|
+
isRecording(): boolean;
|
|
38
|
+
/** Append an entry (auto-assigns timestamp). */
|
|
39
|
+
addGate(name: string, entityIds: number[], dimensions: number[], params?: Record<string, any>, controls?: {
|
|
40
|
+
entityId: number;
|
|
41
|
+
state: number;
|
|
42
|
+
}[]): void;
|
|
43
|
+
/** Append a measurement entry. */
|
|
44
|
+
addMeasurement(entityId: number, dimension: number, outcome: number): void;
|
|
45
|
+
/** Get a copy of the current log. */
|
|
46
|
+
getLog(): CircuitEntry[];
|
|
47
|
+
/** Filter log to entries involving any of the given entity IDs. */
|
|
48
|
+
getLogForEntities(entityIds: number[]): CircuitEntry[];
|
|
49
|
+
/** Clear the log and reset the counter. */
|
|
50
|
+
reset(): void;
|
|
51
|
+
}
|
|
52
|
+
/** Global singleton circuit logger. */
|
|
53
|
+
export declare const CircuitLogger: CircuitLoggerImpl;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CircuitLog — toggleable, append-only log of quantum operations.
|
|
3
|
+
*
|
|
4
|
+
* When recording is active, every state-mutating call (gate or measurement)
|
|
5
|
+
* appends an entry. The log is designed for future incremental OpenQASM export:
|
|
6
|
+
* each new entry maps to one circuit instruction, so the export never rebuilds.
|
|
7
|
+
*/
|
|
8
|
+
// ── Logger ───────────────────────────────────────────────────────────
|
|
9
|
+
class CircuitLoggerImpl {
|
|
10
|
+
_recording = false;
|
|
11
|
+
_log = [];
|
|
12
|
+
_counter = 0;
|
|
13
|
+
/** Begin recording. Resets the existing log. */
|
|
14
|
+
startRecording() {
|
|
15
|
+
this._recording = true;
|
|
16
|
+
this._log = [];
|
|
17
|
+
this._counter = 0;
|
|
18
|
+
}
|
|
19
|
+
/** Stop recording and return the captured log. */
|
|
20
|
+
stopRecording() {
|
|
21
|
+
this._recording = false;
|
|
22
|
+
return [...this._log];
|
|
23
|
+
}
|
|
24
|
+
/** Fast guard — check before constructing entry objects on the hot path. */
|
|
25
|
+
isRecording() {
|
|
26
|
+
return this._recording;
|
|
27
|
+
}
|
|
28
|
+
/** Append an entry (auto-assigns timestamp). */
|
|
29
|
+
addGate(name, entityIds, dimensions, params, controls) {
|
|
30
|
+
const entry = {
|
|
31
|
+
type: 'gate',
|
|
32
|
+
name,
|
|
33
|
+
entityIds,
|
|
34
|
+
dimensions,
|
|
35
|
+
timestamp: this._counter++,
|
|
36
|
+
};
|
|
37
|
+
if (params)
|
|
38
|
+
entry.params = params;
|
|
39
|
+
if (controls)
|
|
40
|
+
entry.controls = controls;
|
|
41
|
+
this._log.push(entry);
|
|
42
|
+
}
|
|
43
|
+
/** Append a measurement entry. */
|
|
44
|
+
addMeasurement(entityId, dimension, outcome) {
|
|
45
|
+
this._log.push({
|
|
46
|
+
type: 'measurement',
|
|
47
|
+
entityId,
|
|
48
|
+
dimension,
|
|
49
|
+
outcome,
|
|
50
|
+
timestamp: this._counter++,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Get a copy of the current log. */
|
|
54
|
+
getLog() {
|
|
55
|
+
return [...this._log];
|
|
56
|
+
}
|
|
57
|
+
/** Filter log to entries involving any of the given entity IDs. */
|
|
58
|
+
getLogForEntities(entityIds) {
|
|
59
|
+
const idSet = new Set(entityIds);
|
|
60
|
+
return this._log.filter(entry => {
|
|
61
|
+
if (entry.type === 'measurement') {
|
|
62
|
+
return idSet.has(entry.entityId);
|
|
63
|
+
}
|
|
64
|
+
// Gate — check targets and controls
|
|
65
|
+
if (entry.entityIds.some(id => idSet.has(id)))
|
|
66
|
+
return true;
|
|
67
|
+
if (entry.controls?.some(c => idSet.has(c.entityId)))
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Clear the log and reset the counter. */
|
|
73
|
+
reset() {
|
|
74
|
+
this._log = [];
|
|
75
|
+
this._counter = 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Global singleton circuit logger. */
|
|
79
|
+
export const CircuitLogger = new CircuitLoggerImpl();
|
|
80
|
+
//# sourceMappingURL=CircuitLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CircuitLog.js","sourceRoot":"","sources":["../src/CircuitLog.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,wEAAwE;AAExE,MAAM,iBAAiB;IACX,UAAU,GAAG,KAAK,CAAC;IACnB,IAAI,GAAmB,EAAE,CAAC;IAC1B,QAAQ,GAAG,CAAC,CAAC;IAErB,gDAAgD;IAChD,cAAc;QACV,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,kDAAkD;IAClD,aAAa;QACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,4EAA4E;IAC5E,WAAW;QACP,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED,gDAAgD;IAChD,OAAO,CACH,IAAY,EACZ,SAAmB,EACnB,UAAoB,EACpB,MAA4B,EAC5B,QAAgD;QAEhD,MAAM,KAAK,GAAc;YACrB,IAAI,EAAE,MAAM;YACZ,IAAI;YACJ,SAAS;YACT,UAAU;YACV,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE;SAC7B,CAAC;QACF,IAAI,MAAM;YAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAClC,IAAI,QAAQ;YAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,kCAAkC;IAClC,cAAc,CAAC,QAAgB,EAAE,SAAiB,EAAE,OAAe;QAC/D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,aAAa;YACnB,QAAQ;YACR,SAAS;YACT,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE;SAC7B,CAAC,CAAC;IACP,CAAC;IAED,qCAAqC;IACrC,MAAM;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,mEAAmE;IACnE,iBAAiB,CAAC,SAAmB;QACjC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC/B,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;YACD,oCAAoC;YACpC,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3D,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YAClE,OAAO,KAAK,CAAC;QACjB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,2CAA2C;IAC3C,KAAK;QACD,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACtB,CAAC;CACJ;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Complex {
|
|
2
|
+
re: number;
|
|
3
|
+
im: number;
|
|
4
|
+
}
|
|
5
|
+
export declare const ComplexMath: {
|
|
6
|
+
add(a: Complex, b: Complex): Complex;
|
|
7
|
+
subtract(a: Complex, b: Complex): Complex;
|
|
8
|
+
multiply(a: Complex, b: Complex): Complex;
|
|
9
|
+
conjugate(a: Complex): Complex;
|
|
10
|
+
scale(a: Complex, scalar: number): Complex;
|
|
11
|
+
magnitudeSquared(a: Complex): number;
|
|
12
|
+
phase(angle: number): Complex;
|
|
13
|
+
};
|
package/dist/Complex.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const ComplexMath = {
|
|
2
|
+
// Add two complex numbers
|
|
3
|
+
add(a, b) {
|
|
4
|
+
return {
|
|
5
|
+
re: a.re + b.re,
|
|
6
|
+
im: a.im + b.im
|
|
7
|
+
};
|
|
8
|
+
},
|
|
9
|
+
// Subtract b from a
|
|
10
|
+
subtract(a, b) {
|
|
11
|
+
return {
|
|
12
|
+
re: a.re - b.re,
|
|
13
|
+
im: a.im - b.im
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
// Multiply two complex numbers
|
|
17
|
+
multiply(a, b) {
|
|
18
|
+
return {
|
|
19
|
+
re: a.re * b.re - a.im * b.im,
|
|
20
|
+
im: a.im * b.re + a.re * b.im
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
// Complex conjugate
|
|
24
|
+
conjugate(a) {
|
|
25
|
+
return {
|
|
26
|
+
re: a.re,
|
|
27
|
+
im: -a.im
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
// Multiply a complex number by a scalar
|
|
31
|
+
scale(a, scalar) {
|
|
32
|
+
return {
|
|
33
|
+
re: a.re * scalar,
|
|
34
|
+
im: a.im * scalar
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
// Calculate magnitude squared (probability)
|
|
38
|
+
magnitudeSquared(a) {
|
|
39
|
+
return a.re * a.re + a.im * a.im;
|
|
40
|
+
},
|
|
41
|
+
// Calculate the complex phase e^(i * angle)
|
|
42
|
+
phase(angle) {
|
|
43
|
+
return {
|
|
44
|
+
re: Math.cos(angle),
|
|
45
|
+
im: Math.sin(angle)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=Complex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Complex.js","sourceRoot":"","sources":["../src/Complex.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,WAAW,GAAG;IACvB,0BAA0B;IAC1B,GAAG,CAAC,CAAU,EAAE,CAAU;QACtB,OAAO;YACH,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;YACf,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;SAClB,CAAC;IACN,CAAC;IAED,oBAAoB;IACpB,QAAQ,CAAC,CAAU,EAAE,CAAU;QAC3B,OAAO;YACH,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;YACf,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;SAClB,CAAC;IACN,CAAC;IAED,+BAA+B;IAC/B,QAAQ,CAAC,CAAU,EAAE,CAAU;QAC3B,OAAO;YACH,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;YAC7B,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;SAChC,CAAC;IACN,CAAC;IAED,oBAAoB;IACpB,SAAS,CAAC,CAAU;QAChB,OAAO;YACH,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;SACZ,CAAC;IACN,CAAC;IAED,wCAAwC;IACxC,KAAK,CAAC,CAAU,EAAE,MAAc;QAC5B,OAAO;YACH,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM;YACjB,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM;SACpB,CAAC;IACN,CAAC;IAED,4CAA4C;IAC5C,gBAAgB,CAAC,CAAU;QACvB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IACrC,CAAC;IAED,4CAA4C;IAC5C,KAAK,CAAC,KAAa;QACf,OAAO;YACH,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YACnB,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;SACtB,CAAC;IACN,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { QuantumEntity, type Predicate } from './QuantumEntity';
|
|
2
|
+
import { type QuantumOperation, type SparseAmplitudes } from './Operations';
|
|
3
|
+
import { type Complex } from './Complex';
|
|
4
|
+
export declare class JointQuantumSystem {
|
|
5
|
+
entities: QuantumEntity[];
|
|
6
|
+
dimensions: number[];
|
|
7
|
+
totalDimension: number;
|
|
8
|
+
jointAmplitudes: SparseAmplitudes;
|
|
9
|
+
private constructor();
|
|
10
|
+
static create(entities: QuantumEntity[]): JointQuantumSystem;
|
|
11
|
+
static merge(involvedEntities: QuantumEntity[]): JointQuantumSystem;
|
|
12
|
+
private _calculateTensorProduct;
|
|
13
|
+
decomposeState(jointState: number): number[];
|
|
14
|
+
composeState(states: number[]): number;
|
|
15
|
+
applyLocal(entityIndex: number, operation: QuantumOperation): void;
|
|
16
|
+
applyToSubspace(targetEntities: QuantumEntity[], operation: QuantumOperation): void;
|
|
17
|
+
getTargetProbabilities(targetEntity: QuantumEntity): Record<number, number>;
|
|
18
|
+
getTargetAmplitudes(targetEntity: QuantumEntity): Record<number, Complex>;
|
|
19
|
+
measureTarget(targetEntity: QuantumEntity, forcedOutcome?: number): number;
|
|
20
|
+
getProjectedAmplitudes(conditions: {
|
|
21
|
+
entity: QuantumEntity;
|
|
22
|
+
state: number;
|
|
23
|
+
}[]): SparseAmplitudes;
|
|
24
|
+
getJointProbability(conditions: {
|
|
25
|
+
entity: QuantumEntity;
|
|
26
|
+
state: number;
|
|
27
|
+
}[]): number;
|
|
28
|
+
areMutuallyExclusive(conditions: {
|
|
29
|
+
entity: QuantumEntity;
|
|
30
|
+
state: number;
|
|
31
|
+
}[]): boolean;
|
|
32
|
+
getReducedDensityMatrix(targetEntity: QuantumEntity): Record<number, Record<number, Complex>>;
|
|
33
|
+
/**
|
|
34
|
+
* Create a joint system from existing amplitudes and entities (Bit-Perfect Restoration/Cloning).
|
|
35
|
+
*/
|
|
36
|
+
static inject(entities: QuantumEntity[], jointAmplitudes: SparseAmplitudes): JointQuantumSystem;
|
|
37
|
+
}
|
|
38
|
+
export interface TargetOperation {
|
|
39
|
+
entity: QuantumEntity;
|
|
40
|
+
operation: QuantumOperation;
|
|
41
|
+
}
|
|
42
|
+
export declare const EntanglementEngine: {
|
|
43
|
+
interactMany(entities: QuantumEntity[], operation: QuantumOperation): void;
|
|
44
|
+
conditionalInteract(args: {
|
|
45
|
+
controls: Predicate[];
|
|
46
|
+
targets: TargetOperation[];
|
|
47
|
+
}): void;
|
|
48
|
+
};
|