koblas

SparseMatrix

@Serializable
@SerialName(value = "SparseMatrix")
class SparseMatrix(val rows: Int, val cols: Int, val colPtr: IntArray, val rowIdx: IntArray, val values: DoubleArray) : MatrixView(source)

A sparse matrix in compressed-sparse-column (CSC) form: column j occupies rowIdx[colPtr[j] until colPtr[j + 1]] with the parallel nonzero values in values, row indices strictly ascending within a column — validated by the constructor, since get relies on the ordering. colPtr has length cols + 1 with colPtr[0] == 0 and colPtr[cols] == values.size. CSC is the layout sparse solvers and column-oriented sweeps (matrix–vector products, LU factorization) consume directly — and, since DenseMatrix became column-major, the axis both storages agree is contiguous.

The invariants are not arbitrary: they are what a host sparse solver requires. UMFPACK states them verbatim — Ap[0] zero, Ap[j] <= Ap[j+1], row indices ascending within a column with no duplicates, 0-based and in range — so this backing passes to umfpack_di_* with no repacking. KLU takes the same (n, Ap, Ai) with int32_t arrays. CHOLMOD wraps it as a cholmod_sparse with packed = 1, sorted = 1, stype = 0 (both triangles stored, which is what koblas does) and itype = CHOLMOD_INT. Checked against the 7.x headers rather than remembered.

Two mismatches remain for a future binding, neither structural. A library built for 64-bit indices wants the umfpack_dl_* family and a widening copy, the sparse counterpart of the LP64/ILP64 split the dense backend already documents. And an explicitly stored zero is part of the value here — equality distinguishes it — where a host library may drop it, so a round-trip through one can lose pattern.

Arithmetic lives on the sparse seam rather than here, as it does for DenseMatrix: the products are com.eignex.koblas.sparse.SparseBlas methods, reachable as gemv/lu extensions, so a container stays storage and does not depend on which backend is installed.

A MatrixView like the dense one, so anything written against the view contract accepts either. The two access costs differ though, and the difference is not small: get searches a column rather than indexing, and toArray densifies. Code that wants the sparsity should reach for forEachInColumn.

Serializes through its CSC arrays rather than a readable 2D form, unlike DenseMatrix. The flat dense backing is an implementation detail worth hiding behind a nicer wire shape; CSC is the format, and writing a sparse matrix out densely would cost the rows × cols the representation exists to avoid.

Constructors

SparseMatrix

constructor(rows: Int, cols: Int, colPtr: IntArray, rowIdx: IntArray, values: DoubleArray)(source)

Types

Link copied to clipboard
object Companion

Factories for CSC matrices.

Properties

colPtr

cols

open override val cols: Int(source)

nnz

val nnz: Int(source)

Number of stored nonzeros.

rowIdx

rows

open override val rows: Int(source)

values

Functions

equals

open operator override fun equals(other: Any?): Boolean(source)

Structural equality over the shape and the stored entries.

Compares the CSC arrays, which means two matrices that agree everywhere but store a different set of explicit zeros are not equal. That mirrors SparseVector, where a stored zero is also part of the value: the pattern is information, and a factorization that reserved a slot for fill differs from one that never had it.

forEachInColumn

inline fun forEachInColumn(j: Int, action: (row: Int, value: Double) -> Unit)(source)

Visit the nonzero entries of column j as (row, value), rows ascending.

get

open operator override fun get(i: Int, j: Int): Double(source)

Read entry (i, j), or 0.0 where nothing is stored.

A binary search over column j's row indices, which are ascending — so this is O(log nnzⱼ), not the O(1) the dense storage gives. Fine for a probe; wrong for a sweep, where forEachInColumn visits the same information in O(nnzⱼ) total.

hashCode

open override fun hashCode(): Int(source)

toArray

open override fun toArray(): Array<DoubleArray>(source)

Materialise into a fresh rows × cols array of rows. Densifies, so the result is as large as the representation was avoiding; only the stored entries are written, the rest stay zero.

toString

open override fun toString(): String(source)
Link copied to clipboard
fun MatrixLike.cholesky(policy: CholeskyPolicy = CholeskyPolicy.Strict): DenseMatrix

Lower-triangular Cholesky decomposition A = L * LT, returned as a fresh matrix, from the installed backend; see Lapack.cholesky.

Link copied to clipboard
fun SparseMatrix.gemv(x: DoubleArray, transpose: Boolean = false): DoubleArray

this · x, or thisᵀ · x when transpose, with the active backend (koblas).

Link copied to clipboard
fun SparseMatrix.lu(equilibrate: Boolean = false, dropTolerance: Double = NO_DROP): SparseFactorization

Factorize this sparse matrix with the active backend (koblas) — the sparse counterpart of DenseMatrix.lu(), carrying the same name for the same operation on the other storage.

Link copied to clipboard

Fresh transposed matrix Aᵀ, still CSC — which makes this the CSC-to-CSR conversion as well.

Link copied to clipboard
fun SparseMatrix.trsv(x: DoubleArray, lower: Boolean, transpose: Boolean = false)

Solve op(T) · x = b in place against this matrix's lower or upper triangle, with the active backend (koblas) — the sparse counterpart of the dense trsv free function. See SparseBlas.trsv.