/** * Matrix Product State (MPS) tensor network simulator — Float64Array edition. * * Tensors are stored as flat Float64Arrays with interleaved re/im: * T[l][p][r] at element index ((l * 2 + p) * chiR + r) * real part: data[((l * 2 + p) * chiR + r) * 2] * imag part: data[((l * 2 + p) * chiR + r) * 2 + 1] * * This eliminates all Complex object allocations in hot loops, reducing * GC pressure and improving cache locality by 3-8× over the previous * Complex[][][] layout. * * Handles 50+ qubit circuits with bounded entanglement (GHZ, BV, shallow QFT) * in O(n · chi² · 4) bytes — vs O(2ⁿ) for full statevector. */ import { type Complex } from './complex.js'; import type { Gate2x2, Gate4x4 } from './statevector.js'; /** * Rank-3 tensor stored as a flat Float64Array. * T[l][p][r] = data[((l*2+p)*chiR+r)*2] (re) and +1 (im). */ export type Tensor = { readonly data: Float64Array; readonly chiL: number; readonly chiR: number; }; /** Matrix Product State: one rank-3 tensor per qubit site. */ export type MPS = Tensor[]; export declare const CNOT4: Gate4x4; export declare const SWAP4: Gate4x4; /** Build a controlled-U gate4x4 (MSB = control). */ export declare function controlledGate([[a, b], [cc, d]]: Gate2x2): Gate4x4; /** Return the |0...0⟩ MPS with bond dimension 1. */ export declare function mpsInit(n: number): MPS; /** Apply a single-qubit gate to site q. O(chiL · chiR). */ export declare function mpsApply1(mps: MPS, q: number, [[a, b], [cc, d]]: Gate2x2): MPS; /** * Apply a two-qubit gate to sites a and b (a < b). * Non-adjacent pairs are handled by a SWAP network. */ export declare function mpsApply2(mps: MPS, a: number, b: number, gate: Gate4x4, maxBond: number): MPS; /** * Draw one sample from the MPS by sequential left-to-right marginal collapse. * Returns the sampled bitstring as a BigInt (qubit 0 = LSB). */ export declare function mpsSample(mps: MPS, rand: () => number): bigint; /** * Mutable, pre-allocated MPS for quantum trajectory simulation. * * Allocates all workspace at construction time. apply1, apply2, reset, * and sample are zero-allocation in the hot path — no Float64Array is * created after the constructor returns. * * Usage: construct once, then call reset() + circuit ops + sample() per shot. */ export declare class MpsTrajectory { readonly n: number; readonly maxChi: number; /** * Relative Schmidt truncation threshold. Singular values σ_k < truncErr · σ_max * are discarded in addition to the hard `maxChi` cap. * * 0 (default) means truncate by absolute tolerance only (1e-14). * Typical values: 1e-8 for chemistry/VQE circuits with rapidly decaying Schmidt spectra. */ readonly truncErr: number; readonly data: Float64Array[]; readonly chiL: Int32Array; readonly chiR: Int32Array; readonly bondLambda: Float64Array[]; private readonly mBuf; private readonly aBuf; private readonly qBuf; private readonly rBuf; private readonly vBuf; private readonly sigmaBuf; private readonly orderBuf; private readonly sv0re; private readonly sv0im; private readonly sv1re; private readonly sv1im; private readonly stRe; private readonly stIm; private readonly exOgBuf; private readonly exFBuf; private readonly exCurBuf; private readonly exNxtBuf; private readonly exDiag; /** Set to true by svdDecompose whenever the hard maxBond cap truncates a significant singular value. */ private wasTruncated_; /** True if any SVD during this trajectory discarded a singular value above the truncation threshold due to the maxBond cap. */ get wasTruncated(): boolean; constructor(n: number, maxChi: number, truncErr?: number); /** Reset to |0...0⟩. Sets all tensors to T[0][0][0]=1 with chiL=chiR=1. */ reset(): void; /** Apply a single-qubit gate in place. O(chiL · chiR). */ apply1(q: number, [[a, b], [c, d]]: Gate2x2): void; /** * Apply a two-qubit gate in place. * * `a` and `b` may be given in either order — if `a > b` the gate matrix is * qubit-transposed so the physical operation is identical regardless of argument order. * Non-adjacent pairs are handled via a SWAP network at O(|b-a|) adjacent applications. */ apply2(a: number, b: number, gate: Gate4x4): void; private apply2Adjacent; /** * One-sided complex Jacobi SVD on this.mBuf (rows × cols, row-major). * * Decomposes M ≈ U · diag(σ) · V† where: * qBuf — U (rows × bond, row-major): left singular vectors * rBuf — σ·V† (bond × cols, row-major): scaled right singular vectors * * Algorithm: sweep Jacobi rotations over column pairs until orthogonal, * then sort by descending singular value and truncate to maxChi. * Gives the optimal low-rank approximation at each MPS bond cut (Schmidt decomp). * * All buffers pre-allocated; zero heap allocation in this method. */ private svdDecompose; /** * Sample a basis state by sequential left-to-right marginal collapse. * No allocations — uses pre-allocated sv0/sv1/st workspace. */ sample(rand: () => number): bigint; /** Maximum bond dimension currently in use across all sites. */ maxBondUsed(): number; /** * Von Neumann entanglement entropies S_b = -Σ_k σ_k² log₂(σ_k²) at each bond. * Returns n-1 values. Uses the stored bondLambda Schmidt values (left-canonical). * O(n · χ) — suitable for circuit monitoring and truncation diagnostics. */ bondEntropies(): number[]; /** * Single-site expectation value ⟨ψ|I⊗…⊗O_q⊗…⊗I|ψ⟩ directly from MPS tensors. * * Exploits the Vidal canonical form: the left environment at site q is Λ[q-1]² and * the right environment is Λ[q]², so the result is exact with no sampling variance. * O(χ²) per call. * * @param q Site index (0-based). * @param op 2×2 gate matrix representing the observable. */ expect1(q: number, op: Gate2x2): Complex; /** * Expectation value ⟨ψ|O₀⊗O₁⊗…⊗O_{n-1}|ψ⟩ for a product observable. * * Pass `null` for identity at a site. Uses a left-to-right transfer matrix sweep in the * Vidal Γ-Λ basis. O(n·χ³) — far cheaper than building a density matrix or sampling. * * Fast path: leading identity sites cost O(χ) each (E stays diagonal). * After the first non-identity site the full O(χ³) contraction runs for remaining sites. * * Typical use: Hamiltonian terms in VQE, two-point correlators, Pauli strings. * * @param ops Array of length n. null = identity at that site. */ expectation(ops: readonly (Gate2x2 | null)[]): Complex; } /** Apply single-qubit depolarizing channel to a trajectory MPS in place. */ export declare function dep1Traj(traj: MpsTrajectory, q: number, p: number, rand: number): void; /** Apply two-qubit depolarizing channel to a trajectory MPS in place. */ export declare function dep2Traj(traj: MpsTrajectory, a: number, b: number, p: number, rand: number): void; /** * Normalized, serializable gate operation for trajectory execution. * * `controlled` and multi-qubit `unitary` ops from Circuit are pre-expanded * into `single` / `two` before being stored as TrajOp, so the worker dispatch * loop stays a flat switch with no helper calls. */ export type TrajOp = { kind: 'single'; q: number; gate: Gate2x2; } | { kind: 'cnot'; control: number; target: number; } | { kind: 'swap'; a: number; b: number; } | { kind: 'two'; a: number; b: number; gate: Gate4x4; } | { kind: 'barrier'; }; /** * Execute one trajectory: apply all ops with optional depolarizing noise. * rng() is only called when p1 or p2 is non-zero. */ export declare function applyTrajOps(traj: MpsTrajectory, ops: readonly TrajOp[], p1: number, p2: number, rng: () => number): void; /** * @internal — exposed for testing only. * Contract the full MPS into a dense 2^n amplitude array. * Returns Float64Array of length 2^n * 2 (interleaved re/im). * Only practical for n ≤ 20. */ export declare function mpsContract(mps: MPS): Float64Array; /** * @internal — exposed for testing only. * Return max bond dimension currently in the MPS. */ export declare function mpsMaxBond(mps: MPS): number; /** * @internal — exposed for testing only. * Return the tensor at site q (for inspection). */ export declare function mpsTensor(mps: MPS, q: number): Tensor; //# sourceMappingURL=mps.d.ts.map