Skip to content

Complete BLAS Level 1-3 API and add NaN guards to statistics - #20

Merged
AdaWorldAPI merged 13 commits into
masterfrom
claude/continue-session-0mAVa
Mar 22, 2026
Merged

Complete BLAS Level 1-3 API and add NaN guards to statistics#20
AdaWorldAPI merged 13 commits into
masterfrom
claude/continue-session-0mAVa

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Summary

This PR completes the BLAS Level 1-3 trait APIs in ndarray's HPC module and hardens numerical stability by adding guards against division-by-zero in statistics and awareness classification.

Key Changes

BLAS API Completion

Level 1 (blas_level1.rs)

  • Added blas_rotg() function and GivensRotation<A> struct for Givens rotation computation
  • Implements the standard BLAS ROTG operation: given scalars a and b, computes rotation parameters (r, c, s) such that the rotation zeros out the second component
  • Includes comprehensive tests for both normal and zero-input cases

Level 2 (blas_level2.rs)

  • Added blas_syr() — symmetric rank-1 update: A = alpha * x * x^T + A
  • Added blas_syr2() — symmetric rank-2 update: A = alpha * x * y^T + alpha * y * x^T + A
  • Added blas_gbmv() — general banded matrix-vector multiply with band storage format
  • Added blas_sbmv() — symmetric banded matrix-vector multiply
  • All operations respect Uplo (Upper/Lower) triangle specification
  • Includes tests validating band storage indexing and triangle-only updates

Level 3 (blas_level3.rs)

  • Added blas_trmm() — triangular matrix-matrix multiply
  • Supports both Side::Left (alpha * A * B) and Side::Right (alpha * B * A)
  • Respects Uplo triangle specification for the triangular matrix
  • Includes tests for both left and right multiplication

Numerical Stability Hardening

statistics.rs

  • Added zero-length axis guard in var_axis() to prevent NaN from division by zero
  • Returns appropriately-shaped zero array when axis length is 0
  • Added test case var_axis_zero_length_axis_no_nan

bf16_truth.rs

  • Added zero-dims guard in awareness_classify() to prevent division by zero when computing percentages
  • Returns empty SuperpositionState when n_dims == 0
  • Added test case validating zero-dims handling

cascade.rs

  • Added zero-check guard in warmup calibration to prevent division by zero
  • Ensures sigma_pop is 0.0 when warmup_n == 0 instead of computing 0.0/0.0

Documentation & Constants

quantized.rs

  • Added BF16::ZERO and BF16::ONE constants for common values
  • Added from_f32_truncate() alias to clarify truncation semantics (matching rustyblas naming convention)
  • Improved docstring for from_f32() to explicitly document truncation behavior

Implementation Details

  • All BLAS operations follow standard BLAS semantics and naming conventions
  • Band storage format uses row-major indexing: element A(i,j) stored at band[ku + i - j, j]
  • Triangle operations use Uplo enum to determine which half of the matrix to access
  • All new functions include comprehensive unit tests with known-good expected values
  • NaN guards use early returns with appropriately-shaped zero arrays to maintain type consistency

Testing

  • 8 new unit tests for BLAS operations (syr, syr2, gbmv, sbmv, trmm variants)
  • 3 new unit tests for numerical stability guards
  • All tests verify both correctness and edge case handling

https://claude.ai/code/session_01CdqyUTUfjKZuk8YGJzv6LB

claude and others added 13 commits March 22, 2026 08:21
Documents module map (36,868 LOC across 50 modules), BLAS parity gaps
(15/23 routines missing), quantized GEMM verification, NaN guard audit
(3 unguarded divisions found), and not-yet-migrated upstream modules.

https://claude.ai/code/session_01CdqyUTUfjKZuk8YGJzv6LB
rustynum's simd_avx512.rs = std::simd compat layer (F32x16, F64x8, U8x64 etc.)
backed by stable core::arch. ndarray uses raw __m512 — works but x86_64-only.
Port as src/backend/simd_compat.rs to unlock aarch64 and std::simd migration.
Cranelift JIT for scan param baking — real infrastructure, not a JSON parser.
…t scan opt

array_windows (1.94) covers scan record-size baking. jitson's real purpose:
compile graph topology → native function via Cranelift. Keep at P2.
hybrid.rs=3-stage pipeline, delta.rs=XOR overlay, layer_stack.rs=collapse gate,
soaking.rs=int8 accumulation, tail_backend.rs=libCEED trait. All P1.
10 features cross-referenced. Compat layer unblocks 5 of them.
gather/dispatch/prefetch exist internally but need user-facing API.
SpatialArray3 and stencil are genuinely new P3 types.
…wiring

array_struct.rs has 35 SimdOps dispatch calls that ndarray lacks.
activations.rs sigmoid/softmax use mapv (scalar).
vml.rs exp/log/sqrt use for loops (scalar).
Backend kernels exist (kernels_avx512.rs) but aren't wired to hpc traits.
Port rustynum simd_avx512.rs → src/backend/simd_compat.rs.
11 types, 60 impl blocks, zero runtime cost.
Refactor kernels_avx512.rs + bitwise.rs to use compat types.
Wire activations.rs + vml.rs through SIMD dispatch.
Level 1: scal, asum, iamax, swap, copy, rotg
Level 2: trsv, symv, syr, syr2, gbmv, sbmv
Level 3: trmm, trsm, symm

https://claude.ai/code/session_01CdqyUTUfjKZuk8YGJzv6LB
@AdaWorldAPI
AdaWorldAPI merged commit de05727 into master Mar 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22bfb7ab01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/hpc/blas_level2.rs
Comment on lines +413 to +417
Uplo::Lower => {
if j <= i {
self[[j.abs_diff(i), j.max(i)]]
} else {
self[[i.abs_diff(j), i.max(j)]]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Index lower-band sbmv data from the smaller column

When blas_sbmv(..., Uplo::Lower, ...) is given a normally packed lower band, this branch reads off-diagonal entries from max(i, j) instead of the stored column min(i, j). For example, with k = 1, A[1,0] should come from the first subdiagonal slot for column 0, but this code fetches column 1 instead, so distinct subdiagonal values are shifted and the matrix-vector product is wrong for essentially every nontrivial lower-stored band matrix.

Useful? React with 👍 / 👎.

Comment thread src/hpc/blas_level1.rs
Comment on lines +160 to +161
let scale = a.abs() + b.abs();
let r = scale * ((a / scale).powi(2) + (b / scale).powi(2)).sqrt();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scale blas_rotg without summing magnitudes first

This normalization overflows for large but still representable inputs. In f64, blas_rotg(1e308, 1e308) should produce a finite rotation, but a.abs() + b.abs() becomes inf, so both normalized terms collapse to zero and r becomes NaN (inf * 0). That propagates to c and s, making the new API fail exactly in the high-magnitude cases where a stable Givens implementation is expected to keep working.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants