Optional acceleration · 0.8.4+

Use the GPU where the workload earns it

JDistlib can route large vector, dense/sparse linear-algebra, and batched likelihood work through optional CUDA, OpenCL, or Vulkan providers. Version 0.10.0 also exposes prepared operands and reusable factorizations to downstream libraries. Small operations stay on the deterministic CPU reference path by default.

Unified FP64/FP32 linear algebra is available in JDistlib 0.10.0

One-file installation

Download one JAR; let JDistlib detect the machine

The recommended jdistlib-all.jar contains core JDistlib, all three providers, and their Java/JNI dependencies for Windows, Linux, and macOS x86-64. The stable filename always resolves to the newest GitHub release and runs on CPU when no compatible GPU runtime is present.

Selection contract

Automatic is conservative; explicit choices are strict

Compute choiceBehaviorIf unavailable
Compute.AUTODiscover available providers and route eligible large operations according to their reported capabilities.Use CPU and record the selection.
Compute.CPUUse the deterministic CPU reference implementation.Always available.
Compute.ONEMKLRequire the system Intel oneMKL CBLAS/LAPACKE runtime.Fail immediately.
Compute.OPENBLASRequire the system OpenBLAS CBLAS/LAPACKE runtime.Fail immediately.
Compute.GPURequire any supported GPU provider.Fail immediately; never fall back.
Compute.CUDARequire CUDA specifically.Fail immediately.
Compute.OPENCLRequire OpenCL specifically.Fail immediately.
Compute.VULKANRequire the LWJGL Vulkan FP64 compute provider.Fail immediately.

The current automatic router uses conservative JVM-heap thresholds: 32,768 vector elements and approximately one million multiply/evaluation terms for matrix multiplication or logistic likelihood batches. These are implementation defaults, not performance promises. Prepared inputs remain resident on the selected device, which can make regular batched workflows profitable sooner.

Reusable numerical engine · 0.10.0

Keep fixed operands resident and inspect every fallback

The matching FP64/FP32 contracts cover dense BLAS, CSR products and triangular solves, batched work, and Cholesky, LU, LDL', QR, ordinary/generalized symmetric eigen, and thin-SVD decompositions. Prepared dense handles keep a fixed operand in native CPU or provider device storage; prepared sparse Cholesky separates symbolic analysis from repeated numeric refactorization.

Capability records and execution plans distinguish provider-native work from the portable Java fallback. This makes the same API usable on a CPU-only machine without claiming that every decomposition is a native GPU kernel. See the linear-algebra overview or the complete contract.

Programmatic Java API

Put the policy in immutable sampling options

SamplingOptions follows the existing builder pattern. The shorter backend method is an alias for computeBackend.

SamplingOptions options = SamplingOptions.builder()
    .warmupIterations(1000)
    .sampleIterations(2000)
    .backend(Compute.AUTO)
    .nutsBackend(ComputeNuts.AUTO)
    .build();

try (AcceleratedLogisticRegression target =
        AcceleratedLogisticRegression.forNuts(
            options, design, outcomes, priorPrecision)) {
    Fit fit = Inference.fit(
        new NoUTurnSampler(), target, initialStates,
        options, 20260828L, 4);

    System.out.println(fit.manifest().computeBackend());
    System.out.println(fit.manifest().computeDevice());
}

To require CUDA target evaluation for NUTS, replace the two policy lines:

.backend(Compute.CUDA)
.nutsBackend(ComputeNuts.FORCE)

A strict policy throws if the provider is missing, the device lacks FP64, or the target is not accelerator-aware. It never quietly changes the request to CPU.

Use numerical primitives directly

try (ComputeSelection selection =
        ComputeBackends.select(Compute.AUTO)) {
    ComputeBackend compute = selection.backend();
    double dot = compute.dot(x, y);
    double[][] product = compute.matrixMultiply(a, b);
    System.out.println(selection.deviceInfo().description());
    System.out.println(selection.plan(LinearAlgebraOperation.GEMM,
        NumericPrecision.FP64, rows, columns, shared).description());
    try (PreparedTransposeProduct scores =
            compute.prepareTransposeProduct(design)) {
        double[][] xtResidual = scores.multiply(residualBatches);
    }
}

The optional modules implement double-precision unary vector functions, AXPY, dot products, dense matrix multiplication, and batched logistic likelihoods and gradients. CUDA and OpenCL also keep a prepared matrix resident for repeated transpose products such as high-dimensional residual scores; Vulkan uses the CPU fallback for this primitive. The modular core artifact remains native-free.

Command-line applications

Use reusable switches or JVM properties

Applications embedding JDistlib can pass their argument array through InferenceCliOptions. Unknown arguments are returned to the host application.

InferenceCliOptions cli = InferenceCliOptions.parse(args);
SamplingOptions options = cli.applyTo(
    SamplingOptions.builder()).build();
String[] applicationArguments = cli.remainingArguments();
SwitchMeaning
--compute=auto|cpu|onemkl|openblas|gpu|cuda|opencl|vulkanSelect automatic routing, Java CPU, a native CPU runtime, any required GPU, or an exact GPU provider.
--nuts-offload=off|auto|forceDisable, automatically choose, or require accelerated NUTS target evaluation.
--gpu-nutsConvenience alias for --compute=gpu --nuts-offload=force.

The equivalent JVM properties work even when an application does not expose those switches:

java -Djdistlib.compute.backend=cuda \
     -Djdistlib.compute.nuts=force \
     -cp "..." your.application.Main

Backend selection is emitted once through java.util.logging. Command-line applications normally show it on the console, for example:

JDistlib compute: policy=auto, backend=cuda,
device=NVIDIA GeForce RTX 2080 (sm_75), thresholded CPU fallback enabled

RunManifest also retains the requested policy, concrete backend, device, and NUTS offload mode so a run does not depend on a transient console message.

NUTS boundary

Offload target evaluation, not irregular tree control

NUTS tree depths differ by chain and U-turn decisions stop each tree at different times. JDistlib therefore keeps tree construction, adaptation, candidate selection, and U-turn control on CPU. An accelerator-aware target may execute its likelihood, gradient, vector math, and dense linear algebra on a GPU.

NUTS choiceContract
ComputeNuts.OFFRequire CPU target evaluation.
ComputeNuts.AUTOUse the configured backend and automatic thresholds. This is the default.
ComputeNuts.FORCERequire a CUDA, OpenCL, or Vulkan-backed target and bypass automatic CPU thresholds.

The whole-NUTS GPU decision remains provisional until representative end-to-end effective-samples-per-second measurements include warmup, synchronization, divergences, and transfer costs.

Measured smoke test · 2026-08-27

Batching changes the result

The repeatable CUDA smoke used an NVIDIA GeForce RTX 2080 and an 8,192-row, 32-predictor double-precision logistic regression. Times are five-trial medians after context and NVRTC compilation warmup.

StatesCPUCUDA residentResident speedupCUDA with copiesEnd-to-end speedup
10.7165 ms0.8915 ms0.804×1.8337 ms0.391×
42.6209 ms0.8043 ms3.259×1.8411 ms1.424×
1610.6519 ms1.4382 ms7.406×2.3092 ms4.613×
6445.6427 ms2.7388 ms16.665×3.6493 ms12.507×

The largest CPU/CUDA likelihood or gradient difference was below 3.1e-11. These measurements establish a batching boundary on one machine, not a portable speed guarantee. Re-run gradlew :jdistlib-cuda:cudaSmokeBenchmark on the deployment hardware and review the checked-in benchmark record.

Providers and reproducibility

Optional artifacts keep core JDistlib portable

CUDA

jdistlib-cuda uses JCuda Driver and JNvrtc. NVRTC compiles device code without nvcc or MSVC, but a compatible NVIDIA driver, NVRTC, and nvrtc-builtins are required.

OpenCL

jdistlib-opencl uses JOCL and selects an OpenCL GPU with FP64 support.

Vulkan

jdistlib-vulkan uses LWJGL Vulkan and shaderc, selects a compute queue with shaderFloat64, and compiles maintained GLSL kernels to cached SPIR-V pipelines at runtime. Version 0.10.0 can retain prepared FP64/FP32 dense operands in provider buffers. The provider remains explicit about operations that use portable fallback.

Native CPU

jdistlib-nativecpu dynamically loads a system oneMKL or OpenBLAS runtime through JNA. The all-in-one JAR includes the adapter, not Intel or OpenBLAS binaries. Use Compute.ONEMKL or Compute.OPENBLAS for a strict choice.

The unified JAR carries the x86-64 JNI bindings, but it cannot carry vendor drivers. CUDA still needs an NVIDIA driver plus NVRTC, OpenCL needs a vendor OpenCL implementation, and Vulkan needs a Vulkan loader/driver (MoltenVK is bundled through LWJGL on x86-64 macOS). On other architectures, use the modular artifacts so the dependency manager can select matching natives.

Caller-provided random streams remain deterministic. Parallel reductions can differ in their final floating-point bits across hardware and providers, so JDistlib does not promise bit-identical chains across CPU, CUDA, OpenCL, or Vulkan devices. Use CPU for the cross-machine reference path; for accelerated reproduction, retain the backend/device manifest and use the same software and hardware configuration.