QuantumPropertyManager
QuantumPropertyManager is the core abstraction for working with quantum properties in your game. Every game that uses Quantum Forge extends this class to create a game-specific registry that manages quantum property lifecycles.
The Pattern
Every production game built with Quantum Forge follows the same pattern:
- Extend
QuantumPropertyManagerwith a game-specific registry - Acquire properties (from pool or fresh)
- Apply gates to create superposition, entanglement, phase
- Measure to collapse superposition into definite outcomes
- Release measured properties back to the pool
import { QuantumPropertyManager, ensureLoaded } from "quantum-forge-framework/quantum";
class MyRegistry extends QuantumPropertyManager {
constructor(logger?: LoggerInterface) {
super({ dimension: 2, logger });
}
// Game-specific operations go here
}
await ensureLoaded();
const registry = new MyRegistry(logger);Constructor Options
| Option | Type | Default | Description |
|---|---|---|---|
dimension | number | 2 | Number of basis states per property (must be ≥ 2) |
logger | LoggerInterface | undefined | Optional structured logger |
The dimension determines how many states each property can be in. For most games, 2 (qubit — two states) is the right choice. Use higher dimensions only when your game design specifically needs more basis states.
Property Lifecycle
Acquire
const prop = this.acquireProperty();
// prop starts in |0⟩
// If pool has a reset property, reuses it (no tensor product growth)
// If pool is empty, creates a fresh property from the WASM moduleRegister with ID
this.setProperty("ball-1", prop);
// Later:
const p = this.getProperty("ball-1"); // retrieve by ID
const exists = this.hasProperty("ball-1"); // check existenceApply Gates
Use the protected gate wrapper methods (see Gates):
this.cycle(prop); // |0⟩ → |1⟩
this.hadamard(prop); // → superposition
this.iSwap(prop1, prop2, 0.5); // entangle two properties
this.clock(prop, 0.5); // phase rotationMeasure
const [value] = this.measureProperties([prop]);
// value is 0 or 1 (for dim=2), determined by quantum probabilitiesRelease to Pool
this.deleteProperty("ball-1"); // remove ID mapping
this.releaseProperty(prop, value); // reset to |0⟩ and return to poolCRITICAL
Always release properties after measurement. Without pooling, every new property grows the tensor product until the qudit limit is reached and operations fail. See Performance for details.
Complete Registry Example
From Quantum Pong — the canonical example:
class QuantumRegistry extends QuantumPropertyManager {
constructor(logger?: LoggerInterface) {
super({ dimension: 2, logger });
}
/** Split a classical ball into an entangled quantum pair */
entangleSplit(originalId: string, newId: string): void {
const prop1 = this.acquireProperty();
const prop2 = this.acquireProperty();
this.cycle(prop1); // |0⟩ → |1⟩ (exists)
// prop2 stays |0⟩ (doesn't exist)
this.iSwap(prop1, prop2, 0.5); // entangle: (|10⟩ + i|01⟩)/√2
this.setProperty(originalId, prop1);
this.setProperty(newId, prop2);
}
/** Get probability that a ball exists */
getExistenceProbability(id: string): number {
const prop = this.getProperty(id);
if (!prop) return 1.0; // classical balls always exist
const results = this.getModule().probabilities([prop]);
for (const r of results) {
if (r.qudit_values[0] === 1) return r.probability;
}
return 0;
}
/** Measure existence — collapses state, pools property */
measureExistence(id: string): number {
const prop = this.getProperty(id);
if (!prop) return 1;
const [value] = this.measureProperties([prop]);
this.deleteProperty(id);
this.releaseProperty(prop, value);
return value; // 1 = exists, 0 = ghost
}
}Read-Only Queries
For operations that don't change quantum state, use this.getModule() directly:
// Probability distribution (no collapse)
const probs = this.getModule().probabilities([prop]);
// Returns: [{ probability: 0.5, qudit_values: [0] }, { probability: 0.5, qudit_values: [1] }]
// Reduced density matrix (for phase/coherence)
const rdm = this.getModule().reduced_density_matrix([prop1, prop2]);
// Returns: [{ row_values, col_values, value: { real, imag } }, ...]TIP
Read-only queries are not recorded in the operation log. This is correct — they don't change state, so they don't need to be replayed.
String ID Mapping
The ID system maps human-readable strings to WASM property handles:
| Method | Description |
|---|---|
setProperty(id, prop) | Register a property with a string ID |
getProperty(id) | Get the WASM handle for an ID (or undefined) |
hasProperty(id) | Check if an ID has a registered property |
deleteProperty(id) | Remove the ID mapping (does NOT release the property) |
removeProperty(id) | Measure, release, and delete in one call |
Multiple Registries
Different quantum systems in your game can use separate registries:
const enemyRegistry = new QuantumEnemyRegistry(logger); // dim=2
const puzzleRegistry = new QuantumPuzzleRegistry(logger); // dim=4
// Each manages its own pool and qudit space independentlyPattern: Every Game Has a Registry
All five production games extend QuantumPropertyManager:
| Game | Registry Class | Dimension | Key Operations |
|---|---|---|---|
| Quantum Pong | QuantumRegistry | 2 | entangleSplit, quantumSplit, applyPhase, measureExistence |
| Quantris | QuantumPieceRegistry | 2 | setExistence, entangleGroup, measurePiece, getCoherenceInfo |
| Hex Diffusion | HexQuantumRegistry | 7 | diffuse (fractional H), interfere, barrier (phase rotate), battle |
| Bloch Invaders | BlochRegistry | 2 | rotateTheta (Y), rotatePhi (Z), measureBasis, entangleTwins |
| Ponq | PonqQuantumRegistry | 2 | chargePhase, entangleSplit, getCoherence (RDM), activateZone |
The dimension and operations vary, but the lifecycle pattern — acquire, gate, measure, release — is universal.