Skip to content
stamichPublic

About

Pure JVM N-dimensional arrays with NumPy semantics. Powered by Java 25 FFM API (Project Panama) for blazing fast numerical computing. No native or Python runtimes needed.

Topics

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

209 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NumJVM

build NumPy oracle sonar Quality Gate Status Coverage Maintainability Rating Reliability Rating Security Rating Lines of Code License

Numerical computing for the Java Virtual Machine: N-dimensional arrays with NumPy semantics, built on the Java 25 Foreign Function & Memory API. No Python, NumPy or native libraries are needed at run time.

Status: 0.5.0-alpha implementation candidate (M3.1). Experimental expression execution and real contractions extend the CPU APIs. Reference measurements show substantial AUTO regressions in some cases; see 0.5.0 status. Performance gates remain open and the API remains experimental.

Quick start

import java.nio.file.Path;

import org.numjvm.NJ;
import org.numjvm.dtype.DType;
import org.numjvm.io.Npy;
import org.numjvm.memory.NJScope;
import org.numjvm.ndarray.NDArray;
import org.numjvm.ndarray.Slice;

NDArray m = NJ.array(new double[]{1, 2, 3, 4, 5, 6}, 2, 3);
System.out.println(m);
// array([[1., 2., 3.],
//        [4., 5., 6.]])

NDArray t = m.transpose();                         // zero-copy: strides [24, 8] -> [8, 24]
NDArray r = m.reshape(3, 2);                       // a view when the layout allows it
NDArray s = m.slice(Slice.all(), Slice.step(-1));  // m[:, ::-1]
double x = m.getDouble(1, 2);                      // 6.0, no allocation

NDArray b = NJ.array(new double[]{10, 20, 30});
NDArray sum = m.add(b);                            // broadcasting: (2, 3) + (3,)
NDArray big = NJ.greater(m, 3.0);                  // bool array
NDArray e = NJ.exp(m.transpose());                 // any layout; the result keeps it
NDArray cols = NJ.sum(m, 0);                       // [5., 7., 9.]
double mean = NJ.mean(m).getDouble();              // 3.5
NDArray i8 = NJ.arange(0, 6, 1).astype(DType.INT8);
DType p = NJ.add(i8, NJ.zeros(DType.FLOAT32, 6)).dtype();   // float32, as NumPy 2
m.multiplyInPlace(2.0);                            // out= m

Npy.write(Path.of("m.npy"), m);                    // NumPy reads it with np.load
NDArray back = Npy.read(Path.of("m.npy"));

try (NJScope scope = NJ.scope()) {                 // deterministic memory release
    NDArray tmp = scope.zeros(DType.FLOAT64, 1000, 1000);
    NDArray tmp2 = NJ.sqrt(tmp);                   // results of scoped operands stay in the scope
    NDArray kept = scope.keep(tmp2.transpose());   // copy that outlives the scope
}

Runnable versions: examples/basic-arrays and examples/memory-scopes:

./gradlew :examples:basic-arrays:run
./gradlew :examples:memory-scopes:run

Array and elementwise API

Area Contents
Dtypes bool, int8–64, uint8–64, float16, bfloat16, float32, float64, complex64/128 (Complex values)
Memory heap (long[]) and 64-byte aligned native buffers, NJScope (confined or shared), keep(), NJ.memoryStats()
Arrays shape, byte strides (negative and zero allowed), 0-D and empty arrays, invariants checked once per array
Element access getDouble/getLong/getBoolean/getComplex and setters, rank-specialised, allocation-free
Views transpose, permuteDims, swapAxes, slice (NumPy basic indexing), reshape/reshapeView, broadcastTo, real/imag
Copies copy(Order), astype(DType)
Ufuncs arithmetic, comparisons, logical and bitwise, abs, sign, sqrt, exp, log, sin, cos, tanh, rounding: 41 in Ufuncs, with NJ and NDArray shortcuts
Promotion NumPy 2 / Array API 2025.12 lattice, NJ.resultType, NJ.canCast, weak Java long/double/Complex scalars
Reductions sum (pairwise), prod, max, min, mean, any, all over axis lists with keepdims; ufunc.reduce
Output out= arrays with overlap detection, addInPlace and friends; results in the operands' memory order and scope
Iteration IterationPlan (axis reordering and coalescing), StridedIterator, IndexIterator
I/O .npy read, write and memory-map (format 1.0–3.0, both byte orders, C and Fortran order)
Printing toString() matches NumPy's repr

Modules

Module Artifact Contents
numjvm-core io.github.stamich:numjvm-core dtypes, memory, NDArray, views, ufuncs, reductions
numjvm-jit io.github.stamich:numjvm-jit experimental expression execution
numjvm-io io.github.stamich:numjvm-io NumPy .npy I/O
numjvm-vector io.github.stamich:numjvm-vector optional SIMD kernels
numjvm-linalg io.github.stamich:numjvm-linalg real matmul, dot and vecdot; optional SIMD/OpenBLAS
numjvm-bench not published JMH benchmarks
numjvm-tests-oracle not published NumPy differential-test fixtures

Optional CPU acceleration and matmul

M2 adds optional numjvm-vector and numjvm-linalg artifacts. Core remains usable without Vector API resolution or a native library. Matrix multiplication is real float32/64, with vector promotion, broadcast batches, arbitrary input strides and an exact-shape/dtype out variant:

import org.numjvm.linalg.Linalg;

NDArray a = NJ.array(new double[]{1, 2, 3, 4, 5, 6}, 2, 3);
NDArray gram = Linalg.matmul(a, a.transpose()); // [[14, 32], [32, 77]]
./gradlew :examples:accelerated-arrays:run
./gradlew :examples:accelerated-arrays:run -PwithoutVector
./gradlew :examples:matmul:run

The optional SIMD provider accelerates selected contiguous and broadcast operations, with scalar fallback elsewhere. Java matmul needs no native dependencies; optional OpenBLAS supports Linux x86-64 LP64 CBLAS. See the setup and backend guide for flags, forced modes, ownership, diagnostics, native validation and consumer examples. The M2 status distinguishes implemented behavior from outstanding release performance evidence.

Experimental real float32/64 Linalg.dot and Linalg.vecdot add vector and higher-rank contractions, with exact-shape/dtype explicit outputs. See the contraction guide and run ./gradlew :examples:contractions:run (add -PwithVector for SIMD).

Experimental expression fusion

The optional numjvm-jit module traces explicit float32/float64 arithmetic expressions for block or generated execution. The alpha supports add, subtract, multiply, divide, negative, absolute, square, and square root; comparisons, conditional selection, other transcendental functions, reductions, and matmul inside expressions are not yet supported. See the expression guide and run ./gradlew :examples:fused-expressions:run. AUTO may select optional fused SIMD when eligible; lastDecision() identifies the actual strategy. Reference results show substantial AUTO slowdowns and allocation in some cases; see the results and reference protocol.

Building

Requires JDK 25. The Gradle wrapper downloads Gradle, and the build can provision a JDK 25 toolchain if none is installed.

./gradlew build                 # compile, unit, property and oracle tests, coverage
./gradlew :numjvm-bench:jmh -PjmhArgs="UfuncBenchmark -p size=65536 -prof gc"

Coverage reports (JaCoCo) are written to numjvm-*/build/reports/jacoco/test/. Static analysis runs on SonarCloud; see CONTRIBUTING.md.

The NumPy oracle needs Python only to regenerate or re-verify its fixtures; see numjvm-tests-oracle.

Documentation

License

Apache License 2.0, see LICENSE.

About

Pure JVM N-dimensional arrays with NumPy semantics. Powered by Java 25 FFM API (Project Panama) for blazing fast numerical computing. No native or Python runtimes needed.

Topics

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages