Skip to main content

ulpwise

Numerical conformance testing for ML code. A Rust core with a Python API and a pytest plugin.

ulpwise answers three questions that come up every time a numerical test goes red on one platform and green on another:

  1. Which inputs break first? Named edge values computed from the float format (the largest x with x * x == 0, the first x whose square overflows, the value where 1 / x overflows, binade boundaries, subnormals) and every-float enumerations of a range.
  2. What is the right answer, exactly? Rounding oracles that compare the exact result against the candidate floats with integer arithmetic on significands, so the correctly rounded sqrt, reciprocal and quotient, and the distance from the exact result to the rounding midpoint, do not depend on any libm.
  3. Where do two implementations disagree? Knife-edge scans: the inputs whose exact result sits within tol ulp of a rounding midpoint, so that two implementations that differ by one ulp return different floats. 8.4 million f32 inputs are scanned in about 0.3 s.

Why this exists

On 2026-09-23 a kornia pull request went red on 21 of 39 CI jobs after a maintainer added a test around the shape (1.0, 0.9235056042671204, 0.8528626561164856). In float32 the exact value of sqrt(0.8528626561164856) lies 0.0004 ulp below the midpoint of its two float32 neighbours. The macOS runners and my sandbox rounded it down, the ubuntu and windows runners rounded it up, and the estimator under test took two different branches. ulpwise finds that input, and the 16,995 others like it in [0.5, 1), before CI does:

$ ulpwise midpoint sqrt 0.8528626561164856 --dtype f32
rounded 0.9235056042671204, exact result lies above it, 3.771e-04 ulp from the rounding midpoint

$ ulpwise knife sqrt --lo 0.85 --hi 0.86 --tol 1e-3 --limit 3
                       x                  rounded  midpoint distance (ulp) exact lies
      0.8500027060508728       0.9219558835029602                2.521e-04      above
      0.8500217199325562       0.9219662547111511                5.452e-04      below
      0.8500407338142395       0.9219765067100525                5.924e-04      above
3 knife edge(s) with distance < 0.001 ulp

Measured with examples/torch_sqrt_conformance.py on one Linux x86_64 machine (AVX512, torch 2.14.0+cpu built with MKL, numpy 2.2.6), at the 16,996 float32 knife edges of [0.5, 1) with tol = 1e-3:

implementation off by 1 ulp at knife edges direction
numpy.sqrt float32 0 / 16,996 correctly rounded
torch.sqrt float32 7,131 / 16,996 (42.0%) always rounded down instead of up
torch.pow(x, 0.5) float32 7,131 / 16,996 (42.0%) same
torch.sqrt float64 then cast 0 / 16,996 correctly rounded
torch.sqrt float64 (20,000 f64 knife edges) 9,996 / 20,000 (50.0%) always down

On random inputs the same torch.sqrt differs from correct rounding at 0.70% of float32 values, which is why this goes unnoticed until a test happens to pin one of them. Other platforms will show other numbers; the CI of this repository prints them for ubuntu, macOS and windows on every run.

Install

pip install ulpwise          # or: uv add ulpwise
uvx ulpwise special f32      # run the command line tool without installing anything

Wheels on PyPI cover Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows x64, for Python 3.9 or newer (abi3). To build from source you need a Rust toolchain (1.86 or newer) and maturin:

pip install maturin
pip install .            # or: maturin develop --release  (inside a virtualenv)

The Rust crate is usable on its own (cargo add --git https://github.com/Nicholas022400701/ulpwise).

Use

import ulpwise

# ulp distances, dtype aware: a float32 tensor is measured in float32 ulps.
ulpwise.ulp_distance(0.9235056042671204, 0.9235056638717651, "f32")   # 1
ulpwise.assert_max_ulp(torch_out, reference, max_ulp_=2)               # floats, lists, numpy, torch

# the 29 named edge values of a dtype
dict(ulpwise.special("f32"))["square_underflows_to_zero"]              # 2.6469779601696886e-23, the largest x with x * x == 0

# exact placement of a result relative to its rounding midpoint
ulpwise.midpoint("sqrt", 0.8528626561164856, "f32")   # (0.9235056042671204, 0.000377, True, False)
ulpwise.midpoint("div", 1.0, "f64", 3.0)              # (0.3333333333333333, 0.1666..., True, False)

# knife edges of a function on a range: (x, rounded, distance_ulp, exact_above)
ulpwise.knife_edges("exp", 0.5, 1.0, tol_ulp=1e-3, dtype="f32", limit=100)

# a correctly rounded sqrt that does not depend on the platform
ulpwise.sqrt_cr(0.8528626561164856, "f32")            # 0.9235056042671204

midpoint and knife_edges accept sqrt, recip and div (exact integer oracle, f32 and f64) and rsqrt exp exp2 expm1 log log2 log10 log1p sin cos tan atan tanh sigmoid softplus (f32 only, f64 libm as reference, trust the distance down to about 1e-8 ulp).

pytest plugin

Installed automatically. A test that takes edge_f32 or edge_f64 runs once per named edge value, and assert_max_ulp is available as a fixture:

def test_my_kernel_survives_the_edges(edge_f32, assert_max_ulp):
    x = torch.tensor([edge_f32])
    assert_max_ulp(my_kernel(x), reference(x), max_ulp_=1)

Failures read test_my_kernel_survives_the_edges[square_overflows].

Regression corpus

python/ulpwise/corpus/cases.json holds upstream bugs found by the contribution pipeline this project grew out of, each with a runnable repro, the buggy behaviour quoted from the merged pull request, and the check that tells the two apart. pytest tests/test_corpus.py runs every case whose packages are installed; a case is an expected failure while the installed release is not known to contain the fix, so the run tells you which bugs are present in your environment.

case kind merged
kornia #4683 second derivative sign in spatial_gradient(order=2) sign 2026-09-22
kornia #4767 mixed second order kernel scale, wrong hessian_response determinant scale 2026-09-23
pytorch #198006 Multinomial.entropy() evaluated in the default dtype dtype 2026-09-23
pytorch/rl #4443 arange(0, 1, 1/n) gives n + 1 positions for 140 values of n below 2000 rounding 2026-09-20
pytorch/rl #4444 min_value or -inf drops min_value=0 falsy zero 2026-09-20
pytorch/rl #4445 scheduler state_dict() contained a module object crash 2026-09-20
timm #2786 Mars kept a reference to p.grad as the previous gradient aliasing 2026-09-17
timm #2790 AdafactorBigVision clipped updates in the wrong direction direction 2026-09-18
timm #2791 AdaMuon conv LR scale computed from the wrong dims scale 2026-09-18
timm #2792 Kron __setstate__ shadowed crash 2026-09-18
peft #3777 pointwise Conv3d took the conv2d 1x1 shortcut shape 2026-09-21

Two ultralytics fixes (#26240, #26246) are not in the corpus yet because their repros need model weights and a dataset layout; they will come with fixtures.

How the exact oracle works

For sqrt(x) the candidate r and the midpoint m between r and its neighbour are written as integers times a power of two, m^2 is formed in u128 (at most 110 bits) and compared with x after aligning exponents. The sign says on which side of m the exact root lies, and |x - m^2| / (sqrt(x) + m) is the distance to the midpoint. If the hardware result turns out to be on the wrong side of a midpoint it is stepped one ulp toward the exact value and checked again, so sqrt_cr is correctly rounded even where the platform sqrt is not. Division uses the same machinery with m * b against a, and there the residual is exact. tests/test_core.py cross-checks both against fractions.Fraction and decimal.Decimal at 80 digits.

Roadmap

  • Mutation scoring for numerical tests: single token mutants of the code under test (abs, a dropped sqrt, / 4 for / 16) run against the test suite, reporting which survive.
  • float16 and bfloat16 ulps and edge values.
  • Exact references for transcendental functions (correctly rounded exp, log, ...) so the f64 ones can be scanned too.
  • Zero copy paths for numpy arrays and torch tensors.
  • More corpus entries, with fixtures for the cases that need data.

AI disclosure

This project is written with an AI coding agent (Claude) working for 区梓灏 (@Nicholas022400701), who owns the repository and reviews what is published. The same pipeline produced the upstream fixes in the corpus; each of those pull requests carries the same disclosure.

License

Licensed under either of

at your option. "At your option" means that whoever uses or redistributes this code picks whichever of the two licenses they want to comply with. Nobody has to ask anyone. Unless you say otherwise, a contribution you send is dual licensed the same way, without extra terms.

Release files for ulpwise 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ulpwise 0.1.2
File Size Uploaded
ulpwise-0.1.2.tar.gz 43.1 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for ulpwise 0.1.2
File
ulpwise-0.1.2-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
ulpwise-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
ulpwise-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
ulpwise-0.1.2-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
ulpwise-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.2 MB

Release files / ulpwise-0.1.2.tar.gz

Download URL ulpwise-0.1.2.tar.gz
Size 43.1 kB
Tags Source
SHA-256 checksum
How to use checksums
37e3f978d781171e1858a010ecaba30435494c8afb6ddf8c073fb7136e19ef58
BLAKE2b-256 checksum
How to use checksums
8e70cddd6b8b42cb6581a7142853cf9cd3b9cd102dda8acfb953d9be9cf77f75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ulpwise-0.1.2-cp39-abi3-win_amd64.whl

Download URL ulpwise-0.1.2-cp39-abi3-win_amd64.whl
Size 158.3 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
83fcc65cfb7994d54d7b27c63956bace29fa5f1afa5724ae3a95a262d7ffb9d6
BLAKE2b-256 checksum
How to use checksums
9075dac474235076afc1847bcf18af3ce909632fe3fe706d05f905bde6f46dc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ulpwise-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ulpwise-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 266.4 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
6fd453491e0e03cccbe68bc066fdd4ba7b1a15b54f509cea5b071061488e21a5
BLAKE2b-256 checksum
How to use checksums
262f7a315123b97dfbf00e65ab91ffc7f10e1f942c450c9f7d0fd47daeb2f761
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ulpwise-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL ulpwise-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 264.4 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
c837834c08d6a78a74f92ddf17ae1619ec14d6c4ad8fedfbfd3578fb20586a99
BLAKE2b-256 checksum
How to use checksums
2afe22ffc06a446c2dbba4a1b6736a14efd731a9b779e883f39312fc419f0042
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ulpwise-0.1.2-cp39-abi3-macosx_11_0_arm64.whl

Download URL ulpwise-0.1.2-cp39-abi3-macosx_11_0_arm64.whl
Size 246.7 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
21a118b01fae441bed02e74df4f6057124b1143199b0c75a0f206945448d1aeb
BLAKE2b-256 checksum
How to use checksums
087a8538f48e8b4664c1c164e2408b12cd266b53f291e8ef6a96c8e2d3801b8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / ulpwise-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl

Download URL ulpwise-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl
Size 256.2 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
b8e848ba24873d66147973d08000c7a6d0c94783d8880c3b3d6554ca07e25094
BLAKE2b-256 checksum
How to use checksums
07a00d1b73f7ca30cce522753af52afb67665292a0375fc700397d96a7db1664
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

6 release files

0.2.0

6 release files

This release

0.1.2 This release

6 release files

0.1.1

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page