ts-quantum
Version:
TypeScript library for quantum mechanics calculations and utilities
292 lines • 9.7 kB
JavaScript
;
/**
* Sparse matrix utilities for quantum operators
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeSparseZeros = exports.validateSparseMatrix = exports.extractDiagonalEntries = exports.isSparseDiagonalMatrix = exports.isIdentityMatrix = exports.sparseNorm = exports.sparseTrace = exports.sparseConjugateTranspose = exports.sparseTranspose = exports.denseToSparse = exports.sparseToDense = exports.sparseMatrixMultiply = exports.sparseVectorMultiply = exports.getSparseEntry = exports.setSparseEntry = exports.createSparseMatrix = void 0;
const math = __importStar(require("mathjs"));
/**
* Create an empty sparse matrix
*/
function createSparseMatrix(rows, cols) {
return {
rows,
cols,
entries: [],
nnz: 0
};
}
exports.createSparseMatrix = createSparseMatrix;
/**
* Add entry to sparse matrix
*/
function setSparseEntry(matrix, row, col, value) {
// Remove existing entry if present
const existingIndex = matrix.entries.findIndex(entry => entry.row === row && entry.col === col);
if (existingIndex !== -1) {
matrix.entries.splice(existingIndex, 1);
matrix.nnz--;
}
// Add new entry if non-zero
if (!isZeroComplex(value)) {
matrix.entries.push({ row, col, value });
matrix.nnz++;
}
}
exports.setSparseEntry = setSparseEntry;
/**
* Get entry from sparse matrix
*/
function getSparseEntry(matrix, row, col) {
const entry = matrix.entries.find(e => e.row === row && e.col === col);
return entry ? entry.value : math.complex(0, 0);
}
exports.getSparseEntry = getSparseEntry;
/**
* Multiply sparse matrix by dense vector
*/
function sparseVectorMultiply(matrix, vector) {
if (matrix.cols !== vector.length) {
throw new Error(`Matrix columns (${matrix.cols}) must match vector length (${vector.length})`);
}
const result = Array.from({ length: matrix.rows }, () => math.complex(0, 0));
for (const entry of matrix.entries) {
const product = math.multiply(entry.value, vector[entry.col]);
if (typeof product !== 'object' || !('re' in product) || !('im' in product)) {
throw new Error('Invalid complex multiplication result');
}
result[entry.row] = math.add(result[entry.row], product);
}
return result;
}
exports.sparseVectorMultiply = sparseVectorMultiply;
/**
* Multiply two sparse matrices
*/
function sparseMatrixMultiply(a, b) {
if (a.cols !== b.rows) {
throw new Error(`Matrix A columns (${a.cols}) must match matrix B rows (${b.rows})`);
}
const result = createSparseMatrix(a.rows, b.cols);
// Create column index for matrix B for efficient access
const bCols = {};
for (const entry of b.entries) {
if (!bCols[entry.row]) {
bCols[entry.row] = [];
}
bCols[entry.row].push(entry);
}
// Multiply
for (const aEntry of a.entries) {
const bCol = bCols[aEntry.col];
if (bCol) {
for (const bEntry of bCol) {
const row = aEntry.row;
const col = bEntry.col;
const product = math.multiply(aEntry.value, bEntry.value);
const existing = getSparseEntry(result, row, col);
const sum = math.add(existing, product);
setSparseEntry(result, row, col, sum);
}
}
}
return result;
}
exports.sparseMatrixMultiply = sparseMatrixMultiply;
/**
* Convert sparse matrix to dense
*/
function sparseToDense(matrix) {
const dense = Array.from({ length: matrix.rows }, () => Array.from({ length: matrix.cols }, () => math.complex(0, 0)));
for (const entry of matrix.entries) {
dense[entry.row][entry.col] = entry.value;
}
return dense;
}
exports.sparseToDense = sparseToDense;
/**
* Convert dense matrix to sparse
*/
function denseToSparse(dense) {
if (dense.length === 0) {
return createSparseMatrix(0, 0);
}
const rows = dense.length;
const cols = dense[0].length;
// Validate matrix is rectangular
if (!dense.every(row => row.length === cols)) {
throw new Error('Input matrix must be rectangular (all rows must have same length)');
}
const matrix = createSparseMatrix(rows, cols);
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (!isZeroComplex(dense[i][j])) {
setSparseEntry(matrix, i, j, dense[i][j]);
}
}
}
return matrix;
}
exports.denseToSparse = denseToSparse;
/**
* Transpose sparse matrix
*/
function sparseTranspose(matrix) {
const result = createSparseMatrix(matrix.cols, matrix.rows);
for (const entry of matrix.entries) {
setSparseEntry(result, entry.col, entry.row, entry.value);
}
return result;
}
exports.sparseTranspose = sparseTranspose;
/**
* Conjugate transpose sparse matrix
*/
function sparseConjugateTranspose(matrix) {
const result = createSparseMatrix(matrix.cols, matrix.rows);
for (const entry of matrix.entries) {
const conjugate = math.conj(entry.value);
setSparseEntry(result, entry.col, entry.row, conjugate);
}
return result;
}
exports.sparseConjugateTranspose = sparseConjugateTranspose;
/**
* Calculate trace of sparse matrix
*/
function sparseTrace(matrix) {
if (matrix.rows !== matrix.cols) {
throw new Error('Trace requires square matrix');
}
let trace = math.complex(0, 0);
for (const entry of matrix.entries) {
if (entry.row === entry.col) {
trace = math.add(trace, entry.value);
}
}
return trace;
}
exports.sparseTrace = sparseTrace;
/**
* Calculate Frobenius norm of sparse matrix
*/
function sparseNorm(matrix) {
let sum = 0;
for (const entry of matrix.entries) {
const magnitude = Number(math.abs(entry.value));
sum += magnitude * magnitude;
}
return Math.sqrt(sum);
}
exports.sparseNorm = sparseNorm;
/**
* Check if matrix is identity
*/
function isIdentityMatrix(matrix, tolerance = 1e-12) {
if (matrix.rows !== matrix.cols) {
return false;
}
const n = matrix.rows;
// Check if we have exactly n entries (diagonal)
if (matrix.nnz !== n) {
return false;
}
// Check each entry is on diagonal and equals 1
for (const entry of matrix.entries) {
if (entry.row !== entry.col) {
return false;
}
const realPart = Number(entry.value.re);
const imagPart = Number(entry.value.im);
if (Math.abs(realPart - 1) > tolerance || Math.abs(imagPart) > tolerance) {
return false;
}
}
return true;
}
exports.isIdentityMatrix = isIdentityMatrix;
/**
* Check if matrix is diagonal
*/
function isSparseDiagonalMatrix(matrix) {
for (const entry of matrix.entries) {
if (entry.row !== entry.col) {
return false;
}
}
return true;
}
exports.isSparseDiagonalMatrix = isSparseDiagonalMatrix;
/**
* Extract diagonal entries from sparse matrix
*/
function extractDiagonalEntries(matrix) {
const diagonal = new Array(Math.min(matrix.rows, matrix.cols)).fill(0).map(() => math.complex(0, 0));
for (const entry of matrix.entries) {
if (entry.row === entry.col) {
diagonal[entry.row] = entry.value;
}
}
return diagonal;
}
exports.extractDiagonalEntries = extractDiagonalEntries;
/**
* Validate sparse matrix structure
*/
function validateSparseMatrix(matrix) {
// Check dimensions
if (matrix.rows <= 0 || matrix.cols <= 0) {
return false;
}
// Check entries are within bounds
for (const entry of matrix.entries) {
if (entry.row < 0 || entry.row >= matrix.rows ||
entry.col < 0 || entry.col >= matrix.cols) {
return false;
}
}
// Check nnz matches entries length
return matrix.nnz === matrix.entries.length;
}
exports.validateSparseMatrix = validateSparseMatrix;
/**
* Remove entries that are effectively zero
*/
function removeSparseZeros(matrix, tolerance = 1e-12) {
matrix.entries = matrix.entries.filter(entry => !isZeroComplex(entry.value, tolerance));
matrix.nnz = matrix.entries.length;
}
exports.removeSparseZeros = removeSparseZeros;
/**
* Check if complex number is effectively zero
*/
function isZeroComplex(value, tolerance = 1e-12) {
if (typeof value !== 'object' || !('re' in value) || !('im' in value)) {
throw new Error('Invalid complex number');
}
return Math.abs(value.re) < tolerance && Math.abs(value.im) < tolerance;
}
//# sourceMappingURL=sparse.js.map