@thi.ng/sparse
Version:
Sparse vector & matrix implementations
211 lines (210 loc) • 4.85 kB
JavaScript
import { assert } from "@thi.ng/errors/assert";
import { ensureIndex2 } from "@thi.ng/errors/out-of-bounds";
import { ASparseMatrix } from "./amatrix.js";
import { at, compress, diag, setAt } from "./compressed.js";
class CSC extends ASparseMatrix {
/**
* Constructs CSC from dense column-major matrix values.
*
* @param m - rows
* @param n - columns
* @param dense - matrix values
*/
static fromDense(m, n, dense) {
const [cols, rows, data] = compress(m, n, dense);
return new CSC(m, n, data, cols, rows);
}
static empty(m, n = m) {
return new CSC(m, n, [], new Array(n + 1).fill(0), []);
}
static identity(m) {
return CSC.diag(new Array(m).fill(1));
}
static diag(vals) {
const [cols, rows] = diag(vals);
return new CSC(vals.length, vals.length, vals, cols, rows);
}
/**
* Non-zero matrix values
*/
data;
/**
* Column start indices into A
*/
cols;
/**
* Row indices for values in A
*/
rows;
constructor(m, n, data, cols, rows) {
super(m, n);
this.cols = cols;
this.rows = rows;
this.data = data;
}
copy() {
return new CSC(
this.m,
this.n,
this.data.slice(),
this.cols.slice(),
this.rows.slice()
);
}
zero() {
this.data.length = this.rows.length = 0;
this.cols.fill(0);
return this;
}
*nzEntries() {
const { cols, rows, data } = this;
for (let i = 0; i < this.n; i++) {
for (let j = cols[i], jj = cols[i + 1]; j < jj; j++) {
yield [rows[j], i, data[j]];
}
}
}
at(m, n, safe = true) {
safe && ensureIndex2(m, n, this.m, this.n);
return at(n, m, this.cols, this.rows, this.data);
}
setAt(m, n, x, safe = true, compact = true) {
safe && ensureIndex2(m, n, this.m, this.n);
const state = setAt(
n,
m,
this.n,
x,
this.cols,
this.rows,
this.data,
compact
);
this.cols = state[0];
this.rows = state[1];
this.data = state[2];
return this;
}
mul(mat) {
assert(this.n === mat.m, "incompatible matrix sizes");
const res = CSC.empty(this.m, mat.n);
for (let j = 0; j < mat.n; j++) {
if (mat.nnzCol(j) > 0) {
for (let k = 0; k < mat.m; k++) {
const bkj = mat.at(k, j);
if (bkj !== 0) {
for (let i = 0; i < this.m; i++) {
const aik = this.at(i, k, false);
if (aik !== 0) {
res.setAt(
i,
j,
res.at(i, j, false) + aik * bkj,
false
);
}
}
}
}
}
}
return res;
}
mulV(vec) {
assert(this.m === vec.length, `vector length != ${this.m}`);
const { cols, rows, data } = this;
const res = new Array(this.m).fill(0);
for (let i = 0; i < this.n; i++) {
const jj = cols[i + 1];
for (let j = cols[i]; j < jj; j++) {
res[rows[j]] += data[j] * vec[i];
}
}
return res;
}
nnz() {
return this.data.length;
}
nnzCol(n) {
return this.cols[n + 1] - this.cols[n];
}
nzColRows(n) {
return this.rows.slice(this.cols[n], this.cols[n + 1]);
}
nzColVals(n) {
return this.data.slice(this.cols[n], this.cols[n + 1]);
}
nnzRow(m) {
const rows = this.rows;
let res = 0;
for (let i = rows.length; i-- > 0; ) {
if (rows[i] === m) {
res++;
}
}
return res;
}
nzRowVals(m) {
const { rows, data } = this;
const res = [];
for (let i = 0, num = rows.length; i < num; i++) {
if (rows[i] === m) {
res.push(data[i]);
}
}
return res;
}
nzRowCols(m) {
const res = [];
for (let i = 0; i < this.n; i++) {
if (this.at(m, i, false) !== 0) {
res.push(i);
}
}
return res;
}
transpose() {
const res = CSC.empty(this.n, this.m);
const { rows, data } = this;
for (let i = 0; i < this.n; i++) {
const jj = this.cols[i + 1];
for (let j = this.cols[i]; j < jj; j++) {
res.setAt(i, rows[j], data[j]);
}
}
return res;
}
denseCol(n) {
const res = new Array(this.m).fill(0);
const ii = this.cols[n + 1];
const { rows, data } = this;
for (let i = this.cols[n]; i < ii; i++) {
res[rows[i]] = data[i];
}
return res;
}
denseRow(m) {
const res = new Array(this.n);
for (let i = 0; i < this.n; i++) {
res[i] = this.at(m, i, false);
}
return res;
}
toDense() {
let res = [];
for (let i = 0; i < this.n; i++) {
res = res.concat(this.denseCol(i));
}
return res;
}
toString() {
const res = [];
for (let i = 0; i < this.m; i++) {
res.push(this.denseRow(i).join(" "));
}
return res.join("\n");
}
}
export {
CSC
};