Skip to content

v0.2.0-Major Changes

Latest

Choose a tag to compare

@CH-Abhinav CH-Abhinav released this 21 Oct 19:18

JNum v0.2.0: The Pure Java Numerical & Tensor Computing Ecosystem

We are thrilled to announce the official release of JNum v0.2.0!

While v0.1 proved that modern Java can replace C++ math backends by combining Java 25, the Foreign Function & Memory (FFM) API, and Project Panama Vector API, v0.2.0 expands JNum from a fast tensor engine into a comprehensive, production-ready numerical computing ecosystem.

JNum v0.2.0 introduces a 4-tier BLIS-inspired SIMD matrix multiplication engine, a complete Linear Algebra suite (SVD, Eigen, QR, Cholesky, Solvers), HuggingFace Safetensors & NumPy I/O with zero-copy memory mapping, a fused JIT expression engine, a statistical computing library, a neural network module, and a dedicated Kotlin DSL with operator overloading.

All of this remains 100% pure Java — zero JNI, zero native .dll/.so binaries, and zero sun.misc.Unsafe.


🚀 Key Features & Architectural Upgrades

1. 4-Tier Hardware-Adaptive BLIS MatMul Engine

Matrix multiplication has been completely re-engineered from the ground up, replacing scalar/basic SIMD with a 4-tier hardware-adaptive BLIS-style execution hierarchy:

  • Tier 0 (Nano Micro-Kernels): Fully unrolled register-direct kernels for small matrices ($\le 4\times 4$), eliminating loop and dispatch overhead.
  • Tier 1 (Direct Register-Blocked SIMD): Direct SIMD kernels without packing overhead for matrices up to $128\times 128$ that comfortably fit in L1/L2 CPU cache.
  • Tier 2 (Single-Threaded BLIS Cache-Blocked): Full 5-loop cache-blocking with $A$-panel ($M_C \times K_C$) and $B$-panel ($K_C \times N_R$) packing for medium matrices ($128 < \max \le 256$) and low-FLOP counts.
  • Tier 3 (Multi-Threaded BLIS Parallel): Cache-oblivious macro-kernel with parallel work-stealing ForkJoin scheduling for large matrices ($> 256\times 256$).
  • Performance:
    • $1024 \times 1024$: ~10ms (best) / 14ms (avg) (down from 51ms in preview and 23ms in v0.1) — 15–22× faster than EJML and matching native OpenBLAS!
    • $8192 \times 8192$: ~5,500ms (best) / 6,500–7,000ms (avg) — 20–25× faster than EJML (which takes >141 seconds).

2. Complete Linear Algebra Suite (jnum.linalg.Linalg)

A full-featured linear algebra module matching numpy.linalg:

  • Matrix Decompositions:
    • Linalg.svd(A) — Singular Value Decomposition ($U, \Sigma, V^T$).
    • Linalg.eig(A) & Linalg.eigh(A) — Eigenvalue and eigenvector solvers for general and symmetric/Hermitian matrices.
    • Linalg.qr(A) — Householder QR factorization ($Q, R$).
    • Linalg.cholesky(A) — Fast Cholesky decomposition ($L L^T$).
  • Solvers & Matrix Inversion:
    • Linalg.solve(A, B) — Direct solver for linear matrix equations $AX = B$.
    • Linalg.inv(A) — Full matrix inversion.
    • Linalg.pinv(A) — Moore-Penrose pseudo-inverse with singular-value tolerance thresholding.
  • Metrics, Determinants & Norms:
    • Linalg.det(A) & Linalg.slogdet(A) — Determinant and sign/log-determinant for numerical stability.
    • Linalg.matrixRank(A) — SVD-based numerical rank determination.
    • Linalg.cond(A) — Matrix condition number.
    • Linalg.norm(A, ord) — Matrix and vector $p$-norms (Frobenius, nuclear, $L_1, L_2, L_\infty$).
    • Linalg.trace(A, offset) — Diagonal trace with arbitrary offsets.
    • Linalg.cosineSimilarity(A, B) — High-speed vector cosine similarity.

3. High-Performance File I/O & Zero-Copy Memory Mapping (jnum.io.JNumIO)

First-class interoperability with modern AI/ML and Python formats:

  • Hugging Face Safetensors:
    • Direct reading and writing of .safetensors model weights without external dependencies.
    • readSafetensorsMmap(path) / MmapTensors: Instantaneous, zero-copy memory mapping of multi-gigabyte neural network checkpoints via JDK 25 MemorySegment.map(...).
    • inspectSafetensors(path): Fast header inspection without loading tensor payload.
  • NumPy Formats (.npy & .npz):
    • Full support for NumPy .npy (v1.0 & v2.0) and multi-array compressed/uncompressed .npz archives.
    • readNpyMmap(path) / MmapArray for memory-mapped zero-copy .npy ingestion.
  • CSV / TSV / Delimited Files:
    • Ultra-fast readCsv() and writeCsv() powered by FastNumberParser (bypassing slow Double.parseDouble()).
  • Asynchronous Non-Blocking I/O:
    • Non-blocking CompletableFuture APIs for all readers and writers (readNpyAsync, writeSafetensorsAsync, readCsvAsync, etc.).

4. Statistical Computing Module (jnum.stat.Stats)

NumPy-grade summary statistics with full axis reduction and keepDims options:

  • Central Tendency & Dispersion: mean(), average() (with arbitrary weighting tensors), var(), std(), sampleVar(), sampleStd() with custom degrees of freedom (ddof).
  • Quantiles & Percentiles: median(), quantile(), percentile(), iqr() (interquartile range) implementing all 9 standard quantile estimation methods (QuantileMethod).
  • Multivariate & Discrete Analysis: cov() (full covariance matrices with rowvar support), mode(), ptp() (peak-to-peak), cumsum(), cumprod(), and diff() (n-th discrete differences).

5. Fused JIT Expression Engine (jnum.internal.eval.EvalEngine)

Compile and evaluate complex compound expressions with zero intermediate allocations:

// Evaluates (a + b) * c - d in a single fused SIMD pass without temporary arrays
NDArray result = JNum.evaluate("(a + b) * c - d", a, b, c, d);
  • AST-based parsing and compilation directly into vectorized SIMD loops.
  • ConcurrentHashMap expression plan cache eliminates parser overhead in hot loops.
  • Over 3× faster than chained standard operations on large datasets.

6. Neural Network Foundation (jnum.nn)

Lightweight, composable neural network components:

  • Module base class and Sequential container.
  • Linear layer with configurable weights and biases.
  • SIMD-accelerated activation functions: ReLU, Sigmoid, Softmax, and Tanh.

7. First-Class Kotlin Module (kotlin-core)

Seamless, idiomatic Kotlin support via io.github.ch-abhinav:jnum-kotlin:

  • Operator Overloading: a + b, a - b, a * b, a / b, -a, plus in-place operators (+=, -=, etc.).
  • Infix Linear Algebra: val c = a matmul b, val d = x dot y.
  • Pythonic Multidimensional Slicing:
    val slice = arr[0..3, all, 2..10 step 2]
    val row = arr[2, all]
  • All operators are marked inline to guarantee zero JVM call-frame or boxing overhead.

8. Expanded Type System & Vectorized Logic

  • Added full DType.bool support.
  • SIMD logical reductions and operations: all(), any(), and(), or(), not(), xor().
  • SIMD elementwise comparisons: maximum(), minimum().
  • Hardware-aligned memory allocations (64-byte alignment) to completely prevent split-cache-line penalties.

📊 Benchmark Shootout (Java 25, AVX-512/AVX2)

Benchmarks run via JMH 1.37 on JDK 25 (-Xms8g -Xmx8g -XX:+AlwaysPreTouch -XX:+UseParallelGC --add-modules jdk.incubator.vector).

1. Matrix Multiplication (ms/op — lower is better)

Matrix Dimension JNum v0.1 JNum v0.2.0 (avg) JNum v0.2.0 (best) EJML v0.43 NumPy (OpenBLAS)
64 × 64 ~0.08 ms 0.013 ms 0.010 ms 0.140 ms 0.013 ms
128 × 128 ~0.45 ms 0.107 ms 0.095 ms 1.028 ms 0.254 ms
512 × 512 ~4.10 ms 3.45 ms 2.80 ms 27.16 ms 1.86 ms
1024 × 1024 ~23.0 ms 14.0 ms 10.0 ms 216.56 ms 8.98 ms
2048 × 2048 — 143.5 ms 130.0 ms 2,049.86 ms 62.99 ms
4096 × 4096 — 1,102.2 ms 980.0 ms 17,419.52 ms 487.92 ms
8192 × 8192 — 6,500–7,000 ms 5,500 ms 141,500+ ms (DNF) 5,967.30 ms

⚡ JNum v0.2.0 is 15–22× faster than EJML at 1024×1024, and performs on par with native C/Fortran OpenBLAS.


2. Transcendental & Trigonometric Functions (100M Elements, ms/op)

Operation JNum v0.2.0 EJML NumPy (OpenBLAS) Speedup vs. EJML Speedup vs. NumPy
Sin 151.6 ms 1,334.8 ms 154.9 ms 8.8× faster 1.02× faster
Cos 170.9 ms 1,077.3 ms 151.3 ms 6.3× faster ≈ tie
Tan 269.2 ms 1,767.3 ms 792.7 ms 6.6× faster 2.9× faster
Exp 111.5 ms 805.0 ms 122.1 ms 7.2× faster 1.1× faster
Log 121.5 ms 844.1 ms 164.2 ms 6.9× faster 1.35× faster
Sqrt 51.3 ms 55.0 ms 48.6 ms 1.07× faster ≈ tie
Tanh 301.4 ms 1,493.3 ms 197.4 ms 5.0× faster 0.65×
Sigmoid 115.0 ms 973.7 ms 440.5 ms 8.5× faster 3.8× faster

3. Linear Algebra Inversion & Decompositions (ms/op)

Operation JNum v0.2.0 ND4J (CPU Native) Speedup vs. ND4J
Inverse [32×32] 0.025 ms 2.68 ms 107× faster
Inverse [128×128] 0.798 ms 152.56 ms 191× faster
Inverse [256×256] 7.271 ms 1,224.65 ms 168× faster
Determinant [128×128] 0.260 ms 43.95 ms 169× faster
Cholesky [32×32] 0.006 ms 6.64 ms 1,100× faster
Solve [128×128] 0.277 ms 9.83 ms 35× faster
QR [128×128] 3.302 ms 134.53 ms 41× faster
jnum_multilibrary_benchmark_graphs_page-0002 jnum_multilibrary_benchmark_graphs_page-0003 jnum_multilibrary_benchmark_graphs_page-0004 jnum_multilibrary_benchmark_graphs_page-0005 jnum_multilibrary_benchmark_graphs_page-0006 jnum_multilibrary_benchmark_graphs_page-0007 jnum_multilibrary_benchmark_graphs_page-0008 jnum_multilibrary_benchmark_graphs_page-0009 jnum_multilibrary_benchmark_graphs_page-0010 jnum_multilibrary_benchmark_graphs_page-0011

📦 Getting Started

1. Maven Dependency

JNum is published under io.github.ch-abhinav:

<!-- Core Java Tensor & Math Library -->
<dependency>
    <groupId>io.github.ch-abhinav</groupId>
    <artifactId>jnum</artifactId>
    <version>0.2.0</version>
</dependency>

<!-- Optional: Kotlin Idiomatic Operator Extensions -->
<dependency>
    <groupId>io.github.ch-abhinav</groupId>
    <artifactId>jnum-kotlin</artifactId>
    <version>0.2.0</version>
</dependency>

2. Gradle (Kotlin DSL)

implementation("io.github.ch-abhinav:jnum:0.2.0")
implementation("io.github.ch-abhinav:jnum-kotlin:0.2.0")

3. JVM Launch Configuration

Because JNum uses the Project Panama Vector API, add the following flag when compiling and running on Java 21+:

--add-modules jdk.incubator.vector

(Optional for cleanest execution: --enable-native-access=ALL-UNNAMED)


🛠️ Quick Example

Java

import jnum.JNum;
import jnum.NDArray;
import jnum.linalg.Linalg;
import jnum.io.JNumIO;
import java.nio.file.Path;

// 1. Create tensors
NDArray a = JNum.randn(1024, 1024);
NDArray b = JNum.eye(1024);

// 2. BLIS-accelerated MatMul
NDArray c = Linalg.matmul(a, b);

// 3. Vectorized Math
NDArray activated = JNum.sigmoid(c);

// 4. Zero-copy Safetensors / NumPy I/O
JNumIO.writeNpy(activated, Path.of("weights.npy"));

Kotlin

import jnum.*
import java.nio.file.Path

val a = JNum.randn(512, 512)
val b = JNum.eye(512)

// Infix & operator overloads
val c = (a matmul b) + 2.5
val slice = c[0..10, all]

// Safetensors export
JNumIO.writeSafetensors(mapOf("tensor" to slice), Path.of("model.safetensors"))

⚠️ Migration & Breaking Changes from v0.1

  1. Maven Coordinates: Group ID migrated from com.github.ch-abhinav to official verified Maven Central namespace: io.github.ch-abhinav.
  2. Modular Architecture: Project is now modularized into jnum-parent, jnum (Java core), and jnum-kotlin.
  3. Dedicated Namespaces:
    • Linear algebra functions moved to jnum.linalg.Linalg.
    • File format loaders moved to jnum.io.JNumIO.
    • Statistics moved to jnum.stat.Stats.
    • Neural network components reside in jnum.nn.