Entanglement
Entanglement is the mechanic that makes quantum games fundamentally different from classical ones. When two properties are entangled, they share a quantum state. Measuring one instantly determines the other. No classical system can do this.
How Entanglement Happens
There are two ways to entangle properties in Quantum Forge:
- Predicated gates: any gate conditioned on another property's state entangles them
i_swap: a two-property gate designed specifically for entanglement
Both work. Which you use depends on the correlations you want.
Predicated Gates (CNOT and Friends)
A predicated gate fires only when a control property satisfies a condition. Because the control is in superposition, the gate creates a correlation, and correlation between quantum properties is entanglement.
The simplest example: a predicated shift on a qubit is the CNOT gate.
const control = this.acquireProperty();
const target = this.acquireProperty();
const m = this.getModule();
m.cycle(control); // |0⟩ → |1⟩
m.hadamard(control); // → (|0⟩ + |1⟩)/√2
// CNOT: flip target when control is |1⟩
m.shift(target, 1, [control.is(1)]);
// Result: (|00⟩ + |11⟩)/√2
// Measuring control as 0 → target is 0
// Measuring control as 1 → target is 1
// Positively correlatedThis generalizes to any gate. A predicated hadamard puts the target into superposition only when the control allows it. A predicated clock rotates phase conditionally. Each creates a different kind of entanglement with different correlations.
// Controlled-Hadamard: target enters superposition only if control is |1⟩
m.hadamard(target, 1, [control.is(1)]);
// Multi-control: gate fires only when BOTH controls are |1⟩
m.shift(target, 1, [controlA.is(1), controlB.is(1)]);
// Negative predicate: gate fires when control is NOT |0⟩
m.cycle(target, 1, [control.is_not(0)]);iSwap
i_swap is a two-property entanglement gate. At fraction=0.5, starting from |10⟩:
|10⟩ → (|10⟩ + i|01⟩) / √2This means:
- 50% chance prop1 is
|1⟩and prop2 is|0⟩ - 50% chance prop1 is
|0⟩and prop2 is|1⟩ - These outcomes are anti-correlated. Exactly one of the pair is
|1⟩ - Measuring either one instantly collapses the other
this.getModule().i_swap(prop1, prop2, fraction);Where predicated gates create positive correlations (both same, or one depends on the other), i_swap naturally creates anti-correlations (exactly one of two). Use it for "object splits into two ghosts, only one is real."
i_swap also accepts predicates, enabling controlled entanglement:
// Only entangle if a gate property is |1⟩
m.i_swap(prop1, prop2, 0.5, [gateProp.is(1)]);Choosing Between Them
| Mechanism | Correlation | Typical Use |
|---|---|---|
| Predicated shift (CNOT) | Positive, both match | Linked states: both alive or both dead |
| Predicated hadamard | Conditional superposition | One object's "quantumness" depends on another |
i_swap(0.5) | Anti-correlated, exactly one | Object splits into two ghosts |
i_swap + phase | Tunable bias | Player-influenced split probability |
Entanglement Patterns
Pattern 1: Entangle-Split (iSwap)
A classical object enters a quantum zone and splits into an entangled pair. One property starts at |1⟩ (exists), the other at |0⟩ (doesn't exist), then i_swap(0.5) creates anti-correlated superposition.
entangleSplit(originalId: string, newId: string): void {
const prop1 = this.acquireProperty();
const prop2 = this.acquireProperty();
const m = this.getModule();
m.cycle(prop1); // |0⟩ → |1⟩ (exists)
// prop2 stays at |0⟩ (doesn't exist)
m.i_swap(prop1, prop2, 0.5); // entangle
this.setProperty(originalId, prop1);
this.setProperty(newId, prop2);
}After entangle-split:
- Both objects exist with ~50% probability
- Measuring one to "exists" collapses the other to "doesn't exist"
- The game shows both objects as semi-transparent ghosts
Pattern 2: Correlated Pair (CNOT)
Two objects whose states always match, both alive or both dead. Uses predicated shift instead of iSwap.
createLinkedPair(id1: string, id2: string): void {
const prop1 = this.acquireProperty();
const prop2 = this.acquireProperty();
const m = this.getModule();
m.cycle(prop1); // |1⟩ (exists)
m.hadamard(prop1); // superposition
m.shift(prop2, 1, [prop1.is(1)]); // CNOT: prop2 matches prop1
this.setProperty(id1, prop1);
this.setProperty(id2, prop2);
}After CNOT pair:
- State is
(|00⟩ + |11⟩)/√2 - Measuring one to "exists" means the other also exists
- Measuring one to "gone" means the other is also gone
Pattern 3: Quantum-Split (iSwap)
Split an already quantum object. The new property starts at |0⟩ and entangles with the existing one. Pooled properties are preferred because they're already in the shared state (no tensor product growth).
quantumSplit(originalId: string, newId: string): boolean {
const prop1 = this.getProperty(originalId);
if (!prop1) return false;
const prop2 = this.acquireProperty(); // prefers pooled
this.getModule().i_swap(prop1, prop2, 0.5);
this.setProperty(newId, prop2);
return true;
}Pattern 4: Overlap Entanglement
Objects that spatially overlap become entangled. Adjacent hexes at shared boundaries, overlapping pieces in a puzzle, etc.
entangleNeighbors(hexA: string, hexB: string): void {
const propA = this.getProperty(hexA);
const propB = this.getProperty(hexB);
if (!propA || !propB) return;
this.getModule().i_swap(propA, propB, 0.5);
}Pattern 5: Conditional Superposition
A gate property controls whether another object enters superposition. The gate property's state determines the target's behavior:
conditionalQuantum(targetId: string, controlId: string): void {
const target = this.getProperty(targetId);
const control = this.getProperty(controlId);
if (!target || !control) return;
// Target enters superposition ONLY when control is |1⟩
this.getModule().hadamard(target, 1, [control.is(1)]);
}Tensor Product Growth
When an entangling operation connects properties from different shared states, the WASM module computes a tensor product to merge them into one combined state. This is the most expensive operation in quantum simulation.
What triggers it:
i_swap(propA, propB, ...)where propA and propB are in different shared states- Any predicated gate where the control and target are in different shared states
- This typically happens when both properties were freshly acquired (not from pool)
What prevents it:
- Pooled properties are already in the shared state, so operations between a pooled property and an existing one are cheap
- Measuring and releasing properties via
releaseProperty()keeps the pool full
What happens when you run out:
- The shipped build supports up to 12 qudits in a single entangled system
- Exceeding the limit throws an error
- Your game should handle this gracefully (see the
quantumSplitexample above)
try {
this.getModule().i_swap(prop1, prop2, 0.5);
} catch {
this.logger?.warn?.("Qudit limit reached, cannot entangle");
this.releaseProperty(prop2, 0); // return unused property
return false;
}Entanglement and Measurement
When you measure one property of an entangled pair:
- That property collapses to a definite value
- The entangled partner's probabilities update instantly
- For anti-correlated pairs (
i_swap(0.5)from|10⟩), measuring one to|1⟩forces the other to|0⟩ - For positively correlated pairs (CNOT from
(|00⟩+|11⟩)/√2), measuring one to|1⟩forces the other to|1⟩too
This is what makes quantum scoring work in Quantum Pong: measuring one ball's existence immediately determines the other ball's existence.
Visualizing Entanglement
The common pattern: read probability via this.getModule().probabilities([prop]) and map it to visual opacity, size, color intensity, or glow. In Quantum Pong, entangled balls are rendered as semi-transparent ghosts whose opacity tracks existence probability.