Skip to content

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

GateMethodDimDefault Fraction
Cyclem.cycle(prop, fraction?, predicates?)Any1
Shift / Xm.shift(prop, fraction?, predicates?)Any1
Hadamardm.hadamard(prop, fraction?, predicates?)Any1
Inverse Hadamardm.inverse_hadamard(prop, predicates?)Anyn/a
Clock / Zm.clock(prop, fraction, predicates?)Anyn/a
Ym.y(prop, fraction?, predicates?)2 only1
iSwapm.i_swap(p1, p2, fraction, predicates?)Anyn/a
Swapm.swap(p1, p2, predicates?)Anyn/a
Phase Rotatem.phase_rotate(predicates, angle)Anyn/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.

typescript
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 entanglement

The PredicateSpec type:

typescript
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.

typescript
m.phase_rotate(wasmPreds, Math.PI);       // π phase flip on matching states
m.phase_rotate(wasmPreds, Math.PI / 4);   // π/4 phase shift

Single-Property Gates

Cycle

Cyclic permutation of basis states. For dimension 2, this is the NOT gate.

typescript
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."

typescript
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 probabilities

Game 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.

typescript
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.

typescript
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 π/2

Game 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⟩.

typescript
m.shift(prop);         // same as cycle for dim=2

Y Gate

Pauli Y gate. Qubit-only, throws an error if dimension is not 2. Composite gate: S-X-S-dagger.

typescript
m.y(prop);          // full Y gate (dim must be 2)
m.y(prop, 0.5);     // fractional Y: rotate around Y-axis on Bloch sphere

DANGER

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.

typescript
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 entanglement

At fraction=0.5, starting from |10⟩:

(|10⟩ + i|01⟩) / √2

This 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.

typescript
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.

typescript
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 failure

BatchOp fields

FieldTypeRequiredDescription
opOpCodeYesGate name: "cycle", "shift", "clock", "x", "z", "y", "hadamard", "inverse_hadamard", "swap", "i_swap", "phase_rotate"
targetQFPropertyMost gatesPrimary target property (omit for phase_rotate)
target2QFPropertyswap, i_swapSecond target
fractionnumberNoGate fraction. Omit for the non-fractional (discrete permutation) variant.
anglenumberphase_rotateRotation angle in radians
predicatesPredicate[]NoWASM 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.

typescript
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.

typescript
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

ApproachBest forThroughput
Individual callsInteractive 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 gate
  • fraction=0.1: very gentle, barely moves the state
  • fraction=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.

Powered by Quantum Forge