class VectorPool {
pools = new Map();
maxPoolSize = 100;
acquire(size) {
const pool = this.pools.get(size);
if (pool && pool.length > 0) {
return pool.pop();
}
return new Array(size);
}
release(vector) {
const size = vector.length;
vector.fill(0); let pool = this.pools.get(size);
if (!pool) {
pool = [];
this.pools.set(size, pool);
}
if (pool.length < this.maxPoolSize) {
pool.push(vector);
}
}
clear() {
this.pools.clear();
}
getStats() {
const poolSizes = {};
let totalVectors = 0;
for (const [size, pool] of this.pools) {
poolSizes[size] = pool.length;
totalVectors += pool.length;
}
return { poolSizes, totalVectors };
}
}
export class CSRMatrix {
values;
colIndices;
rowPtr;
rows;
cols;
constructor(rows, cols, nnz) {
this.rows = rows;
this.cols = cols;
this.values = new Float64Array(nnz);
this.colIndices = new Uint32Array(nnz);
this.rowPtr = new Uint32Array(rows + 1);
}
static fromCOO(matrix) {
const { values, rowIndices, colIndices } = matrix;
const nnz = values.length;
const csr = new CSRMatrix(matrix.rows, matrix.cols, nnz);
const triplets = Array.from({ length: nnz }, (_, i) => ({
row: rowIndices[i],
col: colIndices[i],
val: values[i],
index: i
}));
triplets.sort((a, b) => a.row - b.row || a.col - b.col);
let currentRow = 0;
let nnzCount = 0;
for (const triplet of triplets) {
if (triplet.val === 0)
continue;
while (currentRow < triplet.row) {
csr.rowPtr[++currentRow] = nnzCount;
}
csr.values[nnzCount] = triplet.val;
csr.colIndices[nnzCount] = triplet.col;
nnzCount++;
}
while (currentRow < matrix.rows) {
csr.rowPtr[++currentRow] = nnzCount;
}
return csr;
}
multiplyVector(x, result) {
result.fill(0);
const blockSize = 4;
let rowBlock = 0;
while (rowBlock < this.rows) {
const endBlock = Math.min(rowBlock + blockSize, this.rows);
for (let row = rowBlock; row < endBlock; row++) {
const start = this.rowPtr[row];
const end = this.rowPtr[row + 1];
let sum = 0;
let i = start;
for (; i < end - 3; i += 4) {
sum += this.values[i] * x[this.colIndices[i]] +
this.values[i + 1] * x[this.colIndices[i + 1]] +
this.values[i + 2] * x[this.colIndices[i + 2]] +
this.values[i + 3] * x[this.colIndices[i + 3]];
}
for (; i < end; i++) {
sum += this.values[i] * x[this.colIndices[i]];
}
result[row] = sum;
}
rowBlock = endBlock;
}
}
getEntry(row, col) {
const start = this.rowPtr[row];
const end = this.rowPtr[row + 1];
let left = start;
let right = end - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const midCol = this.colIndices[mid];
if (midCol === col) {
return this.values[mid];
}
else if (midCol < col) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
return 0;
}
*rowEntries(row) {
const start = this.rowPtr[row];
const end = this.rowPtr[row + 1];
for (let i = start; i < end; i++) {
yield { col: this.colIndices[i], val: this.values[i] };
}
}
getMemoryUsage() {
return this.values.byteLength +
this.colIndices.byteLength +
this.rowPtr.byteLength;
}
getNnz() {
return this.values.length;
}
getRows() {
return this.rows;
}
getCols() {
return this.cols;
}
}
export class CSCMatrix {
values;
rowIndices;
colPtr;
rows;
cols;
constructor(rows, cols, nnz) {
this.rows = rows;
this.cols = cols;
this.values = new Float64Array(nnz);
this.rowIndices = new Uint32Array(nnz);
this.colPtr = new Uint32Array(cols + 1);
}
static fromCSR(csr) {
const nnz = csr.getNnz();
const csc = new CSCMatrix(csr.getRows(), csr.getCols(), nnz);
const triplets = [];
for (let row = 0; row < csr.getRows(); row++) {
for (const entry of csr.rowEntries(row)) {
triplets.push({ row, col: entry.col, val: entry.val });
}
}
triplets.sort((a, b) => a.col - b.col || a.row - b.row);
let currentCol = 0;
let nnzCount = 0;
for (const triplet of triplets) {
while (currentCol < triplet.col) {
csc.colPtr[++currentCol] = nnzCount;
}
csc.values[nnzCount] = triplet.val;
csc.rowIndices[nnzCount] = triplet.row;
nnzCount++;
}
while (currentCol < csc.cols) {
csc.colPtr[++currentCol] = nnzCount;
}
return csc;
}
multiplyVector(x, result) {
result.fill(0);
for (let col = 0; col < this.cols; col++) {
const xCol = x[col];
if (xCol === 0)
continue;
const start = this.colPtr[col];
const end = this.colPtr[col + 1];
for (let i = start; i < end; i++) {
result[this.rowIndices[i]] += this.values[i] * xCol;
}
}
}
getMemoryUsage() {
return this.values.byteLength +
this.rowIndices.byteLength +
this.colPtr.byteLength;
}
getNnz() {
return this.values.length;
}
getRows() {
return this.rows;
}
getCols() {
return this.cols;
}
}
export class StreamingMatrix {
chunks = new Map();
chunkSize;
rows;
cols;
maxCachedChunks;
constructor(rows, cols, chunkSize = 1000, maxCachedChunks = 10) {
this.rows = rows;
this.cols = cols;
this.chunkSize = chunkSize;
this.maxCachedChunks = maxCachedChunks;
}
static fromMatrix(matrix, chunkSize = 1000) {
const streaming = new StreamingMatrix(matrix.rows, matrix.cols, chunkSize);
if (matrix.format === 'coo') {
const sparse = matrix;
const chunkData = new Map();
for (let i = 0; i < sparse.values.length; i++) {
const row = sparse.rowIndices[i];
const chunkId = Math.floor(row / chunkSize);
if (!chunkData.has(chunkId)) {
chunkData.set(chunkId, []);
}
chunkData.get(chunkId).push({
col: sparse.colIndices[i],
val: sparse.values[i]
});
}
for (const [chunkId, entries] of chunkData) {
const chunkRows = Math.min(chunkSize, streaming.rows - chunkId * chunkSize);
const chunkCSR = new CSRMatrix(chunkRows, streaming.cols, entries.length);
const rowData = new Map();
for (const entry of entries) {
const localRow = (chunkId * chunkSize) % chunkSize;
if (!rowData.has(localRow)) {
rowData.set(localRow, []);
}
rowData.get(localRow).push(entry);
}
let nnzCount = 0;
for (let row = 0; row < chunkRows; row++) {
chunkCSR.rowPtr[row] = nnzCount;
const rowEntries = rowData.get(row) || [];
rowEntries.sort((a, b) => a.col - b.col);
for (const entry of rowEntries) {
chunkCSR.values[nnzCount] = entry.val;
chunkCSR.colIndices[nnzCount] = entry.col;
nnzCount++;
}
}
chunkCSR.rowPtr[chunkRows] = nnzCount;
streaming.chunks.set(chunkId, chunkCSR);
}
}
return streaming;
}
getChunk(chunkId) {
return this.chunks.get(chunkId) || null;
}
multiplyVector(x, result) {
result.fill(0);
const totalChunks = Math.ceil(this.rows / this.chunkSize);
for (let chunkId = 0; chunkId < totalChunks; chunkId++) {
const chunk = this.getChunk(chunkId);
if (!chunk)
continue;
const startRow = chunkId * this.chunkSize;
const chunkResult = new Array(chunk.getRows()).fill(0);
chunk.multiplyVector(x, chunkResult);
for (let i = 0; i < chunkResult.length && startRow + i < this.rows; i++) {
result[startRow + i] = chunkResult[i];
}
if (this.chunks.size > this.maxCachedChunks) {
const oldestChunk = Math.max(0, chunkId - this.maxCachedChunks);
this.chunks.delete(oldestChunk);
}
}
}
getMemoryUsage() {
let total = 0;
for (const chunk of this.chunks.values()) {
total += chunk.getMemoryUsage();
}
return total;
}
}
export class OptimizedMatrixOperations {
static vectorPool = new VectorPool();
static getVectorPool() {
return this.vectorPool;
}
static vectorAdd(a, b, result) {
const n = a.length;
const out = result || this.vectorPool.acquire(n);
let i = 0;
for (; i < n - 3; i += 4) {
out[i] = a[i] + b[i];
out[i + 1] = a[i + 1] + b[i + 1];
out[i + 2] = a[i + 2] + b[i + 2];
out[i + 3] = a[i + 3] + b[i + 3];
}
for (; i < n; i++) {
out[i] = a[i] + b[i];
}
return out;
}
static vectorScale(vector, scalar, result) {
const n = vector.length;
const out = result || this.vectorPool.acquire(n);
let i = 0;
for (; i < n - 3; i += 4) {
out[i] = vector[i] * scalar;
out[i + 1] = vector[i + 1] * scalar;
out[i + 2] = vector[i + 2] * scalar;
out[i + 3] = vector[i + 3] * scalar;
}
for (; i < n; i++) {
out[i] = vector[i] * scalar;
}
return out;
}
static vectorDot(a, b) {
const n = a.length;
let sum = 0;
let i = 0;
for (; i < n - 3; i += 4) {
sum += a[i] * b[i] +
a[i + 1] * b[i + 1] +
a[i + 2] * b[i + 2] +
a[i + 3] * b[i + 3];
}
for (; i < n; i++) {
sum += a[i] * b[i];
}
return sum;
}
static vectorNorm2(vector) {
return Math.sqrt(this.vectorDot(vector, vector));
}
static convertToOptimalFormat(matrix) {
if (matrix.format === 'coo') {
const sparse = matrix;
const sparsity = sparse.values.length / (matrix.rows * matrix.cols);
return CSRMatrix.fromCOO(sparse);
}
else {
const sparse = this.denseToSparse(matrix);
return CSRMatrix.fromCOO(sparse);
}
}
static denseToSparse(dense, tolerance = 1e-15) {
const values = [];
const rowIndices = [];
const colIndices = [];
for (let i = 0; i < dense.rows; i++) {
for (let j = 0; j < dense.cols; j++) {
const value = dense.data[i][j];
if (Math.abs(value) > tolerance) {
values.push(value);
rowIndices.push(i);
colIndices.push(j);
}
}
}
return {
rows: dense.rows,
cols: dense.cols,
values,
rowIndices,
colIndices,
format: 'coo'
};
}
static profileMemoryUsage(matrix) {
const memoryUsed = matrix.getMemoryUsage();
let nnz;
let rows;
let cols;
if (matrix instanceof CSRMatrix || matrix instanceof CSCMatrix) {
nnz = matrix.getNnz();
rows = matrix.getRows();
cols = matrix.getCols();
}
else {
nnz = 0;
rows = matrix['rows'];
cols = matrix['cols'];
}
const denseMemory = rows * cols * 8; const compressionRatio = denseMemory / memoryUsed;
return {
matrixSize: rows * cols,
nnz,
memoryUsed,
compressionRatio
};
}
static cleanup() {
this.vectorPool.clear();
}
}