Test suite for wgblas — 26 BLAS Level 1, 2, and 3 routines, each covered
by four kinds of test.
Test Types
The first three live in test.<routine>.js; the fourth is a separate file:
validation — rejects every class of bad input defined in
tests/validation/params/*.json: wrong type, wrong size, out-of-range
value. Catches guard-clause bugs before any GPU work begins.
fixtures — 100 property-based random runs via fast-check, comparing
the GPU result against @stdlib (or a hand-written reference, for
routines @stdlib doesn't cover) as the CPU oracle, using a
routine-specific error metric — see below. Inputs avoid subnormals to
stay safe under WGSL's flush-to-zero (FTZ) rule.
edge cases — deterministic checks for routine-specific behaviour:
ties, strides, identity parameters, exact Pythagorean results, per-flag
srotm paths. The 14 routines with a layout parameter (matrix
routines that accept row-major or column-major storage) get a second
edge-cases pass — edge-cases-column-major.json — run as its own test.
GPU-resident coverage (gpustorage.<routine>.js) — the same
fixtures inputs and stdlib oracle as test 2 and 3, but run through the
GpuVector/GpuMatrix-resident overload instead of the plain-array
one. Runs via make test-gpu/make test-gpu-<routine>, a separate
invocation from make test — and not part of the Kaggle CI kernel,
which only runs make test.
Error Metrics
Four metric families, chosen per routine by what kind of floating-point
error the operation can legitimately produce:
exact match — the operation has no rounding to tolerate (a copy, a
swap, a single multiply, an index) so GPU and CPU must agree bit-for-bit
(or index-for-index); threshold 0.
reduction ULP (ulpDiff/ulpDiff64) — GPU sum/norm routines use a
tree reduction while the CPU reference sums sequentially; different
summation order gives a different (still correct) rounding, bounded in
ULPs rather than required to match exactly.
forward-error factor (forwardFactor) — normalizes the raw error by
a routine-derived rounding-error bound instead of counting ULPs, so a
legal FMA fusion or a cancellation-shrunk result doesn't read as a
failure:
$$\text{factor} = \max_i \frac{|\text{err}_i|}{\varepsilon \cdot \text{bound}_i}, \qquad \varepsilon = 2^{-23}$$
err_i = |gpu_i - ref_i| at each output index i (or i,j for a
matrix output) — bound_i is routine-specific; every routine's is in
the Equation column of the table below.
backward-residual factor (backwardResidualFactor) — for
triangular solves, forward-comparing to a computed "true" x is the
wrong question (a solve can be ill-conditioned without being wrong).
Plugs the GPU's answer back into the original equation and checks the
residual instead, LAPACK-style — same shape as forward-error factor
above, but err_i is now a residual, not a gpu-vs-ref difference:
$$\text{factor} = \max_i \frac{|\text{err}_i|}{\varepsilon \cdot \text{bound}_i}, \qquad \text{err}_i = \left|(\operatorname{op}(A)x_{\text{gpu}})_i - b_i\right|$$
(strsm's right-hand side is \alpha B_{ij}, indexed i,j — a matrix,
not a vector b). bound_i is again routine-specific — strsv's and
strsm's are in the table below, and differ for the reason given there.
n/k below are each routine's own reduction length; r = aOrder, the
shared dimension A is read over (m for side="left", n for
side="right", in ssymm/strmm/strsm).
Runs validation + fixtures + edge cases for all 26 routines (test.*.js)
make test-<routine>
Runs one routine's test.<routine>.js, e.g. make test-saxpy
make test-gpu
Runs GPU-resident coverage for all 26 routines (gpustorage.*.js)
make test-gpu-<routine>
Runs one routine's gpustorage.<routine>.js
Pass ARGS=low-power to prefer the integrated GPU on dual-GPU machines:
make test-saxpy ARGS=low-power. Node's test runner doesn't forward
trailing CLI args the way a plain node script.js invocation does, so
this goes through the WGBLAS_POWER_PREFERENCE env var instead — see
tests/helpers/device.js.
Test suite for wgblas — 26 BLAS Level 1, 2, and 3 routines, each covered by four kinds of test.
Test Types
The first three live in
test.<routine>.js; the fourth is a separate file:tests/validation/params/*.json: wrong type, wrong size, out-of-range value. Catches guard-clause bugs before any GPU work begins.@stdlib(or a hand-written reference, for routines@stdlibdoesn't cover) as the CPU oracle, using a routine-specific error metric — see below. Inputs avoid subnormals to stay safe under WGSL's flush-to-zero (FTZ) rule.layoutparameter (matrix routines that accept row-major or column-major storage) get a second edge-cases pass —edge-cases-column-major.json— run as its own test.gpustorage.<routine>.js) — the same fixtures inputs and stdlib oracle as test 2 and 3, but run through theGpuVector/GpuMatrix-resident overload instead of the plain-array one. Runs viamake test-gpu/make test-gpu-<routine>, a separate invocation frommake test— and not part of the Kaggle CI kernel, which only runsmake test.Error Metrics
Four metric families, chosen per routine by what kind of floating-point error the operation can legitimately produce:
0.ulpDiff/ulpDiff64) — GPU sum/norm routines use a tree reduction while the CPU reference sums sequentially; different summation order gives a different (still correct) rounding, bounded in ULPs rather than required to match exactly.forwardFactor) — normalizes the raw error by a routine-derived rounding-error bound instead of counting ULPs, so a legal FMA fusion or a cancellation-shrunk result doesn't read as a failure: $$\text{factor} = \max_i \frac{|\text{err}_i|}{\varepsilon \cdot \text{bound}_i}, \qquad \varepsilon = 2^{-23}$$err_i = |gpu_i - ref_i|at each output indexi(ori,jfor a matrix output) —bound_iis routine-specific; every routine's is in the Equation column of the table below.backwardResidualFactor) — for triangular solves, forward-comparing to a computed "true"xis the wrong question (a solve can be ill-conditioned without being wrong). Plugs the GPU's answer back into the original equation and checks the residual instead, LAPACK-style — same shape as forward-error factor above, buterr_iis now a residual, not agpu-vs-refdifference: $$\text{factor} = \max_i \frac{|\text{err}_i|}{\varepsilon \cdot \text{bound}_i}, \qquad \text{err}_i = \left|(\operatorname{op}(A)x_{\text{gpu}})_i - b_i\right|$$ (strsm's right-hand side is\alpha B_{ij}, indexedi,j— a matrix, not a vectorb).bound_iis again routine-specific —strsv's andstrsm's are in the table below, and differ for the reason given there.n/kbelow are each routine's own reduction length;r=aOrder, the shared dimensionAis read over (mforside="left",nforside="right", in ssymm/strmm/strsm).Make Commands
make testtest.*.js)make test-<routine>test.<routine>.js, e.g.make test-saxpymake test-gpugpustorage.*.js)make test-gpu-<routine>gpustorage.<routine>.jsPass
ARGS=low-powerto prefer the integrated GPU on dual-GPU machines:make test-saxpy ARGS=low-power. Node's test runner doesn't forward trailing CLI args the way a plainnode script.jsinvocation does, so this goes through theWGBLAS_POWER_PREFERENCEenv var instead — seetests/helpers/device.js.