enclosure-gemm-xpu
enclosure-gemm-xpu returns a pair of IEEE matrices (lo, hi) bracketing the
matrix product evaluated in exact real arithmetic, on Intel GPUs. It is the SYCL
backend of
enclosure-gemm,
same contract and same bytes. When both operands scale losslessly into its fixed
point, the bracket is the tightest the format admits and its width is
independent of the contraction length: one representable step at K = 16 and
one at K = 65536.
An interval GEMM rounds outward on each partial sum, which requires directed
rounding; SYCL exposes none, having no rounding-mode intrinsic and no per-thread
rounding state. Here the inner product is accumulated as an exact integer and
the outward rounding is bit manipulation on that integer. IEEE values are
carried as integer bit patterns throughout, so nothing in the translation unit
requires aspect::fp64. Iris Xe (Gen12LP) reports fp64 absent and cannot
construct a torch.float64 matmul primitive; it runs this kernel's float64 path
and returns the bytes an NVIDIA sm_89 part returns.
Usage
import torch
from kernels import get_kernel
eg = get_kernel("phanerozoic/enclosure-gemm-xpu", version=1, trust_remote_code=True)
A = torch.randn(256, 1024, dtype=torch.float64).to("xpu")
B = torch.randn(1024, 256, dtype=torch.float64).to("xpu")
lo, hi = eg.mm_enclose(A, B) # lo <= A @ B <= hi, entrywise, exactly
eg.width_ulps(lo, hi).max() # 1
eg.digest(lo, hi) # identical on the CUDA and CPU backends
version selects the release branch; trust_remote_code is required by
kernels for publishers without the trusted-publisher mark. Operands are
float32 or float64. On a device without an fp64 unit, construct operands on the
host and move them: the kernel does not require fp64, torch.randn on the
device does.
API
| Symbol | Purpose |
|---|---|
mm_enclose(A, B) |
(lo, hi) bracketing the exact real product A @ B |
mm_enclose(A, B, return_info=True) |
adds the fixed-point plan and whether the scaling was lossless |
mm_enclose_nt(A, Bt) |
A @ Bt.T for Bt [N, K], the orientation the kernels take |
bmm_enclose(A, B) |
batched, [b, M, K] @ [b, K, N], one fixed point for the batch |
bmm_enclose_nt(A, Bt) |
batched, transposed second operand |
mm_interval(A_mid, A_rad, B_mid, B_rad) |
enclosure for midpoint-radius interval operands |
mm_interval_nt(A_mid, A_rad, Bt_mid, Bt_rad) |
the same, transposed second operand |
contains(lo, hi, X) |
containment check |
width_ulps(lo, hi) |
bracket width per entry, in representable values |
digest(lo, hi) |
SHA-256 over the raw bits |
set_operand_checks(bool) |
the split status check and its synchronisation |
Method
Operands are scaled by a power of two and floored into two's-complement integers of up to four int64 limbs, the scale taken from their exponent range. The inner product of the limb matrices is accumulated in 384-bit integer arithmetic, one work-item per output element; the CUDA backend recovers the same integer from residues on tensor cores. The outward-rounding epilogue is the shared core verbatim. The enclosure-gemm card gives the full account.
Work-items are independent and no group collective appears, so the tail requires no early return and cannot leave a collective unreached, which is undefined behaviour that removes the device. The integer width of the bit-pattern tensor names the format on the way in and on the way out, int32 for binary32 and int64 for binary64, avoiding the width conversions torch XPU does not provide. The second operand is transposed as bit patterns rather than as floats, so the copy is an integer copy the device can perform. Kernel lambdas are named, and limb counts arrive as template parameters so the loops in the shared core fold to straight-line code.
Measured
Bracket width against the same computation with directed rounding on each accumulation, random normal operands:
| K | enclosure-gemm-xpu | directed-rounding interval |
|---|---|---|
| 16 | 1 ulp | 52 ulp |
| 128 | 1 ulp | 189 ulp |
| 1024 | 1 ulp | 2504 ulp |
| 8192 | 1 ulp | 19771 ulp |
| 65536 | 1 ulp | 39266 ulp |
At 256x1024x256 float64 on an Iris Xe (Gen12LP, 96 EUs, no matrix engine, no
fp64 unit): 34.5 ms, 1,943 M mac/s. The same shape is 559 M mac/s on 24 x86_64
threads and 59,215 M mac/s on an RTX 6000 Ada. The rate is set by 64-bit integer
multiply and a dependent carry chain, not by any matrix unit.
Correctness
The reference is exact rational arithmetic. Every binary64 value is a rational,
so the tests evaluate the true inner product with fractions.Fraction and
require the returned pair to bracket it, and where the scaling is lossless to
equal its exact floor and ceiling in the format. Cases: random operands at four
shapes; K from 8 to 16384; integer operands, where the product is
representable and lo == hi must equal the torch result; catastrophic
cancellation; subnormal operands; overflow to infinity; float32; operands
spanning 520 binades, where containment is required and tightness is not;
interval operands against sampled corners.
(lo, hi) is bit-identical across NVIDIA sm_89 under nvcc, Intel Iris Xe
Gen12LP under icpx, x86_64 under MSVC, and Cortex-A76 and Cortex-A72 under GCC,
compared by SHA-256 over the raw bits on a frozen case set including subnormals,
cancellation and mixed scale.
A kernel on this stack can compile, launch, return zero and write nothing, so the epilogue is also given a sentinel-filled destination and required to have overwritten every element.
Requirements and limits
- Intel GPU with a torch XPU build; compliance variants target oneAPI 2025.3.2 and 2026.0.
K <= 2^17andbits_a + bits_b + ceil(log2 K) <= 332, matched to the CUDA backend's residue range so an input accepted by one backend is accepted by all and yields the same bits.- The one-step bracket requires both operands to scale losslessly into a 256-bit
fixed point, so their exponent spread must fit the budget above. Wider
operands receive a valid bracket and
return_infomarks it. - One work-item per output element. Gen12LP has no matrix engine.
- Operands must be contiguous: making a non-contiguous float tensor contiguous invokes a typed copy, which a device without an fp64 unit cannot run.
- Non-finite operands raise rather than propagate.
References
Moore 1966 (Interval Analysis); Rump 1999 (INTLAB, midpoint-radius product bound); Kulisch and Miranker 1986 (exact dot product).
License
Apache-2.0.
- Downloads last month
- -
- OS
- linux
- Arch
- x86_64
- Kernel Builder
- 22e2aad
