QuantumPropertyManager (deprecated)
Deprecated in 3.0
QuantumPropertyManager and LegacyQuantumRecorder are deprecated in quantum-forge 3.0. They keep working through 3.x and are removed in 4.0. New code should declare properties with quantum() and let handles carry their own gates, measurement, and lifecycle. Migrating to 3.0 maps every manager call to its handle equivalent.
This page is a reference for code that still uses the manager. The 3.x manager works as an adapter, so a game can move over one system at a time: handles and a manager can live in the same program without claiming each other's WASM properties.
What the manager does
QuantumPropertyManager pools raw WASM properties behind string ids. A game extends it (or holds one) and calls gates through getModule(), using the WASM names:
import { QuantumPropertyManager, ensureLoaded } from "quantum-forge/quantum";
await ensureLoaded();
class MyRegistry extends QuantumPropertyManager {
constructor() {
super({ dimension: 2 });
}
spawn(id: string): void {
const prop = this.acquireProperty(); // starts at |0⟩
this.getModule().hadamard(prop);
this.setProperty(id, prop);
}
collapse(id: string): number {
const prop = this.getProperty(id);
if (!prop) return 0;
const [value] = this.getModule().measure_properties([prop]);
this.deleteProperty(id);
this.releaseProperty(prop, value); // reset to |0⟩ and return to the pool
return value;
}
}Constructor options
| Option | Type | Default | Description |
|---|---|---|---|
dimension | number | 2 | Number of basis states per property |
logger | LoggerInterface | undefined | Optional structured logger |
The dimension cannot exceed the loaded build's maximum (3 in the Qutrit Edition, 2 in the Qubit Edition). A manager constructed with a larger one fails when it creates its first property.
Public API
| Member | Description |
|---|---|
acquireProperty() | A property at |0⟩: pops one from the pool, or creates a fresh one |
releaseProperty(prop, value) | Reset from the measured value to |0⟩ and push onto the pool |
setProperty(id, prop) | Register a property under a string id |
getProperty(id) | The WASM property for an id, or undefined |
hasProperty(id) | Whether an id is registered |
deleteProperty(id) | Remove the id mapping. Does not release the property |
removeProperty(id) | Measure, release, and delete in one call |
getModule() | The WASM module, for gates and queries |
dimension | The configured dimension (read-only) |
clear() | Drop every id mapping and empty the pool |
size | Number of registered properties |
poolSize | Number of pooled properties |
setRecorder(recorder) | Attach a LegacyQuantumRecorder |
getRecorder() | The attached recorder, or undefined |
Measured values are basis indices (0, 1, ...), not declared values. Predicates are WASM objects built with prop.is(value) and prop.is_not(value), passed as the last argument of a getModule() gate. A predicated discrete gate passes undefined for the fraction: m.cycle(target, undefined, [control.is(1)]). Passing 1 there runs the fractional gate at 1.0, which reaches the same state by a slower path.
Pooling does not shrink a shared state
releaseProperty() resets a property but leaves it where it was. A property that ever interacted with another stays inside that shared state after release, so reusing it from the pool can put a "fresh" property into an old group and count against its qudit limit. dispose() on a 3.0 handle avoids this: it destroys a qudit that still shares a state instead of caching it.
LegacyQuantumRecorder
LegacyQuantumRecorder is the 2.x QuantumRecorder under a new name. It records operations on one manager, and it is the only way to replay a log recorded by 2.x. In 3.0, new QuantumRecorder(manager) throws a TypeError that points here.
import { LegacyQuantumRecorder } from "quantum-forge/quantum";
const recorder = new LegacyQuantumRecorder(registry);
registry.setRecorder(recorder);
recorder.startRecording();Once attached, the recorder logs the manager's lifecycle calls (acquireProperty, releaseProperty, setProperty, deleteProperty) by itself. Gates are not logged automatically. Record each one with recordOp():
const m = registry.getModule();
m.cycle(prop);
const index = recorder.getIndex(prop);
if (index !== undefined) recorder.recordOp({ op: "cycle", index, fraction: 1 });
const log = recorder.stopRecording(); // QuantumOperation[]replayLog(log) clears the manager, then replays each operation in order onto fresh WASM properties, with every measurement forced to its recorded outcome. Afterwards registry.getProperty(id) returns a property in the recorded state.
| Method | Description |
|---|---|
new LegacyQuantumRecorder(manager) | Create a recorder for one manager |
startRecording() | Begin recording and reset the log |
stopRecording() | Stop and return the log, a QuantumOperation[] |
isRecording() | Whether recording is active |
getOperationLog() | A copy of the log, even while recording |
replayLog(ops) | Clear the manager and replay from scratch |
recordOp(op) | Record a gate operation |
getIndex(prop) | The recorded index for a property |
buildWasmPredicates(specs) | WASM predicates from PredicateSpec[] |
serializePredicates(specs) | Predicates in log form |
How replay reads fraction
For cycle, shift, hadamard, and y, an entry with fraction: 1 and no predicates replays as the discrete gate. Any other fraction, or any predicates, replays through the fractional call. clock and i_swap always replay through the fractional call. So a fractional gate applied at exactly 1.0 with no predicates comes back as the discrete gate.
The legacy log is a bare array with no version field. QuantumRecorder in 3.0 uses a different format, { version: 1, entries }, and neither recorder reads the other's logs. See Recording and replay.