UNPKG

ts-quantum

Version:

TypeScript library for quantum mechanics calculations and utilities

446 lines 18.5 kB
/** * Quantum information tools * * Provides quantum information theoretic operations including * entropy calculations, Schmidt decomposition, fidelity measures, * and other quantum information-related operations. */ import { StateVector } from '../states/stateVector'; import { eigenDecomposition, multiplyMatrices } from './matrixOperations'; import { matrixSquareRoot } from './matrixFunctions'; import * as math from 'mathjs'; /** * Performs a Schmidt decomposition of a bipartite pure state * * Given a pure state |ψ⟩ in a bipartite system H = H_A ⊗ H_B, * computes the Schmidt decomposition: |ψ⟩ = ∑_i λ_i |i_A⟩ ⊗ |i_B⟩ * * This is fundamental for characterizing entanglement in bipartite systems. * * @param state Bipartite pure state to decompose * @param dimA Dimension of the first subsystem * @param dimB Dimension of the second subsystem * @returns Object containing Schmidt coefficients, and basis states for subsystems A and B */ export function schmidtDecomposition(state, dimA, dimB) { // Check dimensions if (state.dimension !== dimA * dimB) { throw new Error('State dimension must equal product of subsystem dimensions'); } // Reshape state vector to matrix const matrix = []; for (let i = 0; i < dimA; i++) { matrix[i] = []; for (let j = 0; j < dimB; j++) { const index = i * dimB + j; matrix[i][j] = state.amplitudes[index]; } } // Compute reduced density matrix ρB = TrA(|ψ⟩⟨ψ|) const reducedRhoB = Array(dimB).fill(null).map(() => Array(dimB).fill(null).map(() => math.complex(0, 0))); for (let i = 0; i < dimB; i++) { for (let j = 0; j < dimB; j++) { for (let k = 0; k < dimA; k++) { const term = math.multiply(math.conj(matrix[k][i]), matrix[k][j]); reducedRhoB[i][j] = math.add(reducedRhoB[i][j], term); } } } // Get eigenvalues and eigenvectors of reduced density matrix const { values, vectors } = eigenDecomposition(reducedRhoB, { computeEigenvectors: true }); // console.log(vectors, typeof vectors); // Create paired indices and Schmidt values, then filter and sort const indexValuePairs = values.map((val, idx) => ({ index: idx, value: Math.sqrt(Math.max(0, val.re)) })) .filter(pair => pair.value > 1e-14) // Filter before sorting .sort((a, b) => b.value - a.value); // Sort by descending value // Extract sorted and filtered Schmidt values and indices const schmidtValues = indexValuePairs.map(pair => pair.value); const filteredIndices = indexValuePairs.map(pair => pair.index); // Get right Schmidt basis vectors (eigenvectors of ρB) if (!vectors || vectors.length === 0) { throw new Error('Invalid eigenvectors in Schmidt decomposition'); } const statesB = filteredIndices.map(i => { const vector = vectors[i]; // Add debugging statements // console.log('Processing vector:', vector); // console.log('Vector type:', typeof vector); // console.log('Is array?', Array.isArray(vector)); // console.log('Vector properties:', Object.getOwnPropertyNames(vector)); // Ensure proper normalization const norm = Math.sqrt(vector.reduce((sum, v) => sum + v.re * v.re + v.im * v.im, 0)); // console.log('Calculated norm:', norm); const amplitudes = vector.map(v => math.divide(v, math.complex(norm, 0))); // const amplitudes = math.divide(math.matrix(vector), norm).valueOf(); return new StateVector(dimB, amplitudes); }); // Calculate left Schmidt basis vectors using M|v⟩/λ if (!vectors || vectors.length === 0) { throw new Error('Invalid eigenvectors in Schmidt decomposition'); } const statesA = filteredIndices.map((i, idx) => { const schmidt = schmidtValues[idx]; const v = vectors[i]; const amplitudes = Array(dimA).fill(null).map(() => math.complex(0, 0)); // Calculate M|v⟩ and normalize for (let j = 0; j < dimA; j++) { for (let k = 0; k < dimB; k++) { const term = math.multiply(matrix[j][k], v[k]); amplitudes[j] = math.add(amplitudes[j], term); } } // Normalize by Schmidt value const finalAmps = amplitudes.map(a => math.divide(a, math.complex(schmidt, 0))); return new StateVector(dimA, finalAmps); }); return { values: schmidtValues, statesA, statesB }; } /** * Calculates the trace distance between two quantum states * * For density matrices ρ and σ, the trace distance is: * D(ρ,σ) = (1/2)Tr|ρ-σ| where |A| = √(A†A) * * The trace distance is a measure of distinguishability between quantum states. * * @param A First operator (usually a density matrix) * @param B Second operator (usually a density matrix) * @returns The trace distance (between 0 and 1) */ export function traceDistance(A, B) { if (A.dimension !== B.dimension) { throw new Error('Operators must have the same dimension for trace distance'); } // Get matrix representations const matrixA = A.toMatrix(); const matrixB = B.toMatrix(); // Calculate A - B directly using matrices const diffMatrix = matrixA.map((row, i) => row.map((elem, j) => math.subtract(elem, matrixB[i][j]))); // Calculate adjoint of (A-B) const adjointDiffMatrix = diffMatrix.map((row, i) => row.map((elem, j) => math.complex(diffMatrix[j][i].re, -diffMatrix[j][i].im))); // Calculate (A-B)†(A-B) const product = multiplyMatrices(adjointDiffMatrix, diffMatrix); // Take positive square root of eigenvalues and sum diagonal elements const { values } = eigenDecomposition(product); const sqrtValues = values.map(v => Math.sqrt(Math.max(0, v.re))); // Calculate trace and divide by 2 return sqrtValues.reduce((sum, val) => sum + val, 0) / 2; } /** * Calculates fidelity between two pure states * * For pure states |ψ⟩ and |φ⟩, F(|ψ⟩,|φ⟩) = |⟨ψ|φ⟩|² * * Fidelity is a measure of "closeness" between two quantum states. * * @param stateA First pure state * @param stateB Second pure state * @returns The fidelity (between 0 and 1) */ export function fidelity(stateA, stateB) { if (stateA.dimension !== stateB.dimension) { throw new Error('States must have the same dimension for fidelity'); } // Calculate inner product ⟨ψ|φ⟩ const innerProduct = stateA.innerProduct(stateB); // Get magnitude squared of the complex number const magnitude = math.abs(innerProduct); return typeof magnitude === 'number' ? magnitude * magnitude : magnitude.re * magnitude.re; } /** * Calculates trace fidelity between mixed states * * For density matrices ρ and σ, F(ρ,σ) = [Tr(√(√ρσ√ρ))]² * This is a generalization of fidelity to mixed states. * * @param rho First density matrix * @param sigma Second density matrix * @returns The fidelity (between 0 and 1) */ export function traceFidelity(rho, sigma) { if (rho.dimension !== sigma.dimension) { throw new Error('Density matrices must have the same dimension for fidelity'); } const rhoMatrix = rho.toMatrix(); const sigmaMatrix = sigma.toMatrix(); // console.log(rhoMatrix); // Calculate √ρ const sqrtRho = matrixSquareRoot(rhoMatrix); // Calculate √ρ σ √ρ const temp = multiplyMatrices(sqrtRho, sigmaMatrix); const product = multiplyMatrices(temp, sqrtRho); // Calculate √(√ρ σ √ρ) const sqrtProduct = matrixSquareRoot(product); // Calculate trace let trace = 0; for (let i = 0; i < rho.dimension; i++) { trace += sqrtProduct[i][i].re; } // Return trace squared return trace * trace; } /** * Calculates quantum relative entropy S(ρ||σ) = Tr(ρ log ρ - ρ log σ) * * Quantum relative entropy measures how distinguishable one quantum * state is from another. It's analogous to the Kullback-Leibler divergence. * * @param rho First density matrix * @param sigma Second density matrix * @returns The quantum relative entropy (non-negative) */ export function quantumRelativeEntropy(rho, sigma) { if (rho.dimension !== sigma.dimension) { throw new Error('Density matrices must have the same dimension for relative entropy'); } // Get eigendecomposition of rho const rhoMatrix = rho.toMatrix(); const sigmaMatrix = sigma.toMatrix(); const { values: rhoEigenvalues, vectors: rhoEigenvectors } = eigenDecomposition(rhoMatrix, { computeEigenvectors: true }); // Calculate log(σ) by eigendecomposition const { values: sigmaEigenvalues, vectors: sigmaEigenvectors } = eigenDecomposition(sigmaMatrix, { computeEigenvectors: true }); // Convert eigenvalues to log values const logSigmaEigenvalues = sigmaEigenvalues.map(v => v.re > 1e-10 ? math.complex(Math.log(v.re), 0) : math.complex(-1000, 0) // Use a large negative number as approximation ); // Reconstruct log(σ) in the eigenbasis of σ const logSigma = Array(rho.dimension).fill(null).map(() => Array(rho.dimension).fill(null).map(() => math.complex(0, 0))); if (!sigmaEigenvectors) { throw new Error('Failed to compute eigenvectors for sigma'); } for (let i = 0; i < rho.dimension; i++) { for (let j = 0; j < rho.dimension; j++) { for (let k = 0; k < rho.dimension; k++) { if (sigmaEigenvectors[k] && sigmaEigenvectors[k][i] && sigmaEigenvectors[k][j]) { const term1 = math.multiply(sigmaEigenvectors[k][i], math.conj(sigmaEigenvectors[k][j])); const term2 = math.multiply(term1, logSigmaEigenvalues[k]); logSigma[i][j] = math.add(logSigma[i][j], term2); } } } } // Calculate Tr(ρ log ρ) let trRhoLogRho = 0; for (let i = 0; i < rho.dimension; i++) { if (rhoEigenvalues[i].re > 1e-10) { trRhoLogRho += rhoEigenvalues[i].re * Math.log(rhoEigenvalues[i].re); } } // Calculate Tr(ρ log σ) let trRhoLogSigma = 0; for (let i = 0; i < rho.dimension; i++) { for (let j = 0; j < rho.dimension; j++) { trRhoLogSigma += rhoMatrix[i][j].re * logSigma[j][i].re; } } return trRhoLogRho - trRhoLogSigma; } /** * Calculates the von Neumann entropy S(ρ) = -Tr(ρ log ρ) * * The von Neumann entropy is the quantum analog of classical Shannon entropy. * * @param rho Density matrix * @returns Entropy value (non-negative) */ export function vonNeumannEntropy(rho) { const matrix = rho.toMatrix(); const { values } = eigenDecomposition(matrix); let entropy = 0; for (const value of values) { const p = value.re; if (p > 1e-10) { entropy -= p * Math.log(p); } } return entropy; } /** * Calculates the entanglement entropy of a bipartite pure state * * For a pure state, this is the von Neumann entropy of either reduced density matrix. * * @param state Pure state * @param dimA Dimension of first subsystem * @param dimB Dimension of second subsystem * @returns Entanglement entropy */ export function entanglementEntropy(state, dimA, dimB) { // Perform Schmidt decomposition to get Schmidt coefficients const { values } = schmidtDecomposition(state, dimA, dimB); // Calculate entropy from Schmidt coefficients let entropy = 0; for (const lambda of values) { const p = lambda * lambda; // Squared Schmidt coefficient if (p > 1e-10) { entropy -= p * Math.log(p); } } return entropy; } /** * Calculates the linear entropy 1 - Tr(ρ²) of a quantum state * * This is a simpler alternative to von Neumann entropy and measures * how mixed a quantum state is. * * @param rho Density matrix * @returns Linear entropy (between 0 and 1-1/d) */ export function linearEntropy(rho) { return 1 - rho.purity(); } /** * Calculates the quantum mutual information I(A:B) = S(A) + S(B) - S(AB) * * Measures the total correlation between subsystems A and B. * * @param rhoAB Joint density matrix of bipartite system * @param dimA Dimension of first subsystem * @param dimB Dimension of second subsystem * @returns Quantum mutual information */ export function quantumMutualInformation(rhoAB, dimA, dimB) { if (rhoAB.dimension !== dimA * dimB) { throw new Error('Density matrix dimension must match product of subsystem dimensions'); } // Calculate entropy of joint state const jointEntropy = vonNeumannEntropy(rhoAB); // Calculate reduced density matrices // Trace out subsystem B to get rhoA, trace out subsystem A to get rhoB const rhoA = rhoAB.partialTrace([dimA, dimB], [1]); // Trace out subsystem B (index 1) const rhoB = rhoAB.partialTrace([dimA, dimB], [0]); // Trace out subsystem A (index 0) // Calculate entropies of reduced states const entropyA = vonNeumannEntropy(rhoA); const entropyB = vonNeumannEntropy(rhoB); return entropyA + entropyB - jointEntropy; } /** * Calculates concurrence for 2-qubit density matrix * * Concurrence is an entanglement measure for two-qubit systems. * For a density matrix ρ, C(ρ) = max(0, λ₁-λ₂-λ₃-λ₄) * where λᵢ are the square roots of eigenvalues of ρ(σy⊗σy)ρ*(σy⊗σy). * * @param rho Two-qubit density matrix * @returns Concurrence value (between 0 and 1) */ export function concurrence(rho) { if (rho.dimension !== 4) { throw new Error('Concurrence only defined for 2-qubit states'); } const rhoMatrix = rho.toMatrix(); // Define σy⊗σy const sigmaY = [ [math.complex(0, 0), math.complex(0, -1)], [math.complex(0, 1), math.complex(0, 0)] ]; // Calculate σy⊗σy const sigmaYY = Array(4).fill(null).map(() => Array(4).fill(null).map(() => math.complex(0, 0))); for (let i1 = 0; i1 < 2; i1++) { for (let j1 = 0; j1 < 2; j1++) { for (let i2 = 0; i2 < 2; i2++) { for (let j2 = 0; j2 < 2; j2++) { const i = i1 * 2 + i2; const j = j1 * 2 + j2; sigmaYY[i][j] = math.multiply(sigmaY[i1][j1], sigmaY[i2][j2]); } } } } // Calculate ρ(σy⊗σy) const rhoSigmaYY = multiplyMatrices(rhoMatrix, sigmaYY); // Calculate complex conjugate of ρ const rhoStar = rhoMatrix.map(row => row.map(elem => math.conj(elem))); // Calculate ρ(σy⊗σy)ρ*(σy⊗σy) const rhoSigmaYYRhoStar = multiplyMatrices(rhoSigmaYY, rhoStar); const R = multiplyMatrices(rhoSigmaYYRhoStar, sigmaYY); // Calculate R†R which is guaranteed to be Hermitian const RDagger = R.map((row, i) => row.map((_, j) => math.conj(R[j][i]))); const RRDagger = multiplyMatrices(R, RDagger); // Find eigenvalues of RR† which are guaranteed to be real and non-negative const { values } = eigenDecomposition(RRDagger); // Take square roots and sort in descending order const sqrtValues = values.map(v => Math.sqrt(Math.sqrt(Math.max(0, v.re)))) .sort((a, b) => b - a); // Calculate concurrence const concurrence = Math.max(0, sqrtValues[0] - sqrtValues[1] - sqrtValues[2] - sqrtValues[3]); return concurrence; } /** * Calculates negativity for bipartite system * * Negativity is an entanglement measure based on the partial transpose criterion. * N(ρ) = (||ρᵀᴬ||₁ - 1)/2, where ||A||₁ is the trace norm. * * @param rho Density matrix of bipartite system * @param dimA Dimension of first subsystem * @param dimB Dimension of second subsystem * @returns Negativity value (≥ 0) */ export function negativity(rho, dimA, dimB) { if (rho.dimension !== dimA * dimB) { throw new Error('Density matrix dimension must match product of subsystem dimensions'); } const rhoMatrix = rho.toMatrix(); // Calculate partial transpose with respect to subsystem A const rhoTA = Array(dimA * dimB).fill(null).map(() => Array(dimA * dimB).fill(null).map(() => math.complex(0, 0))); for (let i1 = 0; i1 < dimA; i1++) { for (let i2 = 0; i2 < dimB; i2++) { for (let j1 = 0; j1 < dimA; j1++) { for (let j2 = 0; j2 < dimB; j2++) { const i = i1 * dimB + i2; const j = j1 * dimB + j2; const iTA = j1 * dimB + i2; const jTA = i1 * dimB + j2; rhoTA[iTA][jTA] = rhoMatrix[i][j]; } } } } // Calculate eigenvalues of the partial transpose const { values } = eigenDecomposition(rhoTA); // Calculate trace norm (sum of absolute values of eigenvalues) const traceNorm = values.reduce((sum, v) => sum + Math.abs(v.re), 0); // Calculate negativity return (traceNorm - 1) / 2; } /** * Calculates the one-way quantum discord D(A|B) * * Quantum discord measures the quantum correlations that * cannot be captured by classical correlations. * * Note: This is a simplified implementation that assumes * projective measurements on subsystem B. * * @param rho Density matrix of bipartite system * @param dimA Dimension of first subsystem * @param dimB Dimension of second subsystem * @returns One-way quantum discord */ export function quantumDiscord(rho, dimA, dimB) { if (rho.dimension !== dimA * dimB) { throw new Error('Density matrix dimension must match product of subsystem dimensions'); } // Calculate quantum mutual information const mutualInfo = quantumMutualInformation(rho, dimA, dimB); // Calculate reduced density matrix for subsystem B const rhoB = rho.partialTrace([dimA, dimB], [0]); // Trace out subsystem A (index 0) // For a complete implementation, we would need to minimize // over all projective measurements on subsystem B. // This is a computationally intensive task. // For this simplified version, we'll consider only computational basis // measurements and provide a placeholder value // Placeholder for classical correlation const classicalCorrelation = 0.5 * mutualInfo; // Discord = Quantum mutual information - Classical correlation return mutualInfo - classicalCorrelation; } //# sourceMappingURL=information.js.map