Lifecycle & State Management
Quantum properties consume resources in the WASM simulator. By default, properties persist in their shared state vector until measured and pooled. This page covers tools for explicit lifecycle control: destroying properties you no longer need, querying state budget, and isolating independent simulations.
Destroying Properties
When a property is no longer needed, you can destroy it to immediately remove it from the shared state vector, shrinking the state and freeing resources.
const m = manager.getModule();
// Property is done — destroy it
m.destroy(prop);destroy() factorizes the qudit out of any shared state vector. This is different from the measure-and-pool pattern:
| Approach | What happens | When to use |
|---|---|---|
| Measure + release | Collapses to a value, resets to |0\u27E9, returns to pool | Normal gameplay — you need the measurement result |
destroy() | Factorizes the qudit out of the state vector entirely | Cleanup — you don't need the result, you want to shrink the state |
Why destroy instead of letting properties go out of scope?
When a property handle goes out of scope (or you lose the reference), the qudit remains in the shared state vector. It's a dead qudit — it still consumes state space and slows down every operation on the remaining properties. destroy() removes it cleanly.
// BAD: property leaked into the state vector
function doSomething(registry: QuantumPropertyManager) {
const temp = registry.acquireProperty();
const m = registry.getModule();
m.hadamard(temp);
m.i_swap(temp, otherProp, 0.5);
// temp goes out of scope — its qudit is still in the shared state
}
// GOOD: explicit cleanup
function doSomething(registry: QuantumPropertyManager) {
const temp = registry.acquireProperty();
const m = registry.getModule();
m.hadamard(temp);
m.i_swap(temp, otherProp, 0.5);
// ... use the entanglement ...
m.destroy(temp); // qudit factorized out, state vector shrinks
}Checking validity after destroy
After calling destroy(), the property handle is invalid. Any operation on it will throw. Use is_valid() to check:
m.destroy(prop);
prop.is_valid(); // false
// m.hadamard(prop); // would throw — property is destroyedWARNING
Do not use a destroyed property for gates, measurement, or queries. Check is_valid() if you're unsure whether a property has been destroyed.
Replay and adapters
destroy() is particularly useful in recording/replay scenarios. When replaying a quantum log, the adapter creates temporary properties to rebuild state. Once replay is complete, those temporary properties should be destroyed rather than left in the state vector:
// After replaying a log, clean up any properties that aren't needed
for (const tempProp of replayTemporaries) {
m.destroy(tempProp);
}State Budget Queries
You can query the size of the quantum state at runtime. This enables backpressure: your game can make decisions based on how much state is currently allocated.
Per-property queries
const m = manager.getModule();
// How many qudits share this property's state vector?
const numQudits = m.num_active_qudits(prop);
// How many basis amplitudes are in the sparse state vector?
const sparseSize = m.state_vector_size(prop);Both queries are read-only and do not modify state.
| Query | Returns | Use case |
|---|---|---|
num_active_qudits(prop) | Number of qudits in the property's shared quantum state | Check entanglement group size |
state_vector_size(prop) | Current number of basis amplitudes (sparse) | Monitor memory pressure |
Global limit
import { getMaxStateSize } from "quantum-forge/quantum";
const limit = getMaxStateSize(); // 100,000 basis amplitudesgetMaxStateSize() returns the compile-time maximum number of basis amplitudes any single state vector can hold. The WASM module enforces this limit — operations that would exceed it throw an error instead of crashing.
Implementing backpressure
Use state budget queries to implement graceful degradation:
class QuantumRegistry extends QuantumPropertyManager {
private readonly STATE_BUDGET_WARN = 10_000;
private readonly STATE_BUDGET_HARD = 50_000;
canCreateQuantumObject(): boolean {
// Check if any existing property is already near the limit
for (const [, prop] of this.entries()) {
const size = this.getModule().state_vector_size(prop);
if (size > this.STATE_BUDGET_HARD) return false;
}
return true;
}
shouldPreferClassical(): boolean {
// When state is getting large, prefer classical behavior
for (const [, prop] of this.entries()) {
const size = this.getModule().state_vector_size(prop);
if (size > this.STATE_BUDGET_WARN) return true;
}
return false;
}
quantumSplit(originalId: string, newId: string): boolean {
if (!this.canCreateQuantumObject()) {
this.logger?.warn?.("State budget exceeded, falling back to classical");
return false;
}
const prop1 = this.getProperty(originalId);
if (!prop1) return false;
const prop2 = this.acquireProperty();
this.getModule().i_swap(prop1, prop2, 0.5);
this.setProperty(newId, prop2);
return true;
}
}TIP
State budget queries are cheap read-only operations. You can call them every frame without performance concern.
QuantumSimulation
QuantumSimulation creates an isolated simulation context. Properties created within a simulation are sandboxed: they can entangle with each other but not with properties from other simulations (or from the global context).
import { QuantumForge, ensureLoaded } from "quantum-forge/quantum";
await ensureLoaded();
const sim = QuantumForge.createSimulation();
const prop1 = sim.createProperty(2); // dimension 2
const prop2 = sim.createProperty(2);
const m = sim.getModule();
m.hadamard(prop1);
m.i_swap(prop1, prop2, 0.5); // works — same simulation
// Clean up: releases all properties and state vectors at once
sim.destroy();Why isolated simulations?
The primary use case is search tree exploration in games like quantum chess. When exploring possible moves, each branch of the search tree needs its own quantum state. Without isolation, all branches share (and grow) the same state vector:
// WITHOUT QuantumSimulation — all replays share state, vector grows unbounded
for (const move of candidateMoves) {
const props = replayUpTo(move); // creates properties in global context
const score = evaluate(props); // state vector includes ALL replays
cleanupReplay(props); // even with cleanup, tensor products already happened
}
// WITH QuantumSimulation — each replay is isolated
for (const move of candidateMoves) {
const sim = QuantumForge.createSimulation();
const props = replayUpTo(move, sim); // properties are sandboxed
const score = evaluate(props); // state vector is small (just this replay)
sim.destroy(); // everything released at once
}Cross-simulation entanglement is an error
Attempting to entangle properties from different simulations throws an error:
const simA = QuantumForge.createSimulation();
const simB = QuantumForge.createSimulation();
const propA = simA.createProperty(2);
const propB = simB.createProperty(2);
const m = simA.getModule();
// This throws — propA and propB are in different simulations
m.i_swap(propA, propB, 0.5);
// Error: [QuantumForgeError] Cannot entangle properties from different simulationsThis is a feature, not a limitation. It prevents accidental state vector growth from cross-contamination between independent quantum contexts.
Global context still works
QuantumSimulation is opt-in. The existing pattern of creating properties via QuantumPropertyManager.acquireProperty() or QuantumForge.createQuantumProperty() continues to work. Properties in the global context can entangle freely with each other, as before.
// These are equivalent — both use the global context
const prop = manager.acquireProperty();
const prop2 = QuantumForge.createQuantumProperty(2);
// Global properties can still entangle with each other
m.i_swap(prop, prop2, 0.5); // works fineUse QuantumSimulation when you need isolation. Use the global context for simple cases.
Simulation lifecycle
| Method | Description |
|---|---|
QuantumForge.createSimulation() | Create a new isolated simulation |
new QuantumSimulation() | Direct constructor (same result) |
sim.createProperty(dimension) | Create a property bound to this simulation |
sim.destroyProperty(prop) | Factorize one property out and free its resources |
sim.factorizeAllSeparable() | Split out any separable qudits across all shared states |
sim.getModule() | Access the WASM module for gate and query calls |
sim.destroy() | Release all properties and state vectors at once |
Incremental property destruction
For search algorithms that create many ancilla properties, use sim.destroyProperty(prop) to free resources incrementally instead of waiting for sim.destroy():
const sim = QuantumForge.createSimulation();
const board = sim.createProperty(2);
const ancilla = sim.createProperty(2);
const m = sim.getModule();
m.i_swap(board, ancilla, 0.5);
// ... use the entanglement ...
m.i_swap(board, ancilla, -0.5); // undo
// Ancilla is now separable — destroy it to free resources
sim.destroyProperty(ancilla);
// ancilla.is_valid() === false
// board keeps its quantum state (superposition preserved)destroyProperty automatically detects separable qudits after removing the destroyed property and splits them into independent states. This prevents qudit accumulation across many do/undo search cycles.
Destroying entangled properties
If the property is entangled with others (not separable), destroying it measures the qudit, which collapses the entangled partners' state. A console warning is emitted in this case. To avoid unintended collapse, uncompute the entanglement before destroying.
Explicit separability scanning
After undo operations that may have restored separability (e.g. i_swap(a,b,f) followed by i_swap(a,b,-f)), call sim.factorizeAllSeparable() to split separable qudits into independent states without destroying any properties:
// After undoing a sequence of operations
sim.factorizeAllSeparable();
// Separable qudits now have independent state vectorsWARNING
After calling sim.destroy(), all properties created within the simulation become invalid. Check prop.is_valid() if you hold references to them.
Lifecycle Summary
| Tool | Purpose | When to use |
|---|---|---|
| Measure + pool | Collapse and recycle a property | Normal gameplay |
destroy() | Remove a qudit from the state vector | Cleanup without measurement |
| State budget queries | Monitor state vector size | Backpressure, debug overlays |
QuantumSimulation | Isolate independent quantum contexts | Search trees, replay branches, testing |