
Serializable dense and sparse matrix and vector containers, BLAS-like arithmetic and factorizations, sparse Markowitz LU and EtaBasis updates, with runtime-swappable high-performance compute backend.
Koblas provides dense and sparse double-precision BLAS Levels 1–3 for Kotlin Multiplatform. Portable Kotlin implementations work without an installed numerical library. JVM performance uses owned Vector API kernels; Kotlin/Native can accelerate eligible calls through installed host libraries. Koblas has no external runtime dependencies and is Apache-2.0 licensed. It bundles no vendor libraries or custom native numerical kernels.
Add the Maven Central dependency to your JVM or commonMain source set:
implementation("com.eignex:koblas:<version>")Supported targets: JVM on JDK 25 or later, Linux x64/arm64, and macOS arm64.
--add-modules=jdk.incubator.vector to enable the SIMD engine. Without it, calls use portable
Kotlin. Ordinary JVM calls do not load a host library.--enable-native-access=ALL-UNNAMED. Supported vendors include
OpenBLAS, oneMKL, AOCL, Arm Performance Libraries and Accelerate.Engine selection is automatic. Use koblas.routeOf(...) to inspect how a dense or sparse call will run,
including any fallback. The benchmark guide covers explicit engine selection
and identifying the host library used.
import com.eignex.koblas.*
val a = DenseMatrix.ofRows(arrayOf(
doubleArrayOf(2.0, 1.0),
doubleArrayOf(1.0, 3.0),
))
val b = DenseMatrix.diagonal(2)
val x = DenseVector.of(doubleArrayOf(3.0, 5.0))
val product = a * b
val y = a * xOperators allocate results. BLAS-style and *Into calls write into caller-owned destinations, and every
routine that can take scratch accepts a Workspace to lend it, whether that scratch is the snapshot of an
operand overlapping the destination, the gather a strided vector needs, or the panels a blocked product
packs:
val result = DenseMatrix.zero(2, 2)
val workspace = Workspace()
repeat(1_000) {
koblas.gemm(1.0, a, false, b, false, 0.0, result, workspace)
}A workspace retains a bounded set of buffers, and a call that needs none takes nothing from the one it is handed. It does not eliminate the result a fresh-result overload allocates, nor the arrays a sparse operation discovering new structure has to build. Concurrent calls need distinct destinations and workspaces.
Factories are static methods, and Kotlin operators/extensions are exposed through Koblas. Common calls
have overloads for default arguments:
import com.eignex.koblas.DenseMatrix;
import com.eignex.koblas.DenseVector;
import com.eignex.koblas.Koblas;
DenseMatrix a = DenseMatrix.ofRows(new double[][] {{2.0, 1.0}, {1.0, 3.0}});
DenseVector x = DenseVector.of(new double[] {3.0, 5.0});
DenseVector y = Koblas.multiply(a, x);
double[] destination = new double[a.getRows()];
Koblas.gemvInto(a, x, destination);Use Koblas.getDefault() for the selected engine. Containers expose ordinary primitive arrays when direct
storage access is needed.
DenseMatrix is column-major: A(i, j) is stored at i + j * rows.SparseMatrix is validated CSC with ascending row indices. Stored zeros participate in arithmetic;
absent entries are not evaluated, and cancellation does not implicitly remove stored entries.Matrix products accept dense, sparse or prepared sparse operands in either position, including independent
transpose flags. Two sparse operands produce a fresh CSC result; other built-in pairings produce dense results.
gemmInto writes any pairing into a dense destination without densifying sparse inputs.StridedVector and matrix row/column views borrow existing storage.*Into paths support aliases through snapshotting. Explicit vendor bindings retain their own
overlap contract. Symmetric operations read only the selected triangle; triangular solves write in place.Matrix operands: they own their snapshots, run on the engine that prepared
them, and can be shared by readers with separate mutable scratch.Shape mismatches throw DimensionMismatch; invalid indices throw IndexOutOfBoundsException; other invalid
arguments throw IllegalArgumentException. Numerical failures use KoblasException subtypes. Behavior left
open by BLAS, such as tie/NaN choices in idamax, follows the selected implementation.
Most application code needs only com.eignex.koblas.*. Lower-level interfaces support custom algorithms over
caller-owned storage. Factorization and solver workflows belong to consumers.
| Area | API |
|---|---|
| Containers and views | Matrices, sparse matrices, vectors |
| Vector arithmetic | VectorOps |
| Matrix operations | MatrixOps, generic products |
| BLAS | DenseBlas, SparseBlas |
| Reuse | Workspace, prepared sparse snapshots |
| Custom kernels and bindings | SparsePrimitives, vendor Blas |
See the API overview and benchmark guide for kernel contracts, exact-engine comparisons and reproducible captures.
Run ./gradlew check lintDocs. Add -Pkoblas.noSimd=true to check the JVM fallback without the Vector API
module; this does not hide installed vendor libraries.
Koblas provides dense and sparse double-precision BLAS Levels 1–3 for Kotlin Multiplatform. Portable Kotlin implementations work without an installed numerical library. JVM performance uses owned Vector API kernels; Kotlin/Native can accelerate eligible calls through installed host libraries. Koblas has no external runtime dependencies and is Apache-2.0 licensed. It bundles no vendor libraries or custom native numerical kernels.
Add the Maven Central dependency to your JVM or commonMain source set:
implementation("com.eignex:koblas:<version>")Supported targets: JVM on JDK 25 or later, Linux x64/arm64, and macOS arm64.
--add-modules=jdk.incubator.vector to enable the SIMD engine. Without it, calls use portable
Kotlin. Ordinary JVM calls do not load a host library.--enable-native-access=ALL-UNNAMED. Supported vendors include
OpenBLAS, oneMKL, AOCL, Arm Performance Libraries and Accelerate.Engine selection is automatic. Use koblas.routeOf(...) to inspect how a dense or sparse call will run,
including any fallback. The benchmark guide covers explicit engine selection
and identifying the host library used.
import com.eignex.koblas.*
val a = DenseMatrix.ofRows(arrayOf(
doubleArrayOf(2.0, 1.0),
doubleArrayOf(1.0, 3.0),
))
val b = DenseMatrix.diagonal(2)
val x = DenseVector.of(doubleArrayOf(3.0, 5.0))
val product = a * b
val y = a * xOperators allocate results. BLAS-style and *Into calls write into caller-owned destinations, and every
routine that can take scratch accepts a Workspace to lend it, whether that scratch is the snapshot of an
operand overlapping the destination, the gather a strided vector needs, or the panels a blocked product
packs:
val result = DenseMatrix.zero(2, 2)
val workspace = Workspace()
repeat(1_000) {
koblas.gemm(1.0, a, false, b, false, 0.0, result, workspace)
}A workspace retains a bounded set of buffers, and a call that needs none takes nothing from the one it is handed. It does not eliminate the result a fresh-result overload allocates, nor the arrays a sparse operation discovering new structure has to build. Concurrent calls need distinct destinations and workspaces.
Factories are static methods, and Kotlin operators/extensions are exposed through Koblas. Common calls
have overloads for default arguments:
import com.eignex.koblas.DenseMatrix;
import com.eignex.koblas.DenseVector;
import com.eignex.koblas.Koblas;
DenseMatrix a = DenseMatrix.ofRows(new double[][] {{2.0, 1.0}, {1.0, 3.0}});
DenseVector x = DenseVector.of(new double[] {3.0, 5.0});
DenseVector y = Koblas.multiply(a, x);
double[] destination = new double[a.getRows()];
Koblas.gemvInto(a, x, destination);Use Koblas.getDefault() for the selected engine. Containers expose ordinary primitive arrays when direct
storage access is needed.
DenseMatrix is column-major: A(i, j) is stored at i + j * rows.SparseMatrix is validated CSC with ascending row indices. Stored zeros participate in arithmetic;
absent entries are not evaluated, and cancellation does not implicitly remove stored entries.Matrix products accept dense, sparse or prepared sparse operands in either position, including independent
transpose flags. Two sparse operands produce a fresh CSC result; other built-in pairings produce dense results.
gemmInto writes any pairing into a dense destination without densifying sparse inputs.StridedVector and matrix row/column views borrow existing storage.*Into paths support aliases through snapshotting. Explicit vendor bindings retain their own
overlap contract. Symmetric operations read only the selected triangle; triangular solves write in place.Matrix operands: they own their snapshots, run on the engine that prepared
them, and can be shared by readers with separate mutable scratch.Shape mismatches throw DimensionMismatch; invalid indices throw IndexOutOfBoundsException; other invalid
arguments throw IllegalArgumentException. Numerical failures use KoblasException subtypes. Behavior left
open by BLAS, such as tie/NaN choices in idamax, follows the selected implementation.
Most application code needs only com.eignex.koblas.*. Lower-level interfaces support custom algorithms over
caller-owned storage. Factorization and solver workflows belong to consumers.
| Area | API |
|---|---|
| Containers and views | Matrices, sparse matrices, vectors |
| Vector arithmetic | VectorOps |
| Matrix operations | MatrixOps, generic products |
| BLAS | DenseBlas, SparseBlas |
| Reuse | Workspace, prepared sparse snapshots |
| Custom kernels and bindings | SparsePrimitives, vendor Blas |
See the API overview and benchmark guide for kernel contracts, exact-engine comparisons and reproducible captures.
Run ./gradlew check lintDocs. Add -Pkoblas.noSimd=true to check the JVM fallback without the Vector API
module; this does not hide installed vendor libraries.