Repository navigation
Releases: Nicholas022400701/ulpwise
Releases · Nicholas022400701/ulpwise
Release list
ulpwise 0.3.4
- Regression corpus case for pytorch/rl #4504, merged upstream:
MultiCategorical.to_one_hotiterated overself.nvec,
a 2-D tensor for a spec with a batch shape, soone_hotgot a tensor asnum_classesand raisedTypeError, and the
0-D sample of a shape[1]spec raisedIndexErroronval[..., i]. The case one-hot encodes a sample of
MultiCategorical([3, 2], shape=(4, 2))and the scalar sample ofMultiCategorical([5]). ulpwise scan: newdropout-never-appliedrule (medium), the first that reads a whole file rather than one
function: aDropout,DropPathor aModuleDictorModuleListunder a dropout name is assigned toself
and then never called, never passed on, never returned or iterated and its rate never read, anywhere in the file.
The option is accepted and does nothing, so the model trains without the regularisation it reports. peft #3830 is
the example:OFTLayerbuiltmodule_dropoutintoself.oft_dropout, filled it inupdate_layerand never
called it. Reading the rate counts as use (scaled_dot_product_attention(dropout_p=self.dropout.p)), as does
passing the module to another one; filling it does not, and subclasses ofSequential, which run every
attribute, are not read. On kornia, ultralytics, torchvision, diffusers, torchrl, vllm and detectron2 main the
rule reports nothing; on peft main it reports the #3830 line, on timm main oneattn_dropincoat.pythat a
comment already calls unused, and on transformers main nine lines, four of them modular files whoseforward
lives in another file.
ulpwise 0.3.3
- Two regression corpus cases for peft, both open upstream with a fix PR: peft #3769,
add_weighted_adapterwith
combination_type='svd'crashed for Conv1d and Conv3d LoRA layers because only Conv2d deltas were flattened before
the SVD (the case combines two rank 2 adapters atsvd_rank=4and expects the output of both adapters active, which
the fix matches to 2e-6), and peft #3830, OFTmodule_dropoutwas never applied, so ten training forwards of the
same input were identical where the fix spreads them by 0.56. ulpwise scan: newwhere-nan-gradientrule (medium) forwhere(d > eps, f(d), other)withfa division byd
or asqrt,log,acosorasinof it.whereevaluates both branches and hands the discarded one a zero
gradient, and the backward offat the singularity turns that zero into0 / 0 = nan, so the guard protects the
value and not the gradient. kornia #5579, found by the kornia conventions audit, is the example: the mutual
information losses divided by the range of the signal insidewhere(diff > eps, ...)and a constant input or
target got a nan gradient. The condition may be a comparison, a name bound to one, or~,&,|and a subscript
of those, and the method forma.where(cond, b)counts. Quiet fornumpy.where, which has no autograd, for a
comparison against a number above 1 (strength < 50in kornia's JPEG scale is a piecewise definition), for a
floor namedeps,tol,floor,thresh,tiny,boundorlimiton the other side of the comparison, and
oncedis re-bound to awhere,clampormaximumof itself, which is the recommended fix and the shape of
kornia'sfit_lineandrotation_matrix_to_quaternion. On kornia main d15741e2 the rule reports four lines, on
ultralytics main 8df3534 two, both in metric code, and nothing in torchvision.ulpwise scan: newclamp-at-singularityrule (medium) forclamp(x, min=0).sqrt(),sqrt(clamp(x, min=0)),
clamp(c, -1, 1).acos()and theclip,clamp_min,clamp_max,asinandlogforms: the bound is the point
where the next function has an infinite derivative, and clamp's derivative at its own bound is 1 on torch 2.5.1 and
2.9.1 and 0 on 2.14, so the gradient there isinfornanon the older half of a supported range. kornia #4229,
found by the kornia conventions audit, is the example,_cdist'sclamp(min=0.0).sqrt()with identical float16
descriptors, and kornia #5500'sclamp(-1, 1).acos()the second. A bound strictly inside the domain,min=1e-8,
is a floor and is not reported, a bound given by a name is not read, and numpy is quiet. On kornia main d15741e2
the rule reports the three lines of_solve_cubic_real, on ultralytics main 8df3534 one line ofDepthMetrics.ulpwise scan: a finding inside a nested function is reported once, under the nested function, instead of once
more under the enclosing one, and the enclosing function's call and division counts no longer include it.ulpwise scan: neweps-floorrule (medium) forx * (1 - eps) + eps, in either order and withself.epstoo: the
floor moves every value, 0 becomesepsand 1 stays 1, so the entries of a one-hot sum to1 + (C - 1) epsand a
perfect prediction scores a loss that grows with the image. kornia #5538, found by the kornia conventions audit, is
the example:one_hotfloored its zeros with 1e-6 and a perfect prediction of a 256 × 384 image with a one-pixel
class scored a macro Dice loss of 0.0234. On kornia main 9e188e08 the rule reports that line and nothing else, and
nothing in ultralytics or torchvision;(p + eps) / (q + eps)is not a floor and stays quiet.ulpwise scan: thehypot-by-handadvice no longer offersnormas the replacement.torch.linalg.vector_normand
numpy.linalg.normsquare the entries first too, so in float32 both returninffor a vector with two entries of
1.8e19 whilehypotreturns 2.5e19; the advice now says to divide a vector by its largest absolute entry when the
point is a direction. The kornia maintainer caught the case in review of kornia #5505, wherev / v.norm()turned
such vectors into zeros and the angle between them read 0.ulpwise scan:acos-for-anglemoves frominfotomediumand names the mechanism: next to 1 the cosine has no
digits left for a small angle, soacosrecovers the angle with an absolute error ofsqrt(eps), 0.02 degrees in
float32, whatever the angle. The kornia scan listedkornia/metrics/pose.pyunder this rule, and both functions
there,angle_error_matandangle_error_vec, return exactly 0 for every rotation below 0.03 degrees in float32
and 180.0 for 179.99 degrees (kornia #5500); the rule cites it.- Regression corpus case for kornia #5500: a 0.01 degree rotation scores 0.0 in both metrics on kornia main 050ac77f.
- The kornia #5500 corpus case records its fix, kornia #5505, merged 2026-10-06:
angle_error_matreads the sine from
the skew part of the relative rotation andangle_error_vecfrom the cross product of the scaled vectors, and both
returnatan2(sin, cos), so a 0.01 degree rotation scores 0.0099990 in float32. The case stays an expected failure
until a kornia release carries the fix. ulpwise scan:logsumexp-by-handno longer fires on the stable form, where everyexpargument has its
maximum subtracted first (log(exp(x - x.max()).sum()), ors = x - mwithmbound to amax,amax,
maximumortorch.max(...).values). The lm-evaluation-harness scan listed the numerically stable_log_softmax
oflm_eval/models/_onnx_base.pynext to a reallog(exp(a) + exp(b)); it now reports only the real one.
ulpwise 0.3.2
- Two regression corpus cases for pytorch #199850:
torch.erfin bfloat16 and float16 on CPU returns 0 at and
below 1.8e-7 and loses relative accuracy below 1e-3 (13404 bfloat16 ulps, 5 float16 ulps at worst), found with
the half precision rows ofulpwise survey --functions erf --backends torch --dtypes f16,bf16. - Two regression corpus cases for pytorch #199867:
torch.special.logitin float16 and bfloat16 on CPU rounds1 - x
andx / (1 - x)to the input dtype before the log, so logit(0.499756) in float16 is -0.000488 for an exact -0.000977
(512 ulp, 64 bfloat16 ulp at worst), found withulpwise survey --functions logit --backends torch --dtypes f16,bf16. studies/accuracy-survey-2026-10-half: the second accuracy survey, torch 2.14.0+cpu in float16 and bfloat16 for the
45 functions with half precision CPU kernels. 33 float16 and 34 bfloat16 rows of 45 are correctly rounded;erf
(pytorch #199850) andlogit(pytorch #199867) lose their digits before the rounding step,polygamma(2, x)in
float16 is off at every half integer in(-1024, -256)because the Hurwitz zeta sum accumulates in float for the
reduced types, and the activation tails and thersqrtandi0evector versus scalar disagreements of September
show again.ulpwise survey: thepolygamma_1andpolygamma_2references at a non positive integer are now the signed
infinity of the pole,+inffor an odd order (the limit from both sides) and-inffor an even one (the sign of
(-1) ** (n + 1) * n! * zeta(n + 1, x), which is what scipy, torch and jax return), instead of a domain error
that expectednan. The f16 and bf16 grids reach the integers from 2048 and 256 up, so every negative point
past there was a pole and counted as a nonfinite mismatch: on torch 2.14 the two functions had 354 of 3546
float16 points and 492 of 4016 bfloat16 points each,polygamma_2has 0 now in every dtype andpolygamma_1
keeps 491 bfloat16, 4 float32 and 2 float16 points where torch returns a large finite value instead ofinf
at a negative integer (pytorch #198663).
ulpwise 0.3.1
ulpwise surveytakesf16andbf16in--dtypes(the default staysf32,f64): torch and jax get half
precision rows, measured in float16 or bfloat16 ulps against the same mpmath reference; numpy has no
bfloat16 and scipy.special computes a float16 input in float32, so those two have nobf16rows and scipy
has nof16rows. A backend without a kernel for a function in a dtype (torch's Bessel and Airy functions,
erfcx,ndtri,log_ndtrandzetain half precision) is logged and skipped before the reference is
computed. The torch default tolerances of the two dtypes are(1.6e-2, 1e-5)and(1e-3, 1e-5), as in
torch.testing. A log spaced domain wider than the dtype is now clipped to the dtype's finite range before
the points are spaced: the float16 grid ofsqrtandlogover(1e-300, 1e300)kept 32 of 600 points and
acosh24, they have about 570 and 490 now; the float32 grids of the same functions go from about 100 points
to about 600, the float64 grids are unchanged. The(0, 1)and(-1, 1)grids ofndtri,logit,asin,
acos,atanhanderfinvstop at the float below 1 in half precision too, where1 - 1e-4and1 - 1e-3
rounded to 1. Two corrections to the ulp metric for every dtype: the threshold from which an exact result
counts as a correct overflow to infinity wasfmax * (1 + eps / 2), about one ulp above the largest finite
value, and is nowfmax + ulp / 2, the rounding threshold; and the spacing at the largest finite value was
numpy'sinf, which turned every finite result there into a 0 ulp error, it isulpwise.spacingnow.
An unknown dtype raisesValueErrorinstead of aKeyErrorfrom the grid.next_up,next_down,spacing,neighbours,binade_edgesandall_floatsacceptf16andbf16(and the
float16,half,bfloat16,torch.float16,torch.bfloat16aliases) likeulp_distanceandspecialalready
did; they raisedunsupported dtypebefore. The 16 bit versions are pure Python on the bit patterns, with the
conventions of the Rust ones: the largest float steps to infinity, both zeros to the smallest subnormal,spacing
is the distance from|x|to the next float and stays finite at the largest float,neighboursreturns finite
values only. A test walks every finite float16 and bfloat16 and checks the six functions against each other, against
numpy's float16nextafterandspacingand against torch's bfloat16nextafter.ulpwise scanread the first positional argument of a method call as the value the method is applied
to, solog(exp(x).sum(-1))was not alogsumexp-by-handfinding whilelog(exp(x).sum())and
log(exp(x).sum(dim=-1))were. A method call whose first argument is a dimension (an integer,None
or a tuple of them) is now read as applying to its receiver, like the argument-less form.ulpwise scanprinted aSyntaxWarning(aDeprecationWarningbefore Python 3.12) for every invalid
escape sequence in the files it scanned, four lines on kornia. The parse now ignores those warnings;
a file that does not parse is skipped as before.ulpwise scanreported Windows paths with backslashes (kornia\geometry\conversions.py), so the same
report read differently from the one made on Linux or macOS. Paths are now written with forward
slashes on every platform;--runstill imports the module from either form. The CI pytest job on
ubuntu, macOS and windows runstests/test_scan.pynow, and that file also covers the reasons
--rungives for a function it does not run (a class, a constant, an instance method, a builtin
without a signature, a nested name, a module that fails to import) and the ulp comparison of the
results across infinities, NaNs and an all zero reference.ulpwise surveywithout scipy installed died atndtriwith aModuleNotFoundErrorand lost every
row computed before it: the reference started its Newton iteration fromscipy.special.ndtri. It now
starts from a bisection onmath.erfc, returns an exact zero atp = 0.5(a Newton step there leaves a
rounding residual, which the ulp metric would read as 1e16 ulps against the exact 0.0 of torch and scipy)
and gives the same rows as before on the 600 point float64 and float32 grids. The survey also evaluates
the reference only when some requested backend implements the function; a numpy only run no longer
spends its time on references for the torch and scipy only entries. The pytest job on ubuntu, macOS and
windows (Python 3.12 and 3.9) runstests/test_survey.pynow, with numpy as the only backend.ulpwise surveyprinted a numpyRuntimeWarningfor every backend call that divided by zero or
overflowed at the edge values of the grid (reciprocalandlogat 0,reciprocalat the overflow
edge), hundreds of lines on a full run. Those points are counted in thenonfinitecolumn already, so
the survey now evaluates the backends undernp.errstate(all="ignore"), and a test runsreciprocaland
logwith warnings turned into errors.- The README said that two kornia fixes in the corpus were merged but not released; the corpus has 31
kornia cases now, and the pytorch/rl, peft and pytorch (#198006) fixes are in the same state (kornia
0.8.3 and 0.9.0rc1, torchrl 0.14.0, peft 0.21.2 and torch 2.14.1 predate them or cherry-pick other
changes), while the timm and ultralytics fixes are released. The sentence says so now, and
tests/test_corpus.pychecks that the README table has one row per case with the merge date from
cases.json, so a case added without its row, or a row with a stale date, fails the suite. tests/test_survey.pychecks every reference of the survey registry against the libraries it measures:
each entry is surveyed on a 24 point float64 grid against torch, numpy and scipy and its median error must
stay under 32 ulp, which a reference that is another function fails by fifteen orders of magnitude (torch's
polygamma_1is the worst true median at 9 ulp), and the piecewise references (selu,elu,entr,
gelu_tanh,log_ndtr,lgamma,digamma,erfinv,ndtri,zeta,sinc,spherical_bessel_j0)
are pinned once per branch, since a slip in one branch moves a third of the grid and keeps the median.
survey.pygoes from 77 to 94 percent covered.- With torch installed but
expecttestmissing,torch.testing._internaldoes not import and
ulpwise surveysilently used the dtype default as theOpInfotolerance, so theop tolcolumns
looked like an override that was never read. The survey now logs one line with the import error and
thesurveyextra installsexpecttest. The regression corpus CI job, which has torch but had no
expecttest, failed on the twoop_dbtests for the same reason; it installsexpecttestnow and the
tests skip with the reason whereop_dbis not importable. ulpwise surveylooked up torch'sOpInfotolerance by op name and took the firstop_dbentry
with that name.polygammahas one entry per order, and the first,polygamma_n_0, has no
override, so theop failcolumn ofpolygamma_1andpolygamma_2in float32 was computed with
the dtype default (rtol 1.3e-6,atol 1e-5) instead of torch'spolygamma_n_1and_n_2
override (rtol 0.01,atol 1e-4).Entrygainsopinfo_variant,torch_opinfo_tolerance
takes avariantand otherwise prefers the base variant, and the two registry entries name
theirs. Instudies/accuracy-survey-2026-09thepolygamma_1f32 row goes from 86 to 7 inputs
failing the op's tolerance; the measured errors are unchanged (erratum in its README).tests/test_survey.pycovers the survey itself now:survey()onsqrtagainst numpy and torch
(numpy within half an ulp, torch under one), an inexact backend, a backend that raises or returns
the wrong shape, the backend resolution, theop_dbtolerance lookup including the variant case,
the csv and markdown writers,versions(),main()and thesurveysubcommand. CI installs
mpmathfor the Python job so these tests run there instead of being skipped.- Six more kornia cases, each present on kornia 0.8.3 and fixed on main: the Hessian of
So3.exp
at the identity (#4972, nine nan for-I / 4and zeros),sampson_epipolar_distanceof a point on
its epiline withsquared=False(#5116,sqrt(eps)= 1e-4 for 0) and of the sameFscaled by
1e-4 (#5116, a third lower),RandomHueon a float64 image (#5131, 8.7e-8 rad past a half turn,
the float32 rounding error of pi),MS_SSIMLoss(sigmas=(0.5, 1.3), reduction="none")(#5143, an
even 6-pixel window and a(1, 15, 19)map for a(1, 3, 16, 20)input) andMS_SSIMLosson a
uint8 pair (#5353,expected scalar type Byte but found Float; withdata_range=255it now scores
the pair divided by 255 at the default to 1e-6). Six more:So3.right_jacobianof a float16
45 rad rotation (#4967, the identity for a matrix of 0.2 to 0.4),average_quaternionswith a member
stored as3 q(#4980, 41.8 degrees for the 22.5 degree bisector),Hyperplane.througha float16
triangle with legs of 300 (#5104, the normal(0, 0, 1)for(0, 0, -1)),filter2dwith a
per-sample kernel on a channels-last image (#5301, torch's view error),lovasz_hinge_lossof a
float16 prediction (#5303, a float32 loss) andget_box_kernel1d(#5357, a stride-0 view where
one write zeroed all three taps). The kornia block is 31 cases, allpresenton 0.8.3 and all
fixedon main. One more ultralytics case,scale_maskswith the dataloader'sratio_padand an
odd letterbox padding (#26379, a padded row survived the crop and the bottom rows of every mask
faded to 0) andverify_image_labelon two triangles that tile one square (#26377, the second
polygon was dropped as a duplicate of the first because only the class and the box were compared);
both carry `f...
ulpwise 0.3.0
ulpwise scan: static scan of a repository (a directory, a GitHub URL orowner/repo, cloned
with depth 1) for the floating point patterns behind the corpus bugs. Twelve rules with severity,
reason, replacement and upstream example:exp-of-square,sin-of-pi-times,softplus-by-hand,
logsumexp-by-hand,hypot-by-hand,sqrt-of-difference,one-minus-cos,log1p-by-hand,
expm1-by-hand,atan-of-quotient,small-angle-division,acos-for-angle. Findings carry
file, line, function and the source line; the report ends with the functions that do the most
elementary math.--reportwrites Markdown,--rulesfilters,--fail-ongates CI,
--include-testswidens the walk. On kornia main the foursmall-angle-divisionand
one-minus-coslines are the ones kornia #4897 fixes.ulpwise scan --run: imports the math-heavy module level functions and calls them on the same
grid in float32 and float64, reporting the largest error in ulps of the largest output and the
worst elementwise ulp distance with its input. Static methods run, instance methods and other
skips carry their reason.--run-limitbounds it,--run-installedimports the installed package
instead of the scanned tree.ulp_distance,ulp_distances,ordered,max_ulpandassert_max_ulptakef16and
bf16, andflattenreads the dtype off float16 and bfloat16 numpy arrays and torch tensors.
Inputs that are not representable are rounded to nearest even first, so a bfloat16 tensor can be
measured against a float64 reference; without an explicit dtype the less precise of the two
inputs decides. Cross-checked against the numpy int16 view for float16 and the torch view for
bfloat16.special("f16")andspecial("bf16"): the same 29 named edge values as f32 and f64, so
edge_values, theedge_f16andedge_bf16pytest fixtures andulpwise special f16work.
Every value satisfies the same checks as the f32 and f64 tables, run through numpy for float16
and torch for bfloat16.ulpwise corpus: runs the regression corpus against the installed packages without pytest and
prints one line per case, present, fixed or skipped, with the installed version and the upstream
reference.--repofilters by repository or case id,--fail-if-presentmakes a present bug exit 1.
Any exception from a repro counts as present, like the pytest run: several corpus bugs are crashes.