Skip to content

test: migrate blas/base/zaxpy to ULP-based assertions - #15436

Draft
Planeshifter wants to merge 1 commit into
developfrom
philipp/ulp-zaxpy
Draft

Planeshifter wants to merge 1 commit into
developfrom
philipp/ulp-zaxpy

Conversation

@Planeshifter

@Planeshifter Planeshifter commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Resolves a part of #11352.

Description

What is the purpose of this pull request?

This pull request:

  • migrates the tests for blas/base/zaxpy from computed relative-tolerance comparisons (delta = abs( actual[ i ] - expected[ i ] ) / tol = rtol * EPS * abs( expected[ i ] ), asserted via t.ok( delta <= tol, ... )) to ULP-difference assertions using @stdlib/assert/is-almost-same-value.
  • applies the migration to all four test files containing the shared isApprox( t, actual, expected, rtol ) helper: test/test.zaxpy.js, test/test.ndarray.js, test/test.zaxpy.native.js, and test/test.ndarray.native.js. The helper's rtol parameter becomes a ulp parameter, and every call site is preceded by a per-test-body var ULP; declaration set to the minimum integer bound that makes that test's assertions pass.
  • collapses the if ( actual[ i ] === expected[ i ] ) { ... } else { ... } branch in the helper into a single ULP assertion, and standardizes the assertion message to 'returns expected value'.
  • removes the now-unused @stdlib/constants/float64/eps and @stdlib/math/base/special/abs requires and the delta and tol variable declarations.

This mirrors the already-merged migration of blas/base/drot, which uses the same isApprox helper shape with @stdlib/assert/is-almost-same-value (double-precision), and lapack/base/crot, which migrated the analogous single-precision isApprox helper.

The remaining assertions in these files are exact comparisons (arity checks, returned references, and the deepEqual checks that both vectors are unchanged when N <= 0 or alpha === 0), which are correct as-is and are left unchanged.

Only test files are changed; no implementation, fixture, or documentation changes are included. No new package.json dependency was needed, as @stdlib/assert/is-almost-same-value is already used elsewhere in the repository.

ULP bounds

All tests previously used a relative tolerance of 14.0. The measured, tightened ULP bounds are:

  • test/test.zaxpy.js / test/test.zaxpy.native.js — "scales elements from x by alpha and adds the result to y": 24 ULP
  • test/test.zaxpy.js / test/test.zaxpy.native.js — "supports an x stride": 8 ULP
  • test/test.zaxpy.js / test/test.zaxpy.native.js — "supports a y stride": 6 ULP
  • test/test.zaxpy.js / test/test.zaxpy.native.js — "supports negative x strides": 24 ULP
  • test/test.zaxpy.js / test/test.zaxpy.native.js — "supports negative y strides": 8 ULP
  • test/test.zaxpy.js / test/test.zaxpy.native.js — "supports complex access patterns": 6 ULP
  • test/test.zaxpy.js (only) — "supports view offsets": 24 ULP
  • test/test.ndarray.js / test/test.ndarray.native.js — "supports an x offset": 11 ULP
  • test/test.ndarray.js / test/test.ndarray.native.js — "supports a y offset": 1 ULP
  • test/test.ndarray.native.js (only) — "if dcabs1( alpha ) === 0.0, the function returns the second input array unchanged": 0 ULP (exact)

Each bound is the minimum integer N such that isAlmostSameValue( actual[ i ], expected[ i ], N ) holds at every element compared in that test. The bounds were determined by measuring the exact per-element ULP distance with @stdlib/number/float64/base/ulp-difference, run directly against the JavaScript implementation for every fixture in both test.zaxpy.js and test.ndarray.js. The test.*.native.js files reuse the same bounds, since the native addon could not be built/verified in this environment (the tests are skip-guarded via tryRequire and did not execute), and the underlying operation is a straightforward multiply-add with no transcendental functions, so JS/C divergence beyond these bounds is not expected. The one native-only test (dcabs1( alpha ) === 0.0) is an exact no-op case, so its bound is 0.

make test TESTS_FILTER=".*/blas/base/zaxpy/.*" was run twice at the final bounds with identical results on both runs (test.zaxpy.js: 111/111 passing, test.ndarray.js: 128/128 passing, test.js: 5/5 passing, the two native files fully skipped as their addon is unavailable), ruling out FMA/architecture-dependent flakiness on this platform. npx eslint and make lint-javascript-tests TESTS_FILTER=".*/blas/base/zaxpy/.*" are clean.

I initially attempted lapack/base/zrot (explicitly suggested as a follow-on in the merged lapack/base/crot PR), but abandoned it: one fixture element has an expected value of exactly 0.0 while the actual double-precision result is 2.220446049250313e-16 (a genuine, reproducible discrepancy, not a transcription error), and the ULP distance between 0 and that value is astronomically large (~4.37×10¹⁸, since it spans the subnormal range), making a small, meaningful ULP bound impossible for that test. blas/base/zaxpy does not hit this pathology, since no exact-zero expected values occur alongside nonzero actual results in its ULP-checked comparisons.

Related Issues

Does this pull request have any related issues?

This pull request has the following related issues:

Questions

Any questions for reviewers of this pull request?

No.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

Two environment notes, neither of which affected verification:

  • make install-node-modules initially failed with ETARGET for es-object-atoms@^1.1.2, because the local npm packument cache served a stale, filtered view of the registry (confirmed by querying the registry directly, which lists 1.1.2). npm cache clean --force resolved it, and the full toolchain was available for the linting and testing reported above.
  • The pre-commit hook's lint-editorconfig-files step could not run, because it downloads the editorconfig-checker binary from a GitHub release that this environment's network policy does not permit (403). The four changed files were instead checked manually against .editorconfig: LF line endings, tab indentation, no trailing whitespace, and a final newline.

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

This PR was written primarily by Claude Code, running as an unattended scheduled task. It selected the package (after abandoning lapack/base/zrot for the reason described above), studied previously migrated packages in the same family (blas/base/drot, lapack/base/crot) to match the established idiom, performed the migration, and determined the minimum passing ULP bounds empirically over every compared element.


@stdlib-js/reviewers

🤖 Generated with Claude Code

https://claude.ai/code/session_01PLAefATEDhsbjEiq9xEYN4

Resolves a part of #11352.

Migrates the tests for blas/base/zaxpy from computed relative-tolerance
comparisons (delta = abs(actual[i] - expected[i]) / tol = rtol * EPS *
abs(expected[i]), asserted via t.ok(delta <= tol, ...)) to ULP-difference
assertions using @stdlib/assert/is-almost-same-value, mirroring the
established idiom in blas/base/drot: the shared isApprox(t, actual,
expected, ulp) helper collapses to a single isAlmostSameValue assertion,
and each test body declares a single named ULP constant sized to the
minimum integer bound that passes for that test.

Applies to all four test files (test.zaxpy.js, test.ndarray.js, and their
.native.js counterparts, including the native-only "dcabs1(alpha) === 0.0"
test in test.ndarray.native.js). ULP bounds were determined by measuring
the exact per-element ULP distance for every fixture via
@stdlib/number/float64/base/ulp-difference, run directly against the
implementation.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PLAefATEDhsbjEiq9xEYN4
@stdlib-bot stdlib-bot added BLAS Issue or pull request related to Basic Linear Algebra Subprograms (BLAS). Good First PR A pull request resolving a Good First Issue. labels Sep 22, 2026
@stdlib-bot

Copy link
Copy Markdown
Contributor

Coverage Report

Package Statements Branches Functions Lines
blas/base/zaxpy $\\color{green}417/417$
$\\color{green}+100.00\\%$
$\\color{green}18/18$
$\\color{green}+100.00\\%$
$\\color{green}4/4$
$\\color{green}+100.00\\%$
$\\color{green}417/417$
$\\color{green}+100.00\\%$

The above coverage report was generated for the changes in this PR.

@kgryte kgryte left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to inline ULP values.

@kgryte kgryte added Tests Pull requests specifically adding tests. Needs Changes Pull request which needs changes before being merged. labels Sep 23, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BLAS Issue or pull request related to Basic Linear Algebra Subprograms (BLAS). Good First PR A pull request resolving a Good First Issue. Needs Changes Pull request which needs changes before being merged. Tests Pull requests specifically adding tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants