Skip to content

BLAS TESTING: Improve coverage of ?ROT, ?ROTG, ?ROTMG - #1414

Open
ACSimon33 wants to merge 7 commits into
Reference-LAPACK:masterfrom
ACSimon33:blas-cov-rot
Open

ACSimon33 wants to merge 7 commits into
Reference-LAPACK:masterfrom
ACSimon33:blas-cov-rot

Conversation

@ACSimon33

@ACSimon33 ACSimon33 commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Description

CROTG, ZROTG, CSROT and ZDROT had no case in the complex Level 1 testers at all, so all four were entirely unexercised. CHECK0 gains a ?ROTG case covering the branches the argument pair selects (B zero, A zero with B real, imaginary or general, and the ordinary path), plus eight more whose operands sit below, above and astride the window in which ?ROTG uses its unscaled algorithm, and astride the points at which each algorithm changes how it forms S. Their results are awkward to tabulate, so every case checks the identities that define the rotation, C*A + S*B = R and C**2 + ABS(S)**2 = 1. That is a stronger statement than a table of expected digits and needs no reference values. ?ROT is driven over unit, non-unit and negative strides in both arguments, with the expected vectors indexed explicitly, so the stride arithmetic in the routine is what gets checked rather than restated, and once with N = 0 to pin its quick return. CHECK2 returns before its main loop for the new ?ROT case, whose dispatch has no arm for it and would otherwise reach a STOP that Fortran exits successfully.

In the real testers, ?ROTG gains a ninth case with A small enough against B that C = A/R underflows to zero, the only way to reach the Z = ONE branch. Both values are powers of two, so the expected results are exact. ?ROTMG gains a tenth case with DD1 < 0, the input that makes it zero H, D and DX1 and return DFLAG = -1, and two more that enter its scale check holding a flag no earlier input paired with it: one rescales D1 having set DFLAG = 1, the other rescales D2 having set DFLAG = 0, and each of those arms rewrites H into the DFLAG = -1 form before scaling. Their inputs are powers of two, so the expected values stay exact enough to tabulate with the rest rather than needing an identity check. A thirteenth case reaches the guard the routine keeps for safety against rounding: one ulp inside the boundary at which ABS(SQ1) > ABS(SQ2) flips, the two separately rounded quotients behind SU make it exactly zero, and the guard returns D = 0, H = 0, DFLAG = -1.

Coverage

gcov under gfortran 13.3.0 at -O0, each ?blat1 driver linked against an otherwise uninstrumented BLAS. Single and complex shown; double and double complex are identical.

Routine Lines Branches
SROTG 96.30% to 100.00% 100.00%, unchanged
CROTG 0.00% to 97.44% 0.00% to 100.00%
CSROT 0.00% to 100.00% 0.00% to 100.00%
SROTMG 77.00% to 100.00% 94.12% to 100.00%

What is left uncovered: C/ZROTG keep the two C < SAFMIN arms, which need F2/H2 < SAFMIN**2. On the scaled path both operands are scaled to components of magnitude at most one, so F2/H2 >= SAFMIN/4 and that arm is dead by a factor of about 2**62. On the unscaled path the window guards bound F2/H2 below by 2*SAFMIN/SAFMAX, and SAFMIN*SAFMAX is exactly 2 in both single and double, so the bound is SAFMIN**2 itself: unreachable in exact arithmetic, with a margin of about one ulp, and a sweep of the last ulps at both edges of the window did not reach it.

?ROTMG is fully covered, and its guard is not dead code: without it D1/SU is infinite on the thirteenth input and the scale check loops on infinity forever. That is exactly what MKL and OpenBLAS do; xblat1s and xblat1d linked against either pass the first ten subprograms and then hang at ?ROTMG (each was run under a 60 s timeout). Checked on MKL 2025.3, 2026.0 and 2026.1, and on OpenBLAS 0.3.27 and develop. I'll file additional issues and link them here later.

ACSimon33 and others added 5 commits September 14, 2026 16:15
?rotg sets Z = ONE only when C = A/R underflows to zero, which needs A
negligible against B; a ninth case built from powers of two reaches it
exactly.  ?rotmg zeroes H, D and DX1 when DD1 is negative, which no
existing input triggered; a tenth case supplies one.

?rotg is now fully covered.  ?rotmg keeps two gaps: the DU <= 0 branch
its own comment describes as unreachable outside rounding edge cases,
and the DFLAG rescaling arms, which need inputs that rescale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The complex Level 1 testers had no case for either routine, so both
were entirely unexercised.  ?ROTG gets a CHECK0 of its own, covering
the branches its argument pair selects: B zero, A zero with B real,
imaginary or general, and the ordinary path.  Every expected value
satisfies C*A + S*B = R and C**2 + ABS(S)**2 = 1.

?ROT is driven over unit, non-unit and negative strides with the
expected vectors indexed explicitly, so the stride arithmetic in the
routine is what is checked rather than restated.

CHECK2 returns before its main loop for the new case: that loop's
dispatch has no arm for it and would reach its STOP, which Fortran
exits successfully, so the failure would show only as an 'other error'
in the summary rather than a nonzero exit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tabulated cases all sit inside the window where ?ROTG uses its
unscaled algorithm, so none of the scaling code ran.  Five further
cases place the operands below, above and astride that window.

Their results are awkward to tabulate, so these check the identities
that define the rotation instead: C*A + S*B = R and C**2 + ABS(S)**2
= 1.  That is a stronger statement than a table of expected digits
and needs no reference values.

?ROTG goes from 47% to 88% of lines.  What is left is the arm taken
when C = F2/SQRT(F2*H2) underflows below SAFMIN, which the guards
selecting the unscaled algorithm appear to make unreachable: they
bound C below by SAFMIN exactly, so C > SAFMIN always holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three more ?ROTG pairs reach the scaled F2 < H2*SAFMIN arm and both
guards against SQRT(F2*H2) leaving range.  ?ROT gains a negative INCY
and an N = 0 call.  What is left in ?ROTG is the C < SAFMIN arm, which
SAFMIN*SAFMAX = 2 makes unreachable: it needs F2/H2 < SAFMIN**2, and
the guards bound F2/H2 below by exactly that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scale check rewrites H into the DFLAG = -1 form with one arm per
incoming flag, and no case had rescaled D1 holding DFLAG = 1, or D2
holding DFLAG = 0.  Two cases built from powers of two do, which keeps
the expected values exact enough to tabulate alongside the rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.41%. Comparing base (a6c6e74) to head (66b0014).
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1414      +/-   ##
==========================================
+ Coverage   69.36%   69.41%   +0.05%     
==========================================
  Files        6122     6122              
  Lines      486711   487029     +318     
  Branches    23268    23268              
==========================================
+ Hits       337584   338062     +478     
+ Misses     148689   148533     -156     
+ Partials      438      434       -4     
Components Coverage Δ
BLAS 99.13% <ø> (+1.19%) ⬆️
CBLAS 96.98% <ø> (ø)
LAPACK 82.38% <ø> (ø)
LAPACKE 2.17% <ø> (ø)
TMGLIB 55.69% <ø> (ø)
BLAS testing 88.54% <100.00%> (+0.20%) ⬆️
CBLAS testing 89.63% <ø> (ø)
LAPACK testing 82.20% <ø> (ø)
LAPACKE testing ∅ <ø> (∅)
Files with missing lines Coverage Δ
BLAS/TESTING/cblat1.f 91.72% <100.00%> (+2.95%) ⬆️
BLAS/TESTING/dblat1.f 91.81% <100.00%> (+0.96%) ⬆️
BLAS/TESTING/sblat1.f 92.82% <100.00%> (+0.86%) ⬆️
BLAS/TESTING/zblat1.f 91.72% <100.00%> (+2.95%) ⬆️

... and 8 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a6c6e74...66b0014. Read the comment docs.

ACSimon33 and others added 2 commits September 14, 2026 19:37
ABS(SQ1) > ABS(SQ2) is decided on (SD*SX)*SX while SU comes from two
separately rounded quotients, so one ulp inside that boundary SU can
round to zero.  The reference guard then zeroes H, D and X1; without
it the scale check loops on Inf forever, as MKL 2025.3 and OpenBLAS
0.3.27 do on this input.  The rounded quotients' exact product is at
least one, so an FMA for ONE - SH12*SH21 changes nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
R comes back real, so passing it to CTEST as the size asked the
imaginary part of C*A + S*B for an exact zero.  Near either end of the
range that part is a cancellation of two quantities of size
ABS(A)+ABS(B), which is the scale the residual belongs to: CI saw
1.6E+11 against 1.3E+19, half an ulp, on every aarch64 target and on
gfortran 14 and 15.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant