Gates
Quantum gates transform the state of properties without measuring them. All gates are called via getModule() on your QuantumPropertyManager subclass. Every gate accepts an optional predicates parameter. Predicated gates create entanglement between properties (see Core Concepts: Predicated Operations).
Gate Reference
| Gate | Method | Dim | Default Fraction |
|---|---|---|---|
| Cycle | m.cycle(prop, fraction?, predicates?) | Any | 1 |
| Shift / X | m.shift(prop, fraction?, predicates?) | Any | 1 |
| Hadamard | m.hadamard(prop, fraction?, predicates?) | Any | 1 |
| Inverse Hadamard | m.inverse_hadamard(prop, predicates?) | Any | n/a |
| Clock / Z | m.clock(prop, fraction, predicates?) | Any | n/a |
| Y | m.y(prop, fraction?, predicates?) | 2 only | 1 |
| iSwap | m.i_swap(p1, p2, fraction, predicates?) | Any | n/a |
| Swap | m.swap(p1, p2, predicates?) | Any | n/a |
| Phase Rotate | m.phase_rotate(predicates, angle) | Any | n/a |
Where m = this.getModule(). Gate names are snake_case at the WASM level.
Conditional Gates (Predicates)
Every gate accepts an optional predicates parameter that conditions the gate on the state of other properties. This is the primary mechanism for creating entanglement. When a gate depends on another property's state, the two become correlated.
import type { PredicateSpec } from "quantum-forge/quantum";
const predicates: PredicateSpec[] = [
{ property: controlProp, value: 1, isEqual: true }, // control must be |1⟩
{ property: otherProp, value: 0, isEqual: false }, // other must NOT be |0⟩
];
const m = this.getModule();
// To build WASM predicate objects from PredicateSpec:
const wasmPreds = predicates.map(s =>
s.isEqual ? s.property.is(s.value) : s.property.is_not(s.value)
);
// Gate only applies when ALL predicates are satisfied
m.shift(targetProp, 1, wasmPreds); // CNOT (controlled-NOT), creates entanglement
m.hadamard(targetProp, 1, wasmPreds); // controlled-Hadamard, conditional superposition
m.i_swap(p1, p2, 0.5, wasmPreds); // controlled entanglementThe PredicateSpec type:
interface PredicateSpec {
property: QFProperty; // the property to check
value: number; // the basis state to compare against
isEqual: boolean; // true = "is this value", false = "is NOT this value"
}Phase Rotate
Applies a phase rotation conditioned on predicates. Unlike other gates, predicates are required. Phase rotation without a condition is meaningless.
m.phase_rotate(wasmPreds, Math.PI); // π phase flip on matching states
m.phase_rotate(wasmPreds, Math.PI / 4); // π/4 phase shiftSingle-Property Gates
Cycle
Cyclic permutation of basis states. For dimension 2, this is the NOT gate.
const m = this.getModule();
m.cycle(prop); // |0⟩ → |1⟩, |1⟩ → |0⟩ (dim=2)
m.cycle(prop, 0.5); // fractional cycle (√NOT)Game use case: Initialize a ball to |1⟩ (exists) after acquiring at |0⟩. Used in Quantum Pong's entangle-split.
Hadamard
Creates equal superposition from a definite state. The most common gate for putting objects "into quantum."
const m = this.getModule();
m.hadamard(prop); // |0⟩ → (|0⟩+|1⟩)/√2, |1⟩ → (|0⟩−|1⟩)/√2
m.hadamard(prop, 0.5); // fractional Hadamard (H^0.5)
m.hadamard(prop, 0.1); // very gentle, barely moves probabilitiesGame use case: Fractional Hadamard (H^t) for gradual probability diffusion. Small t = slow spread, large t = fast spread.
Inverse Hadamard
The adjoint (reverse) of Hadamard. Collapses a superposition back toward a definite state.
m.inverse_hadamard(prop); // (|0⟩+|1⟩)/√2 → |0⟩Clock / Z
Phase rotation (Z-rotation). Changes the phase of a state without changing measurement probabilities, until the property interacts with something else.
const m = this.getModule();
m.clock(prop, 1.0); // full Z gate: |1⟩ picks up a phase of π
m.clock(prop, 0.5); // half Z: |1⟩ picks up a phase of π/2Game use case: Quantum Pong's phase dial. Paddle hits bias subsequent entanglement interactions.
TIP
Phase alone doesn't change what you'll measure. It changes how the property interacts with other properties. Phase + iSwap = probability redistribution. See Phase & Interference.
Shift / X
Generalized bit-flip. For dimension 2, identical to cycle. For higher dimensions, shifts states: |0⟩→|1⟩→|2⟩→...→|d-1⟩→|0⟩.
m.shift(prop); // same as cycle for dim=2Y Gate
Pauli Y gate. Qubit-only, throws an error if dimension is not 2. Composite gate: S-X-S-dagger.
m.y(prop); // full Y gate (dim must be 2)
m.y(prop, 0.5); // fractional Y: rotate around Y-axis on Bloch sphereDANGER
m.y() throws if the property's dimension is not 2. This is intentional. The Y gate has no standard generalization to higher dimensions.
Two-Property Gates
iSwap
The primary entanglement gate. Creates quantum correlations between two properties.
const m = this.getModule();
m.i_swap(prop1, prop2, 0.5); // half iSwap, maximal entanglement
m.i_swap(prop1, prop2, 1.0); // full iSwap, complete state exchange with phase
m.i_swap(prop1, prop2, 0.25); // quarter iSwap, partial entanglementAt fraction=0.5, starting from |10⟩:
(|10⟩ + i|01⟩) / √2This means: 50% chance property 1 is |1⟩ and property 2 is |0⟩, and 50% chance the reverse, and they are correlated. Measuring one instantly determines the other.
WARNING
i_swap between properties in different shared states triggers a tensor product, growing the combined state space. This is why pooling is critical. Pooled properties are already in the shared state.
Game use case: Quantum Pong's entangle-split. Ball enters quantum zone, splits into correlated pair.
Swap
Direct state exchange between two properties. No entanglement, just moves quantum state from one to the other.
m.swap(prop1, prop2);Batch Gate Execution
When applying many gates in a tight loop (e.g. an AI sweep applying 50-100 gates per tick), each m.cycle() / m.shift() / etc. crosses the JS-WASM boundary separately. The overhead of those boundary crossings can dominate the actual gate math.
executeBatch() sends an entire array of gate operations in one WASM call, eliminating ~99% of boundary crossings.
import type { BatchOp, BatchResult } from "quantum-forge/quantum";
const m = this.getModule();
const ops: BatchOp[] = [
{ op: "hadamard", target: prop1 },
{ op: "cycle", target: prop2, predicates: [prop1.is(1)] },
{ op: "shift", target: prop3, fraction: 0.5 },
{ op: "swap", target: prop1, target2: prop2 },
{ op: "phase_rotate", angle: Math.PI / 4, predicates: [prop1.is(1)] },
];
const result: BatchResult = m.executeBatch(ops);
// result.success — true if all operations completed
// result.opsExecuted — number of operations that ran before stopping
// result.errorMessage — non-empty on failureBatchOp fields
| Field | Type | Required | Description |
|---|---|---|---|
op | OpCode | Yes | Gate name: "cycle", "shift", "clock", "x", "z", "y", "hadamard", "inverse_hadamard", "swap", "i_swap", "phase_rotate" |
target | QFProperty | Most gates | Primary target property (omit for phase_rotate) |
target2 | QFProperty | swap, i_swap | Second target |
fraction | number | No | Gate fraction. Omit for the non-fractional (discrete permutation) variant. |
angle | number | phase_rotate | Rotation angle in radians |
predicates | Predicate[] | No | WASM predicate objects (same as individual gate calls) |
Fractional vs. non-fractional
Omitting fraction calls the non-fractional gate (a discrete permutation, e.g. cycle increments the basis state by 1). Setting fraction: 1.0 calls the fractional gate at fraction 1.0, which is a continuous rotation and produces a different result. This matches the behavior of individual gate calls where m.cycle(prop) and m.cycle(prop, 1.0) are different operations.
Error handling
Operations execute sequentially. On the first error, execution stops — already-executed gates are not rolled back (quantum gates are side-effecting). Check result.opsExecuted to know how many completed.
const result = m.executeBatch(ops);
if (!result.success) {
console.warn(`Batch failed after ${result.opsExecuted}/${ops.length} ops: ${result.errorMessage}`);
}Tape-based batch (maximum throughput)
For 50+ operations, executeBatchTape() bypasses per-op marshaling entirely by accepting a pre-encoded Float64Array. The tape is bulk-copied to WASM in one memcpy.
import { executeBatchTape, OP } from "quantum-forge/quantum";
const properties = [prop1, prop2, prop3];
const tape = new Float64Array([
OP.HADAMARD, 0, -1, NaN, 0, 0, // hadamard(prop1)
OP.CYCLE, 1, -1, NaN, 0, 1, 0, 1, 1, // cycle(prop2) if prop1.is(1)
OP.SWAP, 0, 2, NaN, 0, 0, // swap(prop1, prop3)
]);
const result = executeBatchTape(properties, tape);See the API Reference for the full tape format and OP constants including ROTATE_BASIS_PAIR for fused basis-state rotations.
When to use batch vs. individual calls
| Approach | Best for | Throughput |
|---|---|---|
| Individual calls | Interactive gates, measurement-dependent logic | ~1 µs/gate |
executeBatch(ops) | Moderate batches, readable code | ~1 µs/gate (one WASM call) |
executeBatchTape(props, tape) | Pre-built sequences, 50+ ops, real-time animation | ~0.5 µs/gate (zero marshaling) |
Measurement is not included in batch operations. Use measure_properties() after the batch completes.
Fractional Gates
Most gates accept a fraction parameter for partial application:
fraction=1: full gate (default for cycle, shift, hadamard)fraction=0.5: square root of the gatefraction=0.1: very gentle, barely moves the statefraction=2: applies the gate twice (often identity)
Fractional gates are key to creating gradual quantum effects. For example, hadamard(prop, 0.05) for slow probability diffusion, or y(prop, 0.3) for precise Bloch sphere navigation.