FP64-accurate matrix multiplication from BF16 hardware via Ozaki Extract in JAX
Project description
ozaki-jax
FP64-accurate matrix multiplication built from BF16 GEMMs using Ozaki Extract in JAX.
For square test matrices, the default implementation uses 36 BF16 GEMMs and reaches about 1e-16 relative error against FP64 reference results.
Usage
import numpy as np
from ozaki_jax import matmul
A = np.random.randn(256, 256)
B = np.random.randn(256, 256)
C = matmul(A, B)
Without JAX runtime dependency:
from ozaki_jax import matmul_numpy
C = matmul_numpy(A, B)
On-device extraction path (fixed 65 GEMMs, default accumulation="fused"):
C = matmul(A, B, pipeline="ondevice")
On-device extraction with explicit on-device 2Sum accumulation:
C = matmul(A, B, pipeline="ondevice", accumulation="ondevice")
For accumulation="fused", enable JAX x64 before using matmul:
import jax
jax.config.update("jax_enable_x64", True)
Note: TPU does not provide high-throughput native FP64 in the same way as CPU/GPU
HPC backends. In this project, fused mode uses x64 primarily for on-device
hi/lo splitting before FP32/BF16-style stages. It can reduce host overhead, but
speedups are workload and device dependent; profile before treating it as the
fastest default for a given deployment.
Safety preflight with explicit fallback:
# Reject unsafe Ozaki configurations (default behavior).
C = matmul(A, B, safe_mode="raise")
# Fallback to plain FP64 matmul when preflight fails.
C = matmul(A, B, safe_mode="fallback")
Method summary
Given C = A @ B:
- Split
AandBinto magnitude-controlled slices with Extract. - Run BF16-derived GEMMs for the selected pipeline.
- Rescale and accumulate products.
Pipeline options:
pipeline="host"(default): FP64 host extraction, triangular pairing, 36 GEMMs atn_slices=8.pipeline="ondevice": FP32 extraction (hi/losplit), fixed block structure, 65 GEMMs.
Accumulation options (pipeline="ondevice" only):
accumulation="fused"(default): split, extraction, GEMMs, and 2Sum accumulation in one JIT call (requires JAX x64 enabled).accumulation="ondevice": separate on-device FP32 2Sum accumulation path.accumulation="host": transfer products and accumulate in FP64 on host.
Exactness condition
For BF16 slice values bounded by 2^p - 1, exact FP32 accumulation requires:
K * (2^p - 1)^2 < 2^24
In the default BF16 setting (p = 7), this gives K <= 1040.
Safety preflight
matmul() and matmul_numpy() run a preflight safety check before the Ozaki path.
An input is considered safe only if all checks pass:
- BF16->FP32 exactness bound:
K * (2^p - 1)^2 < 2^24 - Mantissa coverage:
n_slices * p >= 53 - Inputs are finite (no
NaN/Inf) - Shapes are rank-2 and matmul-compatible
On preflight failure:
safe_mode="raise": raisesValueErrorsafe_mode="fallback": returns plain FP64A @ B
For pipeline="ondevice", preflight uses FP32 extraction constraints and the fixed
slice configuration (n_hi=5, n_lo=4).
Notes on rho for BF16
For FP64 source values (m1=53) and BF16 storage (m2=8), storage exactness requires:
rho >= m1 + 1 - m2 = 46
The accumulation constraint is also at least 46 for typical K values in this project, so the implementation uses rho=46 as the lower bound.
Limitations
K <= 1040for the default unblocked exactness condition.- JAX on CPU does not represent TPU BF16 execution behavior.
- Blocking for larger
Kis not implemented.
Benchmarks
python benchmarks/precision.py
python benchmarks/tpu_validate.py
python benchmarks/ondevice_validate.py
References
- Mukunoki, D., Ogita, T., & Imamura, T. (2020). "DGEMM using Tensor Cores, and Its Accurate and Reproducible Versions." ISC High Performance 2020.
- Mukunoki, D. (2025). "Ozaki Scheme-Based Accurate Matrix Multiplication on FP8 Tensor Cores." arXiv:2508.00441
- Ozaki, K., Ogita, T., Oishi, S., & Rump, S.M. (2012). "Error-Free Transformations of Matrix Multiplication by Using Fast Routines of Matrix Multiplication and Its Applications." Numerical Algorithms, 59(1).
License
Apache 2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ozaki_jax-0.5.0.tar.gz.
File metadata
- Download URL: ozaki_jax-0.5.0.tar.gz
- Upload date:
- Size: 52.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4eca7fabcac0e9052e83e3b2543a6e9e1f89a36e0631c3697e6d31994b288ded
|
|
| MD5 |
8bb8dfff1de8f4c8c2a5342f4895405d
|
|
| BLAKE2b-256 |
bd14f567e0932df4340b01e988781c18ecaa740eb7a39a3ab4c1c28f751d7712
|
File details
Details for the file ozaki_jax-0.5.0-py3-none-any.whl.
File metadata
- Download URL: ozaki_jax-0.5.0-py3-none-any.whl
- Upload date:
- Size: 19.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c698bb89e3158bdfc5ee4afa565173e10625ebe3d217b897535d563f56498c9
|
|
| MD5 |
b002a6159c6f121b5e82ae6ecf098d34
|
|
| BLAKE2b-256 |
4b76aa6af9425153aa0e421464c05ffec1298c852c973ffa6c297c0472c03e77
|