/** * Immutable circuit builder with IonQ QIS gate names. * * Each gate method returns a new Circuit — circuits are values, not mutated objects. * Simulation is lazy: nothing runs until `.run()` is called. */ import { Gate2x2, Gate4x4 } from './statevector.js'; import { Complex } from './complex.js'; import { DensityMatrix, DmNoiseParams } from './density.js'; /** Serialization tag carried on ops that have an IonQ JSON representation. */ type GateMeta = { name: string; params?: readonly number[]; }; type SingleOp = { kind: 'single'; q: number; gate: Gate2x2; meta?: GateMeta; }; type CNOTOp = { kind: 'cnot'; control: number; target: number; }; type SWAPOp = { kind: 'swap'; a: number; b: number; }; type TwoOp = { kind: 'two'; a: number; b: number; gate: Gate4x4; meta?: GateMeta; }; type ControlledOp = { kind: 'controlled'; control: number; target: number; gate: Gate2x2; meta?: GateMeta; }; type ToffoliOp = { kind: 'toffoli'; c1: number; c2: number; target: number; }; type CSwapOp = { kind: 'cswap'; control: number; a: number; b: number; }; type CsrSwapOp = { kind: 'csrswap'; control: number; a: number; b: number; }; type MeasureOp = { kind: 'measure'; q: number; creg: string; bit: number; }; type ResetOp = { kind: 'reset'; q: number; }; type IfOp = { kind: 'if'; creg: string; value: number; ops: readonly Op[]; }; /** A user-defined named gate applied to a specific set of parent-circuit qubits. */ type SubcircuitOp = { kind: 'subcircuit'; name: string; qubits: readonly number[]; def: readonly Op[]; }; /** Scheduling/grouping hint — no effect on the statevector; emitted as `barrier` in QASM. */ type BarrierOp = { kind: 'barrier'; qubits: readonly number[]; }; /** Arbitrary N-qubit unitary gate defined by its 2^N × 2^N matrix. */ type UnitaryOp = { kind: 'unitary'; qubits: readonly number[]; matrix: readonly (readonly Complex[])[]; }; /** * A gate whose angle parameters are symbolic strings, deferred until `.bind()` is called. * `params` may mix concrete numbers and string variable names (e.g. `['theta', 0.5]`). */ type ParametricOp = { kind: 'parametric'; name: string; params: readonly (number | string)[]; qubits: readonly number[]; }; type Op = SingleOp | CNOTOp | SWAPOp | TwoOp | ControlledOp | ToffoliOp | CSwapOp | CsrSwapOp | MeasureOp | ResetOp | IfOp | SubcircuitOp | BarrierOp | UnitaryOp | ParametricOp; /** Op after subcircuits have been expanded by flattenOps. Parametric ops are excluded — bind() first. */ export type FlatOp = Exclude; /** Per-gate error parameters for stochastic noise simulation. */ export interface NoiseParams { /** Single-qubit depolarizing error probability per gate (0–1). */ p1?: number; /** Two-qubit depolarizing error probability per gate (0–1). */ p2?: number; /** SPAM: probability of flipping each measured bit (0–1). */ pMeas?: number; } /** Hardware specs and noise parameters for a quantum device. */ export interface DeviceInfo { /** Maximum number of qubits the device supports. */ readonly qubits: number; /** Hardware-native gate set, if applicable (IonQ devices). */ readonly nativeGates?: readonly string[]; /** Published depolarizing + readout noise parameters. */ readonly noise: Readonly; } /** @deprecated Use `DeviceInfo` instead. */ export type IonQDeviceInfo = DeviceInfo & { readonly nativeGates: readonly string[]; }; /** * Published device specifications for all supported hardware. * Keys match the strings accepted by `run({ noise })`, `dm({ noise })`, and `runClifford({ noise })`. * * **IonQ** — depolarizing probabilities from published benchmarks: * `aria-1`, `forte-1`, `harmony` * * **IBM Quantum** — median SX (p1), ECR/CZ (p2), and readout (pMeas) error rates * from arXiv:2410.00916 (AbuGhanem, Aug 2024 calibrations): * `ibm_sherbrooke`, `ibm_brisbane`, `ibm_torino` * * **Quantinuum** — per-zone RB averages from the H-Series performance-validation page * (docs.quantinuum.com, 2024): * `h1-1`, `h2-1` */ export declare const DEVICES: Readonly>; /** IonQ-specific entries — used by `checkDevice()` and `compile()`. */ export declare const IONQ_DEVICES: Readonly>; /** A single gate entry in the IonQ `ionq.circuit.v0` JSON format. */ export interface IonQGate { gate: string; target?: number; targets?: [number, number]; control?: number; rotation?: number; phase?: number; phases?: [number, number]; } /** The `ionq.circuit.v0` circuit object accepted by IonQ Cloud and qsim. */ export interface IonQCircuit { format: 'ionq.circuit.v0'; qubits: number; circuit: readonly IonQGate[]; } /** The versioned JSON schema produced by `circuit.toJSON()` and consumed by `Circuit.fromJSON()`. */ export interface CircuitJSON { /** Format version — always 1 in this release. */ readonly ket: 1; readonly qubits: number; /** Classical register sizes: `{ c: 2 }` means register "c" has 2 bits. */ readonly cregs: Readonly>; /** Named gate definitions registered via `defineGate()`. */ readonly gates: Readonly>; readonly ops: readonly unknown[]; } export interface RunOptions { shots?: number; seed?: number; /** Named device profile (see `DEVICES` for all options) or custom NoiseParams. */ noise?: string | NoiseParams; /** Starting computational basis state as a bitstring (q0 leftmost). E.g. `'110'` = q0=1, q1=1, q2=0. */ initialState?: string; } export interface MpsRunOptions { shots?: number; seed?: number; /** Maximum bond dimension χ (default 64). Larger = more accurate for high-entanglement circuits. */ maxBond?: number; /** * Relative Schmidt truncation threshold (default 0 = off). * * Singular values σ_k < truncErr · σ_max are discarded in addition to the hard * `maxBond` cap. Useful for structured circuits (VQE, chemistry, QFT) where the * Schmidt spectrum decays quickly — a small `truncErr` (e.g. 1e-8) can reduce * effective bond dimension dramatically with negligible error. */ truncErr?: number; /** Starting computational basis state as a bitstring (q0 leftmost). E.g. `'110'` = q0=1, q1=1, q2=0. */ initialState?: string; /** * Depolarizing noise parameters, or a named device profile string (e.g. `'aria-1'`, `'ibm_sherbrooke'`). * * Enables the **quantum trajectory method**: each shot becomes an independent stochastic * trajectory — the circuit is re-executed once per shot with random Pauli errors injected * after each gate. Averaging over trajectories converges to the noisy mixed-state * distribution at rate 1/√shots. * * This is the standard technique for simulating NISQ hardware at large qubit counts. * Noise limits entanglement growth, keeping bond dimension χ tractable even at 100+ qubits. */ noise?: string | NoiseParams; /** * Number of parallel worker threads for the noisy trajectory path (default 0 = single-threaded). * * Workers run in true OS threads (Node.js `worker_threads`), giving an N× wall-time speedup * for the embarrassingly parallel trajectory loop. Each worker handles `shots / workers` * independent trajectories with a unique PRNG seed derived from the master seed. * * Only active when `noise` is set — the clean path builds the MPS once and samples are cheap. * Silently falls back to single-threaded in browsers or environments without `worker_threads`, * and also when running directly from `.ts` source (e.g. `tsx`, `ts-node`). Build the library * first (`npm run build`) to enable the parallel path. */ workers?: number; } /** * Measurement result — the output of running a circuit. * * `probs` keys are standard bitstrings: q0 is the leftmost character. * `histogram` keys are decimal integers (IonQ API convention). */ export declare class Distribution { readonly qubits: number; readonly shots: number; readonly probs: Readonly>; readonly histogram: Readonly>; /** Classical register results: `cregs[name][bit]` = fraction of shots where that bit was 1. */ readonly cregs: Readonly>; /** * `true` if the MPS bond dimension hit the `maxBond` cap during simulation and one or more * significant singular values were discarded. Results are approximate in this case. * * Always `false` for `run()` and `runClifford()`. * Increase `maxBond` or set `truncErr` to reduce approximation error. */ readonly truncated: boolean; constructor(qubits: number, shots: number, counts: Map, cregCounts?: Map, truncated?: boolean); /** Most probable bitstring. */ get most(): string; /** Shannon entropy of the distribution (in bits). */ get entropy(): number; /** ASCII bar chart of measurement outcomes. */ render(): string; /** * SVG bar chart of measurement outcomes — same visual style as the QAOA Max-Cut diagram. * * Bars are sorted by bitstring. Dominant peaks (≥ 80 % of the max probability) are * highlighted in blue with a percentage label; the rest render in slate. * * @param opts.title Override the subtitle line (default: `"measurement outcomes"`). * @param opts.highlight Explicit set of bitstrings to highlight instead of auto-detecting. * * @example * import fs from 'fs' * const result = new Circuit(2).h(0).cnot(0, 1).run({ shots: 1024, seed: 42 }) * fs.writeFileSync('bell.svg', result.toSVG()) */ toSVG(opts?: { title?: string; highlight?: readonly string[]; }): string; } export declare class Circuit { #private; readonly qubits: number; constructor(qubits: number, ops?: readonly Op[], cregs?: ReadonlyMap, gates?: ReadonlyMap); /** Identity gate — no-op on the statevector; preserved by name through import/export. */ id(q: number): Circuit; h(q: number): Circuit; x(q: number): Circuit; y(q: number): Circuit; z(q: number): Circuit; s(q: number): Circuit; si(q: number): Circuit; /** S† — alias `sdg` (Qiskit / OpenQASM convention). */ sdg(q: number): Circuit; t(q: number): Circuit; ti(q: number): Circuit; /** T† — alias `tdg` (Qiskit / OpenQASM convention). */ tdg(q: number): Circuit; v(q: number): Circuit; vi(q: number): Circuit; /** √NOT — alias `srn`; same as `v`. */ srn(q: number): Circuit; /** (√NOT)† — alias `srndg`; same as `vi`. */ srndg(q: number): Circuit; rx(theta: number | string, q: number): Circuit; ry(theta: number | string, q: number): Circuit; rz(theta: number | string, q: number): Circuit; /** * VirtualZ(θ) — named Rz alias common in superconducting hardware native gate sets * (IBM, Rigetti). Functionally identical to `rz(θ)` but carries the `vz` name through * import/export for hardware compilation pass awareness. */ vz(theta: number | string, q: number): Circuit; /** Rz(π/2) — phase rotation by a half-turn; S up to global phase. */ r2(q: number): Circuit; /** Rz(π/4) — phase rotation by a quarter-turn; T up to global phase. */ r4(q: number): Circuit; /** Rz(π/8) — phase rotation by an eighth-turn. */ r8(q: number): Circuit; /** U1(λ) — phase gate; equal to Rz(λ) up to global phase. */ u1(lambda: number | string, q: number): Circuit; /** P(λ) — phase gate alias for U1(λ); Qiskit 1.0+ name. P(π) = Z, P(π/2) = S, P(π/4) = T. */ p(lambda: number | string, q: number): Circuit; /** U2(φ, λ) = U3(π/2, φ, λ) — equatorial gate. U2(0, π) = H. */ u2(phi: number | string, lambda: number | string, q: number): Circuit; /** U3(θ, φ, λ) — general single-qubit unitary; OpenQASM 2.0 basis gate. */ u3(theta: number | string, phi: number | string, lambda: number | string, q: number): Circuit; /** Controlled-NOT. IonQ name: cnot. */ cnot(control: number, target: number): Circuit; swap(a: number, b: number): Circuit; /** XX(θ) = exp(−iθ/2 · X⊗X) — Ising-XX interaction; IonQ native. */ xx(theta: number | string, a: number, b: number): Circuit; /** YY(θ) = exp(−iθ/2 · Y⊗Y) — Ising-YY interaction; IonQ native. */ yy(theta: number | string, a: number, b: number): Circuit; /** ZZ(θ) = exp(−iθ/2 · Z⊗Z) — Ising-ZZ interaction; IonQ native. */ zz(theta: number | string, a: number, b: number): Circuit; /** XY(θ) interaction gate. XY(π) = iSWAP, XY(π/2) = √iSWAP. */ xy(theta: number | string, a: number, b: number): Circuit; /** iSWAP = XY(π): swaps qubits and multiplies each by i. */ iswap(a: number, b: number): Circuit; /** √iSWAP = XY(π/2): square root of iSWAP. */ srswap(a: number, b: number): Circuit; /** Controlled-NOT; alias for cnot. IBM/OpenQASM name. */ cx(control: number, target: number): Circuit; cy(control: number, target: number): Circuit; cz(control: number, target: number): Circuit; ch(control: number, target: number): Circuit; crx(theta: number | string, control: number, target: number): Circuit; cry(theta: number | string, control: number, target: number): Circuit; crz(theta: number | string, control: number, target: number): Circuit; /** Controlled-Rz(π/2) — controlled phase half-turn. */ cr2(control: number, target: number): Circuit; /** Controlled-Rz(π/4) — controlled phase quarter-turn. */ cr4(control: number, target: number): Circuit; /** Controlled-Rz(π/8) — controlled phase eighth-turn. */ cr8(control: number, target: number): Circuit; /** CU1(λ) — controlled phase gate; CU1(π) = CZ. */ cu1(lambda: number | string, control: number, target: number): Circuit; /** CU2(φ,λ) = CU3(π/2,φ,λ) — controlled equatorial gate. */ cu2(phi: number | string, lambda: number | string, control: number, target: number): Circuit; /** CU3(θ,φ,λ) — controlled general unitary; CU3(π,0,π) = CX. */ cu3(theta: number | string, phi: number | string, lambda: number | string, control: number, target: number): Circuit; cs(control: number, target: number): Circuit; ct(control: number, target: number): Circuit; csdg(control: number, target: number): Circuit; ctdg(control: number, target: number): Circuit; /** Controlled-√NOT (C-V); applies V = √X to target when control is |1⟩. */ csrn(control: number, target: number): Circuit; /** GPI(φ) — IonQ hardware-native single-qubit gate. GPI(0) = X, GPI(π/2) = Y. */ gpi(phi: number | string, q: number): Circuit; /** GPI2(φ) — IonQ hardware-native half-rotation. GPI2(0) = Rx(π/2), GPI2(π/2) = Ry(π/2). */ gpi2(phi: number | string, q: number): Circuit; /** MS(φ₀, φ₁) — Mølmer-Sørensen entangling gate; IonQ's native two-qubit operation. MS(0,0) = XX(π/2). */ ms(phi0: number | string, phi1: number | string, a: number, b: number): Circuit; /** Toffoli (CCX): flip target if both c1 and c2 are |1⟩. Universal for reversible computation. */ ccx(c1: number, c2: number, target: number): Circuit; /** Fredkin (CSWAP): swap qubits a and b if control is |1⟩. */ cswap(control: number, a: number, b: number): Circuit; /** C-√iSWAP: apply √iSWAP to qubits a and b if control is |1⟩. Completes the three-qubit gate set. */ csrswap(control: number, a: number, b: number): Circuit; /** * Barrier — scheduling/grouping hint with no effect on the statevector. * In QASM export it emits `barrier q[a],q[b],...;`. Pass the qubit indices to barrier. * Calling with no arguments barriers all qubits. */ barrier(...qubits: number[]): Circuit; /** * Apply a custom N-qubit unitary gate defined by its 2^N × 2^N matrix. * * `matrix` must be 2^N × 2^N where N = qubits.length. Entries may be * `Complex` objects `{ re, im }` or plain `number` (treated as real). * The qubit ordering matches all other multi-qubit gates: `qubits[0]` is the * MSB of the local state index. * * @example * // Real matrix * circuit.unitary([[1,0],[0,1]], 0) * * // Complex matrix * const S = [[{re:1,im:0},{re:0,im:0}],[{re:0,im:0},{re:0,im:1}]] * circuit.unitary(S, 0) */ unitary(matrix: readonly (readonly (number | Complex)[])[], ...qubits: number[]): Circuit; /** * Simulate the circuit and return the full sparse amplitude map. * Only valid for pure circuits (no `measure` / `reset` / `if` ops). * * @param initialState Optional starting computational basis state as a bitstring (q0 leftmost). */ statevector({ initialState }?: { initialState?: string; }): Map; /** * Return the 2^n × 2^n unitary matrix of the circuit. * * `matrix[row][col]` is the amplitude of basis state `|row⟩` after starting * from `|col⟩`. Row and column indices use **standard convention**: q0 is the * MSB, so the ordering is |00…0⟩, |00…1⟩, …, |11…1⟩ with the first qubit * varying slowest. This matches the convention of `unitary()`, textbooks, and * most quantum computing libraries. * * Note: the column/row index ordering here uses q0 as MSB of the integer index, * which differs from the public bitstring API where q0 is the leftmost character. * * Throws `TypeError` for circuits with mid-circuit measurement, reset, or * conditional ops. Throws `RangeError` for circuits wider than 12 qubits * (matrix would be 4096×4096 = 16M entries). * * @example * new Circuit(2).cnot(0, 1).circuitMatrix() * // [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]] — standard CNOT matrix */ circuitMatrix(): Complex[][]; /** * Return the complex amplitude for the basis state identified by `bitstring`. * Bitstring format: q0 is the leftmost character (standard convention), e.g. `'10'` = q0=1, q1=0. */ amplitude(bitstring: string): Complex; /** Return the measurement probability (|amplitude|²) for the given basis state bitstring. */ probability(bitstring: string): number; /** * Return the marginal probability P(qubit q = |1⟩) for each qubit. * Result[q] is the probability of measuring qubit q as 1, summed over all other qubits. * Only valid for pure circuits (no `measure` / `reset` / `if` ops). */ marginals({ initialState }?: { initialState?: string; }): number[]; /** * Return a human-readable representation of the statevector, e.g.: * `0.7071|00⟩ + 0.7071|11⟩` * `0.5|00⟩ + (0.5+0.5i)|01⟩ - 0.5i|10⟩` * * Amplitudes with magnitude² < 1e-10 are omitted. * Only valid for pure circuits (no `measure` / `reset` / `if` ops). */ stateAsString({ initialState }?: { initialState?: string; }): string; /** * Return the statevector as a sorted array of basis-state entries. * * Each entry contains: * - `bitstring` — q0-leftmost basis label, e.g. `'10'` = q0=1, q1=0 * - `re`, `im` — real and imaginary parts of the amplitude * - `prob` — measurement probability |amplitude|² * - `phase` — argument of the amplitude in radians: `Math.atan2(im, re)` * * Entries with prob < 1e-10 are omitted. Results are sorted by prob descending. * Only valid for pure circuits (no `measure` / `reset` / `if` ops). * * @example * new Circuit(2).h(0).cnot(0, 1).stateAsArray() * // [ * // { bitstring: '00', re: 0.7071, im: 0, prob: 0.5, phase: 0 }, * // { bitstring: '11', re: 0.7071, im: 0, prob: 0.5, phase: 0 }, * // ] */ stateAsArray(): { bitstring: string; re: number; im: number; prob: number; phase: number; }[]; /** Declare a classical register of `size` bits. */ creg(name: string, size: number): Circuit; /** * Measure qubit `q` in the computational basis, storing the outcome in * `creg[bit]`. Collapses the statevector for that shot. * Auto-registers the creg if not yet declared. */ measure(q: number, creg: string, bit: number): Circuit; /** * Reset qubit `q` to the given computational basis state (default |0⟩). * * - `reset(q)` / `reset(q, 0)` — unconditionally collapses `q` to |0⟩. * - `reset(q, 1)` — collapses to |0⟩ then flips to |1⟩, equivalent to `reset(q).x(q)`. */ reset(q: number, value?: 0 | 1): Circuit; /** * Conditionally apply a gate (or sequence of gates) only when the classical * register `creg` equals `value`. * * `value` is compared against the register as a little-endian integer: * bit 0 is the LSB. For a 1-bit register, `value` is simply 0 or 1. * * @example * // Apply X to q2 only if register 'c' == 1 * circuit.if('c', 1, c => c.x(2)) */ if(creg: string, value: number, build: (c: Circuit) => Circuit): Circuit; /** * Register a named reusable gate defined by `sub`. * * The registered gate can be used on this circuit (and any circuit derived * from it) via `.gate(name, ...qubits)`. * * @example * const bell = new Circuit(2).h(0).cnot(0, 1) * const c = new Circuit(4) * .defineGate('bell', bell) * .gate('bell', 0, 1) // Bell pair on qubits 0,1 * .gate('bell', 2, 3) // Bell pair on qubits 2,3 */ defineGate(name: string, sub: Circuit): Circuit; /** * Apply a previously registered named gate to the given parent-circuit qubits. * * The number of `qubits` must match the qubit count of the registered gate. * Qubit 0 of the gate maps to `qubits[0]`, qubit 1 to `qubits[1]`, etc. * * @example * circuit.gate('bell', 2, 3) // apply 'bell' gate to parent qubits 2 and 3 */ gate(name: string, ...qubits: number[]): Circuit; /** * Inline all named gates, returning a new `Circuit` containing only primitive ops. * * Required before serialization (toQASM, toQiskit, etc.) when the circuit * contains named gates applied via `.gate()`. * * @example * circuit.defineGate('bell', bell).gate('bell', 0, 1).decompose() */ decompose(): Circuit; /** * Append all ops from `other` onto this circuit and return the combined circuit. * * Both circuits must have the same qubit count. Classical registers and named * gate definitions from both circuits are merged: if the same register name * appears in both, the larger declared size wins; if the same gate name appears * in both, `this` takes precedence. * * @example * const prep = new Circuit(2).h(0).cnot(0, 1) * const meas = new Circuit(2).measure(0, 'c', 0).measure(1, 'c', 1) * const full = prep.compose(meas) * full.run({ shots: 1000 }) */ compose(other: Circuit): Circuit; /** * Return the names of all unbound symbolic parameters in this circuit, sorted. * * @example * new Circuit(1).rx('theta', 0).ry('phi', 0).params // ['phi', 'theta'] */ get params(): string[]; /** * Substitute symbolic parameter names with concrete numeric values and return * a new fully-bound circuit that can be simulated. * * Throws `TypeError` if any parameter referenced in the circuit is not supplied * in `values`. * * @example * // VQE-style: build once, sweep parameters * const ansatz = new Circuit(1).h(0).rx('theta', 0).rz('phi', 0) * for (const theta of [0, Math.PI / 4, Math.PI / 2]) { * const e = ansatz.bind({ theta, phi: 0.5 }).expectation('Z') * } */ bind(values: Record): Circuit; /** * Return the published specs for any supported device (IonQ, IBM, Quantinuum). * Throws if the device name is not recognised. * * @example * Circuit.device('ibm_sherbrooke').noise // { p1: 2.4e-4, p2: 7.4e-3, pMeas: 1.35e-2 } * Circuit.device('h1-1').qubits // 20 */ static device(name: string): DeviceInfo; /** * Return the published specs for a named IonQ device. * Throws if the device name is not recognised. * @deprecated Use `Circuit.device(name)` instead. */ static ionqDevice(name: string): IonQDeviceInfo; /** * Validate that this circuit can be submitted to the named IonQ device. * Throws a `TypeError` listing every issue found: * - qubit count exceeds device capacity * - gates that have no IonQ JSON representation (use `decompose()` or replace them) * * Call this before `toIonQ()` to get a complete error report rather than a * first-failure throw. */ checkDevice(name: string): void; /** * Parse an `ionq.circuit.v0` JSON object into a `Circuit`. * * Angle convention: `rotation` fields are in π-radians (1.0 = π rad); * `phase` / `phases` fields are in turns (1.0 = 2π rad). */ static fromIonQ({ qubits, circuit }: IonQCircuit): Circuit; /** * Serialize to an `ionq.circuit.v0` JSON object ready for IonQ Cloud or qsim. * * Throws `TypeError` for any gate that has no IonQ JSON representation * (controlled variants, U-gates, XY/iSWAP, mid-circuit measurement, etc.). */ toIonQ(): IonQCircuit; /** * Emit a valid OpenQASM 2.0 string for this circuit. * * Gate name mapping: si→sdg, ti→tdg, v→sx, vi→sxdg; r2/r4/r8→rz(π/n); * cs/ct/csdg/ctdg→cu1(±π/n); cr2/cr4/cr8→crz(π/n). * Throws `TypeError` for gates with no QASM 2.0 representation (gpi, gpi2, xx, yy, zz, ms, xy, iswap, srswap, if). */ toQASM(): string; /** * Parse an OpenQASM 2.0 or 3.0 string into a `Circuit`. Auto-detects the version. * * **2.0 syntax supported:** `qreg`/`creg`, `measure q[i] -> c[j]`, `//` comments, * all qelib1.inc gates (h, x, cx, rz, u1, u2, u3, ccx, cswap, …). * * **3.0 syntax supported:** qubit[N]/bit[N] declarations, "c[j] = measure q[i]" * assignment form, block comments, stdgates.inc, p/sx/sdg/tdg gate names. * * Not supported: gate definitions (`gate foo …`), gate modifiers (`ctrl @`, `inv @`), * `if`/`else` blocks, `gphase`, multi-register qubit indexing. */ static fromQASM(source: string): Circuit; /** * Parse a Quil 2.0 program and return an equivalent Circuit. * * Supported: all standard single/two/three-qubit gates, `CONTROLLED` prefix, * `DAGGER` prefix, `MEASURE`, `RESET`, `DECLARE BIT[]`. * Qubit count is inferred from the highest qubit index referenced. * `DEFGATE` and `PRAGMA` are silently skipped. * * @example * const c = Circuit.fromQuil('H 0\nCNOT 0 1\nMEASURE 0 ro[0]') */ static fromQuil(source: string): Circuit; /** * Parse Qiskit Python QuantumCircuit code back into a Circuit. * Accepts the output of toQiskit() — round-trips all gates supported by that method. */ static fromQiskit(source: string): Circuit; /** * Parse Cirq Python circuit code back into a Circuit. * Accepts the output of toCirq() — round-trips all gates supported by that method. */ static fromCirq(source: string): Circuit; /** * Parse a Qiskit Qobj JSON object into a Circuit. * Accepts the object returned by `qc.qobj()` or IBM Quantum job results. * Only the first experiment is used. */ static fromQobj(qobj: { experiments: ReadonlyArray<{ header: { n_qubits: number; creg_sizes?: ReadonlyArray; }; instructions: ReadonlyArray<{ name: string; qubits: number[]; params?: number[]; memory?: number[]; }>; }>; }): Circuit; /** * Emit Python code for Qiskit's `QuantumCircuit` API. * Gate coverage: full standard gate set, rx/ry/rz, u1/u2/u3, controlled family, * rxx/ryy/rzz/iswap. Throws for gpi/gpi2/ms/xy/srswap/if. */ toQiskit(): string; /** * Emit Python code for Google Cirq. * Gate coverage: H/X/Y/Z/S/T, rx/ry/rz, r2/r4/r8, u1/u3, * CNOT/CZ/CY/CH/swap/CCNOT/CSWAP, crx/cry/crz/cu1/cu3. * Throws for gpi/gpi2/ms/xx/yy/zz/xy/iswap/srswap/if. */ toCirq(): string; /** * Emit Python code for TensorFlow Quantum (TFQ). * * TFQ wraps Cirq circuits; qubits must be `cirq.GridQubit` instances. * The output includes the `tfq.convert_to_tensor` call needed to feed * the circuit into a TFQ layer. * * ```python * tensor = tfq.convert_to_tensor([circuit]) * ``` * * Same gate coverage and restrictions as `toCirq()`. */ toTFQ(): string; /** * Emit a Q# operation for Microsoft Azure Quantum. * Gate coverage: H/X/Y/Z/S/T and adjoints, Rx/Ry/Rz, CNOT/CZ/SWAP/CCNOT, * controlled rotations via Controlled Rx/Ry/Rz. Throws for gpi/gpi2/ms/two-qubit interaction gates/if. */ toQSharp(): string; /** * Emit Python code for Rigetti's pyQuil. * Gate coverage: H/X/Y/Z/S/T (Sdg/Tdg via DAGGER), RX/RY/RZ, * CNOT/CZ/SWAP/CCNOT/CSWAP/ISWAP. Throws for controlled-rotation gates, U-gates, gpi/gpi2/ms/if. */ toPyQuil(): string; /** * Emit a Quil (Quantum Instruction Language) program for Rigetti hardware. * * Gate coverage: I/H/X/Y/Z/S/T and daggers, RX/RY/RZ, PHASE, * CNOT/CZ/SWAP/ISWAP/CCNOT/CSWAP, controlled family via CONTROLLED/CPHASE. * Throws for gates with no Quil representation: gpi/gpi2/ms/xx/yy/zz/xy/srswap/u2/u3/cu2/cu3/if. */ toQuil(): string; /** * Emit a `quantikz` LaTeX environment for the circuit. * * The output is a self-contained `\begin{quantikz}...\end{quantikz}` block * using the `tikz-quantikz` package. Paste it into any LaTeX document that * loads `\usepackage{quantikz}` (and `\usepackage{amsmath}` for `\ket{}`). * * Gate coverage: all single-qubit gates, CNOT, SWAP, controlled family, * Toffoli, Fredkin, two-qubit interaction gates, measure, reset. * `if` ops are silently skipped (no standard quantikz representation). * * @example * console.log(new Circuit(2).h(0).cnot(0, 1).toLatex()) * // \begin{quantikz} * // \lstick{$q_{0}$} & \gate{H} & \ctrl{1} & \qw \\ * // \lstick{$q_{1}$} & \qw & \targ{} & \qw * // \end{quantikz} */ toLatex(): string; /** * Emit Python code for Amazon Braket's `Circuit` API. * * Gate coverage: full single-qubit set, rx/ry/rz, phaseshift (u1), xx/yy/zz/xy, * cnot/cy/cz/swap/iswap, ccnot/cswap, controlled family via control= kwarg. * Throws for gates with no Braket representation: gpi/gpi2/ms/srswap/u2/u3/cu2/cu3/measure/reset/if. */ toBraket(): string; /** * Emit a CUDA Quantum (cudaq) Python kernel. * * ```python * import math * import cudaq * * kernel = cudaq.make_kernel() * q = kernel.qalloc(2) * kernel.h(q[0]) * kernel.cx(q[0], q[1]) * ``` * * Limitations: classical ops (measure/reset/if), IonQ-native gates (gpi/gpi2/ms), * √X variants (v/vi), and interaction gates (xx/yy/zz/xy/iswap/srswap) have no * direct CudaQ equivalent and will throw. */ toCudaQ(): string; /** * Emit a Quirk (algassert.com/quirk) JSON circuit descriptor. * * Returns a JSON string `{"cols":[...]}` that can be pasted into Quirk's * "Load" dialog or appended to the URL as `#circuit=`. * * Column structure: each column is an array indexed by qubit. * `1` = idle wire, `"•"` = control, named strings or `{id, arg}` = gates. * Angles are in half-turns: arg = θ/π (Rx(π/2) → arg=0.5). * * Limitations: U2/U3/gpi/gpi2/ms, interaction gates (xx/yy/zz/xy/iswap/srswap), * and if/reset ops have no Quirk equivalent and will throw. * Measure ops emit `"Measure"`. U1 is approximated as Rz (same unitary up to global phase). */ toQuirk(): string; /** Run the circuit and return a probability distribution. */ run({ shots, seed, noise, initialState }?: RunOptions): Distribution; /** * Run the circuit using MPS (tensor-network) simulation. * * Efficient for circuits with bounded entanglement (GHZ, BV, shallow QFT, QAOA low-depth). * Memory: O(n · χ² · 2) vs O(2ⁿ) for full statevector. * Handles 50+ qubit circuits that would be intractable with the statevector backend. * * Limitations: no mid-circuit measure/reset/if; Toffoli and CSWAP * must be decomposed into single- and two-qubit gates first. * * With `noise`: switches to **quantum trajectory mode** — one full circuit execution per shot, * with random Pauli errors injected after each gate. Noise limits entanglement growth so bond * dimension stays tractable even at 100+ qubits. Simulates realistic NISQ hardware accurately. * * @param maxBond Maximum bond dimension χ. Default 64 — exact for GHZ/BV, approximate for deep random circuits. */ runMps({ shots, seed, maxBond, truncErr, initialState, noise: noiseRaw, workers: numWorkers }?: MpsRunOptions): Distribution; /** * Compute ⟨ψ|H|ψ⟩ for a Pauli-string Hamiltonian H = Σᵢ termᵢ.coeff · termᵢ.ops. * * Drop-in replacement for `vqe()` that scales to 50+ qubits via MPS. * Builds the MPS state once, then sweeps each term via transfer matrix — * exact to floating-point precision, O(|terms| · n · χ³). * * Uses the same `PauliTerm` convention as `vqe()`: * `ops[0]` acts on qubit n-1 (MSB); `ops[n-1]` acts on qubit 0 (LSB). * **Qubit 0 is the rightmost character** — `'ZZII'` means Z on qubits 3 and 2, identity on 1 and 0. * * @example * // Heisenberg ZZ + XX chain on 4 qubits — same terms work with vqe() too * const { energy } = circuit.expectMps([ * { coeff: -0.5, ops: 'ZZII' }, // ZZ on qubits 3,2 * { coeff: -0.5, ops: 'IZZI' }, // ZZ on qubits 2,1 * { coeff: -0.5, ops: 'XXII' }, // XX on qubits 3,2 * ]) */ expectMps(terms: readonly { coeff: number; ops: string; }[], { maxBond, truncErr, initialState }?: { maxBond?: number; truncErr?: number; initialState?: string; }): { energy: number; truncated: boolean; }; /** * Von Neumann entanglement entropies S_b = −Σ_k σ_k² log₂(σ_k²) at each bond. * * Builds the MPS state once from this circuit, then reads the Schmidt spectrum * stored at every bond in Vidal canonical form. Returns n−1 values. * * Entanglement entropy is the physicist's diagnostic for quantum correlations: * product states have S=0 everywhere, maximally entangled bonds have S=1, * and area-law states (ground states of local Hamiltonians) grow logarithmically. * * O(n · χ²) total — cheaper than any expectation value. * * @example * // Bell state: one bond, S = 1 (maximally entangled) * new Circuit(2).h(0).cnot(0, 1).bondEntropies() // → [1.0] * * // GHZ state: all bonds saturated at S = 1 * ghz(8).bondEntropies() // → [1, 1, 1, 1, 1, 1, 1] */ bondEntropies({ maxBond, truncErr, initialState }?: { maxBond?: number; truncErr?: number; initialState?: string; }): number[]; /** * Return exact floating-point probabilities from the statevector — no sampling variance. * * Keys are standard bitstrings (q0 leftmost). Only non-negligible amplitudes are included. * Throws for circuits containing mid-circuit measure, reset, or conditional ops. */ exactProbs({ initialState }?: { initialState?: string; }): Readonly>; /** * Compute the Pauli expectation value ⟨ψ|P|ψ⟩ for a tensor-product Pauli operator P. * * `pauli` is a string of length `qubits` over {I, X, Y, Z} (case-insensitive). * `pauli[q]` specifies the Pauli acting on qubit q (q0 leftmost, matching bitstring convention). * * Basis rotations: X → H, Y → Rx(π/2), Z/I → identity. * Throws `TypeError` for circuits with mid-circuit measure, reset, or if ops. * * @example * new Circuit(1).expectation('Z') // 1 (|0⟩ is +1 eigenstate of Z) * new Circuit(1).x(0).expectation('Z') // -1 (|1⟩ is −1 eigenstate of Z) * new Circuit(1).h(0).expectation('X') // 1 (|+⟩ is +1 eigenstate of X) * new Circuit(2).h(0).cnot(0,1).expectation('ZZ') // 1 (Bell state) */ expectation(pauli: string): number; /** * Serialize the circuit to a lossless JSON object. * * Gate matrices are **not** stored — they are reconstructed from their * names and parameters on load, so the output is compact and stable * across library versions. * * @example * const json = circuit.toJSON() * fs.writeFileSync('circuit.json', JSON.stringify(json, null, 2)) */ toJSON(): CircuitJSON; /** * Deserialize a circuit from a `CircuitJSON` object or a JSON string. * * The loaded circuit is fully functional — all gate methods, simulation, * serialization, and visualization APIs work identically to a hand-built circuit. * * @throws TypeError for unrecognised op kinds, unknown gate names, or wrong schema version. * * @example * const circuit = Circuit.fromJSON(fs.readFileSync('circuit.json', 'utf8')) */ static fromJSON(json: CircuitJSON | string): Circuit; /** * Generate a random circuit with `nQubits` qubits and `nGates` gates. * * Each gate is chosen uniformly at random from a standard set: H, X, Y, Z, * S, T, Rx, Ry, Rz (single-qubit) and CNOT, SWAP (two-qubit, 30% chance when * `nQubits ≥ 2`). Rotation angles are drawn uniformly from [0, 2π). * * Useful for benchmarking, quantum-volume estimation, and noise characterisation. * * @param nQubits Number of qubits (≥ 1). * @param nGates Number of gates to add. * @param seed Optional PRNG seed for reproducibility. * * @example * const c = Circuit.random(4, 20, 42) * c.run({ shots: 1024 }) */ static random(nQubits: number, nGates: number, seed?: number): Circuit; /** * Render a minimal ASCII circuit diagram. * * @example * new Circuit(2).h(0).cnot(0, 1).draw() * // q0: ─H──●─ * // │ * // q1: ─────⊕─ */ draw(): string; /** * Export the circuit as a self-contained SVG string. * * The diagram uses the same column layout as `draw()`. No external fonts or * stylesheets are required — the SVG embeds a monospace `font-family` stack. * * @example * fs.writeFileSync('bell.svg', new Circuit(2).h(0).cnot(0, 1).toSVG()) */ toSVG(): string; /** * Return the Bloch sphere angles (θ, φ) for qubit `q`. * * Computes the reduced single-qubit density matrix by tracing out all other * qubits, then extracts the Bloch vector (rx, ry, rz) and converts to * standard spherical coordinates: * - θ ∈ [0, π] — polar angle from |0⟩ (north pole) * - φ ∈ (-π, π] — azimuthal angle in the equatorial plane * * For pure product states the result is exact. For entangled qubits the * Bloch vector has |r| < 1 (mixed state) and the angles are still well * defined as long as the qubit is not maximally mixed (|r| > 0). * * @throws TypeError when called on circuits with measure/reset/if ops. */ blochAngles(q: number, { initialState }?: { initialState?: string; }): { theta: number; phi: number; }; /** * Return the critical path length — the minimum number of time steps needed * to execute this circuit when gates on independent qubits run in parallel. * * Barriers are scheduling hints only and do not increment depth. * IfOps recurse into their inner ops. */ depth(): number; /** * Return a self-contained SVG string showing the Bloch sphere for qubit `q`. * * Uses `blochAngles(q)` to get the state angles, then renders a 300×300 SVG * with the sphere outline, equatorial ellipse, axes, and a blue arrow for the * state vector using cavalier projection. * * @throws TypeError if the circuit contains measure/reset/if ops (see blochAngles). */ blochSphere(q: number): string; /** * Simulate this circuit using the CHP (Aaronson-Gottesman 2004) Clifford * stabilizer algorithm — exponentially faster than the statevector for * Clifford circuits, with exact probabilities. * * Only Clifford gates are supported: * h, x, y, z, s, si/sdg, measure, reset, barrier, cnot, swap, * and controlled gates cx/cy/cz. * * Non-Clifford gates (T, Rx(θ≠kπ/2), etc.) cause a TypeError. * * @param opts.shots Number of measurement shots (default 1024). * @param opts.seed Optional PRNG seed for reproducibility. * @param opts.noise Device name (any key of `DEVICES`, e.g. `'ibm_sherbrooke'`, `'h1-1'`) or * `{ p1?, p2?, pMeas? }` depolarizing + readout error rates. */ runClifford({ shots, seed, noise }?: { shots?: number; seed?: number; noise?: string | NoiseParams; }): Distribution; /** * Transpile this circuit to the native gate set of the specified IonQ device. * * Supported devices: 'aria-1', 'forte-1', 'harmony' — all use {GPI, GPI2, MS, VZ}. * * Single-qubit decompositions (all exact up to global phase): * h → vz(π/2) · gpi2(0) · vz(π/2) * x → gpi(0) * y → gpi(π/2) * z → vz(π) * s → vz(π/2) * si/sdg → vz(-π/2) * t → vz(π/4) * ti/tdg → vz(-π/4) * rz/vz → vz (pass through) * gpi/gpi2 → pass through * * Two-qubit: * cnot(a,b) → gpi2(π/2,a) · ms(0,0,a,b) · gpi2(3π/2,a) · vz(-π/2,a) · vz(-π/2,b) * swap(a,b) → three CNOT decompositions (via above) * ms → pass through * * Barriers pass through. All other gates throw TypeError. * * @param device Target device name ('aria-1', 'forte-1', 'harmony'). * @returns A new Circuit containing only native-gate ops. * @throws TypeError for unsupported gates or unknown device names. */ compile(device: string): Circuit; /** * Simulate the circuit as an exact density matrix and return it. * * Unlike `run()` (which samples) and `statevector()` (which is pure-state only), * `dm()` computes the full ρ = |ψ⟩⟨ψ| evolution and applies optional per-gate * depolarizing noise channels exactly — no sampling, no variance. * * Only valid for pure circuits (no `measure` / `reset` / `if` ops). * Complexity: O(4ⁿ) — practical up to ~12 qubits. * * @param options.noise Device name (any key of `DEVICES`, e.g. `'ibm_sherbrooke'`, `'h1-1'`) or * `{ p1?, p2? }` noise parameters. */ dm(options?: { noise?: DmNoiseParams | string; }): DensityMatrix; } export {}; //# sourceMappingURL=circuit.d.ts.map