ulpwise 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ulpwise-0.3.1/.github/dependabot.yml +23 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/.github/workflows/ci.yml +16 -9
- {ulpwise-0.3.0 → ulpwise-0.3.1}/.github/workflows/release.yml +31 -10
- {ulpwise-0.3.0 → ulpwise-0.3.1}/.gitignore +1 -0
- ulpwise-0.3.1/CHANGELOG.md +222 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/Cargo.lock +11 -11
- {ulpwise-0.3.0 → ulpwise-0.3.1}/Cargo.toml +1 -1
- {ulpwise-0.3.0 → ulpwise-0.3.1}/PKG-INFO +64 -13
- {ulpwise-0.3.0 → ulpwise-0.3.1}/README.md +62 -12
- {ulpwise-0.3.0 → ulpwise-0.3.1}/pyproject.toml +3 -2
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/__init__.py +115 -8
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/__main__.py +1 -1
- ulpwise-0.3.1/python/ulpwise/corpus/cases.json +1024 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/scan.py +20 -7
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/survey.py +152 -49
- {ulpwise-0.3.0 → ulpwise-0.3.1}/studies/accuracy-survey-2026-09/README.md +13 -1
- {ulpwise-0.3.0 → ulpwise-0.3.1}/studies/accuracy-survey-2026-09/results.csv +2 -2
- {ulpwise-0.3.0 → ulpwise-0.3.1}/studies/accuracy-survey-2026-09/results.md +2 -2
- {ulpwise-0.3.0 → ulpwise-0.3.1}/tests/test_core.py +64 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/tests/test_corpus.py +23 -4
- {ulpwise-0.3.0 → ulpwise-0.3.1}/tests/test_scan.py +71 -0
- ulpwise-0.3.1/tests/test_survey.py +681 -0
- ulpwise-0.3.0/CHANGELOG.md +0 -80
- ulpwise-0.3.0/python/ulpwise/corpus/cases.json +0 -437
- ulpwise-0.3.0/tests/test_survey.py +0 -53
- {ulpwise-0.3.0 → ulpwise-0.3.1}/CONTRIBUTING.md +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/LICENSE-APACHE +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/LICENSE-MIT +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/examples/sqrt_knife_values.py +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/examples/torch_sqrt_conformance.py +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/corpus/__init__.py +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/python/ulpwise/plugin.py +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/edge.rs +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/exact.rs +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/knife.rs +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/lib.rs +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/py.rs +0 -0
- {ulpwise-0.3.0 → ulpwise-0.3.1}/src/ulp.rs +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dependabot keeps Cargo.lock and the workflow actions current through pull requests that CI
|
|
2
|
+
# tests, so a pyo3 patch release (0.29.2 -> 0.29.3 at the time of writing) is picked up without
|
|
3
|
+
# anyone running cargo by hand. Python dependencies stay unpinned in pyproject.toml on purpose:
|
|
4
|
+
# the corpus is meant to run against whatever torch, kornia, timm, peft, torchrl and
|
|
5
|
+
# ultralytics the user has installed.
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: cargo
|
|
9
|
+
directory: /
|
|
10
|
+
schedule:
|
|
11
|
+
interval: weekly
|
|
12
|
+
day: monday
|
|
13
|
+
open-pull-requests-limit: 3
|
|
14
|
+
commit-message:
|
|
15
|
+
prefix: "deps(cargo)"
|
|
16
|
+
- package-ecosystem: github-actions
|
|
17
|
+
directory: /
|
|
18
|
+
schedule:
|
|
19
|
+
interval: weekly
|
|
20
|
+
day: monday
|
|
21
|
+
open-pull-requests-limit: 3
|
|
22
|
+
commit-message:
|
|
23
|
+
prefix: "deps(actions)"
|
|
@@ -4,6 +4,9 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: [main]
|
|
6
6
|
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "17 6 * * 1" # Monday 06:17 UTC: the corpus against the latest releases, strict
|
|
9
|
+
workflow_dispatch:
|
|
7
10
|
|
|
8
11
|
jobs:
|
|
9
12
|
rust:
|
|
@@ -14,7 +17,7 @@ jobs:
|
|
|
14
17
|
matrix:
|
|
15
18
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
19
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
20
|
+
- uses: actions/checkout@v7
|
|
18
21
|
- uses: dtolnay/rust-toolchain@stable
|
|
19
22
|
with:
|
|
20
23
|
components: rustfmt, clippy
|
|
@@ -34,29 +37,33 @@ jobs:
|
|
|
34
37
|
- os: ubuntu-latest
|
|
35
38
|
python: "3.9"
|
|
36
39
|
steps:
|
|
37
|
-
- uses: actions/checkout@
|
|
40
|
+
- uses: actions/checkout@v7
|
|
38
41
|
- uses: dtolnay/rust-toolchain@stable
|
|
39
|
-
- uses: actions/setup-python@
|
|
42
|
+
- uses: actions/setup-python@v7
|
|
40
43
|
with:
|
|
41
44
|
python-version: ${{ matrix.python }}
|
|
42
|
-
- run: pip install pytest numpy packaging
|
|
45
|
+
- run: pip install pytest numpy packaging mpmath
|
|
43
46
|
- run: pip install .
|
|
44
|
-
- run: pytest tests/test_core.py -q -p no:cacheprovider
|
|
47
|
+
- run: pytest tests/test_core.py tests/test_survey.py tests/test_scan.py -q -p no:cacheprovider
|
|
45
48
|
- name: sqrt conformance of numpy on this platform
|
|
46
49
|
run: python examples/torch_sqrt_conformance.py && python examples/sqrt_knife_values.py
|
|
47
50
|
|
|
48
51
|
corpus:
|
|
49
52
|
name: regression corpus (ubuntu, torch cpu)
|
|
50
53
|
runs-on: ubuntu-latest
|
|
54
|
+
env:
|
|
55
|
+
# On the weekly and the manual runs an unexpected pass is a failure: a release fixed a case,
|
|
56
|
+
# update cases.json. Push and pull request runs stay lenient.
|
|
57
|
+
ULPWISE_CORPUS_STRICT: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '1' || '0' }}
|
|
51
58
|
steps:
|
|
52
|
-
- uses: actions/checkout@
|
|
59
|
+
- uses: actions/checkout@v7
|
|
53
60
|
- uses: dtolnay/rust-toolchain@stable
|
|
54
|
-
- uses: actions/setup-python@
|
|
61
|
+
- uses: actions/setup-python@v7
|
|
55
62
|
with:
|
|
56
63
|
python-version: "3.12"
|
|
57
|
-
- run: pip install pytest numpy packaging scipy
|
|
64
|
+
- run: pip install pytest numpy packaging scipy mpmath expecttest
|
|
58
65
|
- run: pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
|
|
59
|
-
- run: pip install kornia timm peft torchrl
|
|
66
|
+
- run: pip install kornia timm peft torchrl ultralytics
|
|
60
67
|
- run: pip install .
|
|
61
68
|
- run: pytest tests -q -p no:cacheprovider -rxXs
|
|
62
69
|
- name: sqrt conformance of torch and numpy on this platform
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Builds wheels for every supported platform plus an sdist. On a tag push (v*) it also
|
|
2
2
|
# uploads them to PyPI through trusted publishing (no API token stored anywhere):
|
|
3
3
|
# the PyPI project "ulpwise" must list this repository, this workflow file name and the
|
|
4
|
-
# "pypi" environment as its publisher. A manual run only builds and checks the wheels
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# "pypi" environment as its publisher. A manual run only builds and checks the wheels and
|
|
5
|
+
# gathers them the way the publish job would (the "collect" job), unless its "publish" input
|
|
6
|
+
# is set, in which case it instead uploads, tags the commit and creates the GitHub release
|
|
7
|
+
# with the CHANGELOG section as notes and the wheels attached.
|
|
7
8
|
name: release
|
|
8
9
|
|
|
9
10
|
on:
|
|
@@ -48,8 +49,8 @@ jobs:
|
|
|
48
49
|
target: x64
|
|
49
50
|
smoke: true
|
|
50
51
|
steps:
|
|
51
|
-
- uses: actions/checkout@
|
|
52
|
-
- uses: actions/setup-python@
|
|
52
|
+
- uses: actions/checkout@v7
|
|
53
|
+
- uses: actions/setup-python@v7
|
|
53
54
|
with:
|
|
54
55
|
python-version: "3.12"
|
|
55
56
|
- uses: PyO3/maturin-action@v1
|
|
@@ -66,7 +67,7 @@ jobs:
|
|
|
66
67
|
python -c "import ulpwise; print('ulpwise', ulpwise.__version__)"
|
|
67
68
|
python -m ulpwise ulp 1.0 1.0000001 --dtype f32
|
|
68
69
|
python -m ulpwise midpoint sqrt 0.8528626561164856 --dtype f32
|
|
69
|
-
- uses: actions/upload-artifact@
|
|
70
|
+
- uses: actions/upload-artifact@v7
|
|
70
71
|
with:
|
|
71
72
|
name: wheels-${{ matrix.name }}
|
|
72
73
|
path: dist
|
|
@@ -74,7 +75,7 @@ jobs:
|
|
|
74
75
|
sdist:
|
|
75
76
|
runs-on: ubuntu-latest
|
|
76
77
|
steps:
|
|
77
|
-
- uses: actions/checkout@
|
|
78
|
+
- uses: actions/checkout@v7
|
|
78
79
|
- uses: PyO3/maturin-action@v1
|
|
79
80
|
with:
|
|
80
81
|
command: sdist
|
|
@@ -84,11 +85,31 @@ jobs:
|
|
|
84
85
|
pip install pytest numpy
|
|
85
86
|
pip install dist/*.tar.gz
|
|
86
87
|
pytest tests/test_core.py -q -p no:cacheprovider
|
|
87
|
-
- uses: actions/upload-artifact@
|
|
88
|
+
- uses: actions/upload-artifact@v7
|
|
88
89
|
with:
|
|
89
90
|
name: wheels-sdist
|
|
90
91
|
path: dist
|
|
91
92
|
|
|
93
|
+
# A manual run without "publish" ends here: gather the artifacts exactly the way the publish
|
|
94
|
+
# job does and count them, so a dry run exercises the whole pipeline short of the upload.
|
|
95
|
+
collect:
|
|
96
|
+
if: ${{ !(startsWith(github.ref, 'refs/tags/v') || inputs.publish == true) }}
|
|
97
|
+
needs: [wheels, sdist]
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/download-artifact@v8
|
|
101
|
+
with:
|
|
102
|
+
pattern: wheels-*
|
|
103
|
+
merge-multiple: true
|
|
104
|
+
path: dist
|
|
105
|
+
- name: one abi3 wheel per matrix entry plus the sdist
|
|
106
|
+
run: |
|
|
107
|
+
ls -l dist
|
|
108
|
+
wheels=$(ls dist/*.whl | wc -l)
|
|
109
|
+
sdists=$(ls dist/*.tar.gz | wc -l)
|
|
110
|
+
echo "$wheels wheels, $sdists sdist"
|
|
111
|
+
test "$wheels" -eq 5 && test "$sdists" -eq 1
|
|
112
|
+
|
|
92
113
|
publish:
|
|
93
114
|
if: startsWith(github.ref, 'refs/tags/v') || inputs.publish == true
|
|
94
115
|
needs: [wheels, sdist]
|
|
@@ -98,8 +119,8 @@ jobs:
|
|
|
98
119
|
id-token: write
|
|
99
120
|
contents: write
|
|
100
121
|
steps:
|
|
101
|
-
- uses: actions/checkout@
|
|
102
|
-
- uses: actions/download-artifact@
|
|
122
|
+
- uses: actions/checkout@v7
|
|
123
|
+
- uses: actions/download-artifact@v8
|
|
103
124
|
with:
|
|
104
125
|
pattern: wheels-*
|
|
105
126
|
merge-multiple: true
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 (2026-10-05)
|
|
4
|
+
|
|
5
|
+
- `ulpwise survey` takes `f16` and `bf16` in `--dtypes` (the default stays `f32,f64`): torch and jax get half
|
|
6
|
+
precision rows, measured in float16 or bfloat16 ulps against the same mpmath reference; numpy has no
|
|
7
|
+
bfloat16 and scipy.special computes a float16 input in float32, so those two have no `bf16` rows and scipy
|
|
8
|
+
has no `f16` rows. A backend without a kernel for a function in a dtype (torch's Bessel and Airy functions,
|
|
9
|
+
`erfcx`, `ndtri`, `log_ndtr` and `zeta` in half precision) is logged and skipped before the reference is
|
|
10
|
+
computed. The torch default tolerances of the two dtypes are `(1.6e-2, 1e-5)` and `(1e-3, 1e-5)`, as in
|
|
11
|
+
`torch.testing`. A log spaced domain wider than the dtype is now clipped to the dtype's finite range before
|
|
12
|
+
the points are spaced: the float16 grid of `sqrt` and `log` over `(1e-300, 1e300)` kept 32 of 600 points and
|
|
13
|
+
`acosh` 24, they have about 570 and 490 now; the float32 grids of the same functions go from about 100 points
|
|
14
|
+
to about 600, the float64 grids are unchanged. The `(0, 1)` and `(-1, 1)` grids of `ndtri`, `logit`, `asin`,
|
|
15
|
+
`acos`, `atanh` and `erfinv` stop at the float below 1 in half precision too, where `1 - 1e-4` and `1 - 1e-3`
|
|
16
|
+
rounded to 1. Two corrections to the ulp metric for every dtype: the threshold from which an exact result
|
|
17
|
+
counts as a correct overflow to infinity was `fmax * (1 + eps / 2)`, about one ulp above the largest finite
|
|
18
|
+
value, and is now `fmax + ulp / 2`, the rounding threshold; and the spacing at the largest finite value was
|
|
19
|
+
numpy's `inf`, which turned every finite result there into a 0 ulp error, it is `ulpwise.spacing` now.
|
|
20
|
+
An unknown dtype raises `ValueError` instead of a `KeyError` from the grid.
|
|
21
|
+
- `next_up`, `next_down`, `spacing`, `neighbours`, `binade_edges` and `all_floats` accept `f16` and `bf16` (and the
|
|
22
|
+
`float16`, `half`, `bfloat16`, `torch.float16`, `torch.bfloat16` aliases) like `ulp_distance` and `special` already
|
|
23
|
+
did; they raised `unsupported dtype` before. The 16 bit versions are pure Python on the bit patterns, with the
|
|
24
|
+
conventions of the Rust ones: the largest float steps to infinity, both zeros to the smallest subnormal, `spacing`
|
|
25
|
+
is the distance from `|x|` to the next float and stays finite at the largest float, `neighbours` returns finite
|
|
26
|
+
values only. A test walks every finite float16 and bfloat16 and checks the six functions against each other, against
|
|
27
|
+
numpy's float16 `nextafter` and `spacing` and against torch's bfloat16 `nextafter`.
|
|
28
|
+
- `ulpwise scan` read the first positional argument of a method call as the value the method is applied
|
|
29
|
+
to, so `log(exp(x).sum(-1))` was not a `logsumexp-by-hand` finding while `log(exp(x).sum())` and
|
|
30
|
+
`log(exp(x).sum(dim=-1))` were. A method call whose first argument is a dimension (an integer, `None`
|
|
31
|
+
or a tuple of them) is now read as applying to its receiver, like the argument-less form.
|
|
32
|
+
- `ulpwise scan` printed a `SyntaxWarning` (a `DeprecationWarning` before Python 3.12) for every invalid
|
|
33
|
+
escape sequence in the files it scanned, four lines on kornia. The parse now ignores those warnings;
|
|
34
|
+
a file that does not parse is skipped as before.
|
|
35
|
+
- `ulpwise scan` reported Windows paths with backslashes (`kornia\geometry\conversions.py`), so the same
|
|
36
|
+
report read differently from the one made on Linux or macOS. Paths are now written with forward
|
|
37
|
+
slashes on every platform; `--run` still imports the module from either form. The CI pytest job on
|
|
38
|
+
ubuntu, macOS and windows runs `tests/test_scan.py` now, and that file also covers the reasons
|
|
39
|
+
`--run` gives for a function it does not run (a class, a constant, an instance method, a builtin
|
|
40
|
+
without a signature, a nested name, a module that fails to import) and the ulp comparison of the
|
|
41
|
+
results across infinities, NaNs and an all zero reference.
|
|
42
|
+
- `ulpwise survey` without scipy installed died at `ndtri` with a `ModuleNotFoundError` and lost every
|
|
43
|
+
row computed before it: the reference started its Newton iteration from `scipy.special.ndtri`. It now
|
|
44
|
+
starts from a bisection on `math.erfc`, returns an exact zero at `p = 0.5` (a Newton step there leaves a
|
|
45
|
+
rounding residual, which the ulp metric would read as 1e16 ulps against the exact 0.0 of torch and scipy)
|
|
46
|
+
and gives the same rows as before on the 600 point float64 and float32 grids. The survey also evaluates
|
|
47
|
+
the reference only when some requested backend implements the function; a numpy only run no longer
|
|
48
|
+
spends its time on references for the torch and scipy only entries. The pytest job on ubuntu, macOS and
|
|
49
|
+
windows (Python 3.12 and 3.9) runs `tests/test_survey.py` now, with numpy as the only backend.
|
|
50
|
+
- `ulpwise survey` printed a numpy `RuntimeWarning` for every backend call that divided by zero or
|
|
51
|
+
overflowed at the edge values of the grid (`reciprocal` and `log` at 0, `reciprocal` at the overflow
|
|
52
|
+
edge), hundreds of lines on a full run. Those points are counted in the `nonfinite` column already, so
|
|
53
|
+
the survey now evaluates the backends under `np.errstate(all="ignore")`, and a test runs `reciprocal` and
|
|
54
|
+
`log` with warnings turned into errors.
|
|
55
|
+
- The README said that two kornia fixes in the corpus were merged but not released; the corpus has 31
|
|
56
|
+
kornia cases now, and the pytorch/rl, peft and pytorch (#198006) fixes are in the same state (kornia
|
|
57
|
+
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
|
|
58
|
+
changes), while the timm and ultralytics fixes are released. The sentence says so now, and
|
|
59
|
+
`tests/test_corpus.py` checks that the README table has one row per case with the merge date from
|
|
60
|
+
`cases.json`, so a case added without its row, or a row with a stale date, fails the suite.
|
|
61
|
+
- `tests/test_survey.py` checks every reference of the survey registry against the libraries it measures:
|
|
62
|
+
each entry is surveyed on a 24 point float64 grid against torch, numpy and scipy and its median error must
|
|
63
|
+
stay under 32 ulp, which a reference that is another function fails by fifteen orders of magnitude (torch's
|
|
64
|
+
`polygamma_1` is the worst true median at 9 ulp), and the piecewise references (`selu`, `elu`, `entr`,
|
|
65
|
+
`gelu_tanh`, `log_ndtr`, `lgamma`, `digamma`, `erfinv`, `ndtri`, `zeta`, `sinc`, `spherical_bessel_j0`)
|
|
66
|
+
are pinned once per branch, since a slip in one branch moves a third of the grid and keeps the median.
|
|
67
|
+
`survey.py` goes from 77 to 94 percent covered.
|
|
68
|
+
- With torch installed but `expecttest` missing, `torch.testing._internal` does not import and
|
|
69
|
+
`ulpwise survey` silently used the dtype default as the `OpInfo` tolerance, so the `op tol` columns
|
|
70
|
+
looked like an override that was never read. The survey now logs one line with the import error and
|
|
71
|
+
the `survey` extra installs `expecttest`. The regression corpus CI job, which has torch but had no
|
|
72
|
+
`expecttest`, failed on the two `op_db` tests for the same reason; it installs `expecttest` now and the
|
|
73
|
+
tests skip with the reason where `op_db` is not importable.
|
|
74
|
+
- `ulpwise survey` looked up torch's `OpInfo` tolerance by op name and took the first `op_db` entry
|
|
75
|
+
with that name. `polygamma` has one entry per order, and the first, `polygamma_n_0`, has no
|
|
76
|
+
override, so the `op fail` column of `polygamma_1` and `polygamma_2` in float32 was computed with
|
|
77
|
+
the dtype default (`rtol 1.3e-6`, `atol 1e-5`) instead of torch's `polygamma_n_1` and `_n_2`
|
|
78
|
+
override (`rtol 0.01`, `atol 1e-4`). `Entry` gains `opinfo_variant`, `torch_opinfo_tolerance`
|
|
79
|
+
takes a `variant` and otherwise prefers the base variant, and the two registry entries name
|
|
80
|
+
theirs. In `studies/accuracy-survey-2026-09` the `polygamma_1` f32 row goes from 86 to 7 inputs
|
|
81
|
+
failing the op's tolerance; the measured errors are unchanged (erratum in its README).
|
|
82
|
+
- `tests/test_survey.py` covers the survey itself now: `survey()` on `sqrt` against numpy and torch
|
|
83
|
+
(numpy within half an ulp, torch under one), an inexact backend, a backend that raises or returns
|
|
84
|
+
the wrong shape, the backend resolution, the `op_db` tolerance lookup including the variant case,
|
|
85
|
+
the csv and markdown writers, `versions()`, `main()` and the `survey` subcommand. CI installs
|
|
86
|
+
`mpmath` for the Python job so these tests run there instead of being skipped.
|
|
87
|
+
- Six more kornia cases, each present on kornia 0.8.3 and fixed on main: the Hessian of `So3.exp`
|
|
88
|
+
at the identity (#4972, nine nan for `-I / 4` and zeros), `sampson_epipolar_distance` of a point on
|
|
89
|
+
its epiline with `squared=False` (#5116, `sqrt(eps)` = 1e-4 for 0) and of the same `F` scaled by
|
|
90
|
+
1e-4 (#5116, a third lower), `RandomHue` on a float64 image (#5131, 8.7e-8 rad past a half turn,
|
|
91
|
+
the float32 rounding error of pi), `MS_SSIMLoss(sigmas=(0.5, 1.3), reduction="none")` (#5143, an
|
|
92
|
+
even 6-pixel window and a `(1, 15, 19)` map for a `(1, 3, 16, 20)` input) and `MS_SSIMLoss` on a
|
|
93
|
+
uint8 pair (#5353, `expected scalar type Byte but found Float`; with `data_range=255` it now scores
|
|
94
|
+
the pair divided by 255 at the default to 1e-6). Six more: `So3.right_jacobian` of a float16
|
|
95
|
+
45 rad rotation (#4967, the identity for a matrix of 0.2 to 0.4), `average_quaternions` with a member
|
|
96
|
+
stored as `3 q` (#4980, 41.8 degrees for the 22.5 degree bisector), `Hyperplane.through` a float16
|
|
97
|
+
triangle with legs of 300 (#5104, the normal `(0, 0, 1)` for `(0, 0, -1)`), `filter2d` with a
|
|
98
|
+
per-sample kernel on a channels-last image (#5301, torch's view error), `lovasz_hinge_loss` of a
|
|
99
|
+
float16 prediction (#5303, a float32 loss) and `get_box_kernel1d` (#5357, a stride-0 view where
|
|
100
|
+
one write zeroed all three taps). The kornia block is 31 cases, all `present` on 0.8.3 and all
|
|
101
|
+
`fixed` on main. One more ultralytics case, `scale_masks` with the dataloader's `ratio_pad` and an
|
|
102
|
+
odd letterbox padding (#26379, a padded row survived the crop and the bottom rows of every mask
|
|
103
|
+
faded to 0) and `verify_image_label` on two triangles that tile one square (#26377, the second
|
|
104
|
+
polygon was dropped as a duplicate of the first because only the class and the box were compared);
|
|
105
|
+
both carry `fixed_in_release` 8.4.165, the first tag with the fixes), and `verify_image_label` on a
|
|
106
|
+
pose row for a detect task (#26357, the row was read as a polygon, its box was out of bounds and
|
|
107
|
+
the image was dropped as corrupt; `fixed_in_release` 8.4.164). The corpus is 50 cases.
|
|
108
|
+
- The manual `workflow_dispatch` run of CI is now strict about the corpus like the Monday run (the
|
|
109
|
+
changelog said so already, the workflow set the variable for `schedule` only).
|
|
110
|
+
- The release workflow's manual dry run (`workflow_dispatch` without `publish`) now ends in a
|
|
111
|
+
`collect` job that downloads the artifacts the way the publish job does and checks that there
|
|
112
|
+
are five wheels and one sdist, so a dry run covers the whole pipeline short of the upload. The
|
|
113
|
+
workflow actions moved to `actions/checkout@v7`, `setup-python@v7`, `upload-artifact@v7` and
|
|
114
|
+
`download-artifact@v8` (the first Dependabot pull requests), and `Cargo.lock` to pyo3 0.29.3.
|
|
115
|
+
- Dependabot (`.github/dependabot.yml`) opens weekly pull requests for Cargo.lock and the
|
|
116
|
+
workflow actions, so pyo3 patch releases land through CI-tested pull requests instead of a
|
|
117
|
+
hand-run `cargo update`; Python dependencies stay unpinned on purpose.
|
|
118
|
+
- Eight more kornia cases, each present on kornia 0.8.3 and fixed on main: `Se2.exp`/`Se2.log` at
|
|
119
|
+
theta = 1e-8 (#4960, the translation came back as (1, 2) from exp and (1e-8, -5e-9) from log),
|
|
120
|
+
`point_line_distance` of a homogeneous point with w = 2 (#4975, 4.0 for 1.5), `Quaternion.__pow__`
|
|
121
|
+
of -1 (#5003, the zero quaternion), `So2` from a `(B, 1)` angle times `(B, 2)` points (#5005,
|
|
122
|
+
`(B, B, 2)`), `solve_cubic` of `1e-30 x^3 + 2x - 6` (#5024, roots `[0, 0, 0]` for 3),
|
|
123
|
+
`RgbToGrayscale` on uint8 (#5111, all zeros), `conv_soft_argmax2d` with a far-away peak (#5134,
|
|
124
|
+
the weak window's coordinates moved from 0.8834 to 1.0) and `get_gaussian_discrete_kernel1d(1,
|
|
125
|
+
sigma)` (#5376, 3 taps). Six more in the same shape: `Se3.exp`'s `d t / d omega` at the identity
|
|
126
|
+
(#4963, nan on 0.8.3, zero on main before the fix), `Quaternion.polar_angle`'s gradient at the
|
|
127
|
+
identity (#4981, nan), `decompose_essential_matrix` of a `(3, 3)` input (#4998, `(1, 3, 3)`),
|
|
128
|
+
`Hyperplane.through`'s gradients for orthogonal equal-length edges (#5058, nan),
|
|
129
|
+
`Vector3.normalized` of a float16 zero vector (#5084, NaN) and `otsu_threshold`'s mask below
|
|
130
|
+
zero (#5182, all False). The whole kornia block of the corpus, 19 cases, reads `present` on 0.8.3;
|
|
131
|
+
the mean_average_precision recall thresholds (#5101) were checked and left out because 0.8.3
|
|
132
|
+
already scores the exact-tenth case right.
|
|
133
|
+
- CI runs the regression corpus every Monday (and on `workflow_dispatch`) with
|
|
134
|
+
`ULPWISE_CORPUS_STRICT=1`, which turns an unexpected pass into a failure: a release fixed a case
|
|
135
|
+
and `fixed_in_release` is stale. Push and pull request runs stay non strict. The corpus job also
|
|
136
|
+
installs `ultralytics`, so the ultralytics case runs there instead of being skipped.
|
|
137
|
+
- Corpus metadata: kornia #4838 is fixed by kornia #5124 (merged 2026-09-30) and kornia #4897 by
|
|
138
|
+
kornia #4941 (merged 2026-09-26); both cases now carry the `pr` and `merged_at`, and stay expected
|
|
139
|
+
failures because no kornia release after 0.9.0rc1 (2026-07-19) exists yet. ultralytics #26330 sets
|
|
140
|
+
`fixed_in_release` to `8.4.164`: the tag `v8.4.164` (2026-09-27, the first release after the merge)
|
|
141
|
+
carries the `use_obb` check in `verify_labels` and `v8.4.163` does not, so the case is a hard
|
|
142
|
+
failure, not an expected one, from 8.4.164 on. peft #3777 keeps `fixed_in_release: null`: the
|
|
143
|
+
`v0.21.1` and `v0.21.2` tags still carry the `weight.size()[2:4]` shortcut in `lora/layer.py`.
|
|
144
|
+
|
|
145
|
+
## 0.3.0 (2026-09-26)
|
|
146
|
+
|
|
147
|
+
- `ulpwise scan`: static scan of a repository (a directory, a GitHub URL or `owner/repo`, cloned
|
|
148
|
+
with depth 1) for the floating point patterns behind the corpus bugs. Twelve rules with severity,
|
|
149
|
+
reason, replacement and upstream example: `exp-of-square`, `sin-of-pi-times`, `softplus-by-hand`,
|
|
150
|
+
`logsumexp-by-hand`, `hypot-by-hand`, `sqrt-of-difference`, `one-minus-cos`, `log1p-by-hand`,
|
|
151
|
+
`expm1-by-hand`, `atan-of-quotient`, `small-angle-division`, `acos-for-angle`. Findings carry
|
|
152
|
+
file, line, function and the source line; the report ends with the functions that do the most
|
|
153
|
+
elementary math. `--report` writes Markdown, `--rules` filters, `--fail-on` gates CI,
|
|
154
|
+
`--include-tests` widens the walk. On kornia main the four `small-angle-division` and
|
|
155
|
+
`one-minus-cos` lines are the ones kornia #4897 fixes.
|
|
156
|
+
- `ulpwise scan --run`: imports the math-heavy module level functions and calls them on the same
|
|
157
|
+
grid in float32 and float64, reporting the largest error in ulps of the largest output and the
|
|
158
|
+
worst elementwise ulp distance with its input. Static methods run, instance methods and other
|
|
159
|
+
skips carry their reason. `--run-limit` bounds it, `--run-installed` imports the installed package
|
|
160
|
+
instead of the scanned tree.
|
|
161
|
+
- `ulp_distance`, `ulp_distances`, `ordered`, `max_ulp` and `assert_max_ulp` take `f16` and
|
|
162
|
+
`bf16`, and `flatten` reads the dtype off float16 and bfloat16 numpy arrays and torch tensors.
|
|
163
|
+
Inputs that are not representable are rounded to nearest even first, so a bfloat16 tensor can be
|
|
164
|
+
measured against a float64 reference; without an explicit dtype the less precise of the two
|
|
165
|
+
inputs decides. Cross-checked against the numpy int16 view for float16 and the torch view for
|
|
166
|
+
bfloat16.
|
|
167
|
+
- `special("f16")` and `special("bf16")`: the same 29 named edge values as f32 and f64, so
|
|
168
|
+
`edge_values`, the `edge_f16` and `edge_bf16` pytest fixtures and `ulpwise special f16` work.
|
|
169
|
+
Every value satisfies the same checks as the f32 and f64 tables, run through numpy for float16
|
|
170
|
+
and torch for bfloat16.
|
|
171
|
+
- `ulpwise corpus`: runs the regression corpus against the installed packages without pytest and
|
|
172
|
+
prints one line per case, present, fixed or skipped, with the installed version and the upstream
|
|
173
|
+
reference. `--repo` filters by repository or case id, `--fail-if-present` makes a present bug exit 1.
|
|
174
|
+
Any exception from a repro counts as present, like the pytest run: several corpus bugs are crashes.
|
|
175
|
+
|
|
176
|
+
## 0.2.0 (2026-09-26)
|
|
177
|
+
|
|
178
|
+
- `ulpwise survey` (`ulpwise.survey`): accuracy survey of 61 elementary and special functions of
|
|
179
|
+
torch, numpy, scipy and jax against a 200 bit mpmath reference, in ulps of the dtype, with the
|
|
180
|
+
worst input per row. For torch it also counts inputs where the vectorized kernel and the scalar
|
|
181
|
+
tail disagree and inputs that would fail the `OpInfo` reference test tolerance, default and per op
|
|
182
|
+
override, read from `op_db`. Writes `results.csv` and `results.md`. Optional extra
|
|
183
|
+
`ulpwise[survey]` pulls in mpmath.
|
|
184
|
+
- `studies/accuracy-survey-2026-09`: the first run and its reading notes.
|
|
185
|
+
- Corpus: `max_ulp` check type, and five open cases with the complete patch attached to the issue:
|
|
186
|
+
pytorch #198448 (`torch.sqrt` float64 rounding), pytorch #198583 (Bessel and Airy `p1evl` leading
|
|
187
|
+
1), kornia #4838 and #4897 (small angle series), torchvision #9676 (rotated box clamp). Open cases
|
|
188
|
+
carry `issue`, `pr: null` and `fixed_in_release: null` and stay expected failures until a release
|
|
189
|
+
contains the fix.
|
|
190
|
+
- Corpus: three more open cases, pytorch #198663 (`polygamma(1, x)`: float64 series truncation and
|
|
191
|
+
float32 reflection argument) and pytorch #198664 (`erfcx` negative branch, `exp` at the rounded
|
|
192
|
+
square), both with the complete patch attached to the issue.
|
|
193
|
+
- Corpus: kornia #4768, `ellipse_to_laf` described the wrong ellipse whenever `b != 0`.
|
|
194
|
+
- Corpus: ultralytics #26330, OBB datasets with plain box labels are rejected at load time; the repro
|
|
195
|
+
writes a one-image dataset to a temporary directory, so it needs ultralytics but no weights.
|
|
196
|
+
|
|
197
|
+
## 0.1.2 (2026-09-24)
|
|
198
|
+
|
|
199
|
+
- `ulpwise` console script, so `uvx ulpwise midpoint sqrt 0.85`, `pipx run ulpwise ...` and a plain
|
|
200
|
+
`ulpwise ...` inside a virtualenv work without `python -m`.
|
|
201
|
+
|
|
202
|
+
## 0.1.1 (2026-09-24)
|
|
203
|
+
|
|
204
|
+
No code changes. The AI disclosure in the README was shortened and the package was republished
|
|
205
|
+
from a repository with a fresh history.
|
|
206
|
+
|
|
207
|
+
## 0.1.0 (2026-09-24)
|
|
208
|
+
|
|
209
|
+
First release.
|
|
210
|
+
|
|
211
|
+
- Rust core: ordered float views and ulp distances (`ulp`), named edge values computed from the
|
|
212
|
+
format (`edge`), exact rounding oracles for `sqrt`, reciprocal and division with correctly rounded
|
|
213
|
+
results that do not depend on the platform libm, f64 referenced midpoint reports for 15 more
|
|
214
|
+
`f32` functions (`exact`), and knife-edge scans (`knife`).
|
|
215
|
+
- Python package built with maturin: `ulp_distance`, `assert_max_ulp` and `max_ulp` for floats,
|
|
216
|
+
lists, numpy arrays and torch tensors, `special`, `neighbours`, `binade_edges`, `all_floats`,
|
|
217
|
+
`midpoint`, `knife_edges`, `sqrt_cr`, a CLI (`python -m ulpwise`) and a pytest plugin
|
|
218
|
+
(`edge_f32` / `edge_f64` parametrization, `assert_max_ulp` fixture).
|
|
219
|
+
- Regression corpus of 11 upstream bugs with runnable repros (kornia, pytorch, pytorch/rl, timm,
|
|
220
|
+
peft), run by `tests/test_corpus.py` against whatever is installed.
|
|
221
|
+
- `examples/torch_sqrt_conformance.py`: measures how often a platform's `torch.sqrt` and
|
|
222
|
+
`numpy.sqrt` disagree with correct rounding at knife-edge inputs.
|
|
@@ -37,9 +37,9 @@ dependencies = [
|
|
|
37
37
|
|
|
38
38
|
[[package]]
|
|
39
39
|
name = "pyo3"
|
|
40
|
-
version = "0.29.
|
|
40
|
+
version = "0.29.3"
|
|
41
41
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
checksum = "
|
|
42
|
+
checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655"
|
|
43
43
|
dependencies = [
|
|
44
44
|
"libc",
|
|
45
45
|
"once_cell",
|
|
@@ -51,18 +51,18 @@ dependencies = [
|
|
|
51
51
|
|
|
52
52
|
[[package]]
|
|
53
53
|
name = "pyo3-build-config"
|
|
54
|
-
version = "0.29.
|
|
54
|
+
version = "0.29.3"
|
|
55
55
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
-
checksum = "
|
|
56
|
+
checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb"
|
|
57
57
|
dependencies = [
|
|
58
58
|
"target-lexicon",
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[[package]]
|
|
62
62
|
name = "pyo3-ffi"
|
|
63
|
-
version = "0.29.
|
|
63
|
+
version = "0.29.3"
|
|
64
64
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
65
|
-
checksum = "
|
|
65
|
+
checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330"
|
|
66
66
|
dependencies = [
|
|
67
67
|
"libc",
|
|
68
68
|
"pyo3-build-config",
|
|
@@ -70,9 +70,9 @@ dependencies = [
|
|
|
70
70
|
|
|
71
71
|
[[package]]
|
|
72
72
|
name = "pyo3-macros"
|
|
73
|
-
version = "0.29.
|
|
73
|
+
version = "0.29.3"
|
|
74
74
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
-
checksum = "
|
|
75
|
+
checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6"
|
|
76
76
|
dependencies = [
|
|
77
77
|
"proc-macro2",
|
|
78
78
|
"pyo3-macros-backend",
|
|
@@ -82,9 +82,9 @@ dependencies = [
|
|
|
82
82
|
|
|
83
83
|
[[package]]
|
|
84
84
|
name = "pyo3-macros-backend"
|
|
85
|
-
version = "0.29.
|
|
85
|
+
version = "0.29.3"
|
|
86
86
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
-
checksum = "
|
|
87
|
+
checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3"
|
|
88
88
|
dependencies = [
|
|
89
89
|
"heck",
|
|
90
90
|
"proc-macro2",
|
|
@@ -120,7 +120,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
120
120
|
|
|
121
121
|
[[package]]
|
|
122
122
|
name = "ulpwise"
|
|
123
|
-
version = "0.3.
|
|
123
|
+
version = "0.3.1"
|
|
124
124
|
dependencies = [
|
|
125
125
|
"pyo3",
|
|
126
126
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ulpwise
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Framework :: Pytest
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -11,6 +11,7 @@ Classifier: Topic :: Software Development :: Testing
|
|
|
11
11
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
12
12
|
Requires-Dist: numpy ; extra == 'survey'
|
|
13
13
|
Requires-Dist: mpmath>=1.3 ; extra == 'survey'
|
|
14
|
+
Requires-Dist: expecttest ; extra == 'survey'
|
|
14
15
|
Requires-Dist: pytest>=7 ; extra == 'test'
|
|
15
16
|
Requires-Dist: numpy ; extra == 'test'
|
|
16
17
|
Provides-Extra: survey
|
|
@@ -123,6 +124,11 @@ ulpwise.assert_max_ulp(torch_out, reference, max_ulp_=2) # floats,
|
|
|
123
124
|
dict(ulpwise.special("f32"))["square_underflows_to_zero"] # 2.6469779601696886e-23, the largest x with x * x == 0
|
|
124
125
|
dict(ulpwise.special("bf16"))["square_overflows"] # 1.8446744073709552e+19, 2 ** 64
|
|
125
126
|
|
|
127
|
+
# the floats around a value in any of the four dtypes
|
|
128
|
+
ulpwise.next_up(1.0, "bf16") # 1.0078125
|
|
129
|
+
ulpwise.spacing(65504.0, "f16") # 32.0, the ulp of the largest float16
|
|
130
|
+
ulpwise.neighbours(1.0, 2, "f16") # [0.99902, 0.99951, 1.0, 1.00098, 1.00195]
|
|
131
|
+
|
|
126
132
|
# exact placement of a result relative to its rounding midpoint
|
|
127
133
|
ulpwise.midpoint("sqrt", 0.8528626561164856, "f32") # (0.9235056042671204, 0.000377, True, False)
|
|
128
134
|
ulpwise.midpoint("div", 1.0, "f64", 3.0) # (0.3333333333333333, 0.1666..., True, False)
|
|
@@ -160,7 +166,15 @@ whose packages are installed; a case is an expected failure while the installed
|
|
|
160
166
|
known to contain the fix, so the run tells you which bugs are present in your environment.
|
|
161
167
|
`ulpwise corpus` does the same without pytest, one line per case with the installed version and
|
|
162
168
|
`present`, `fixed` or `skipped`; `ulpwise corpus --repo pytorch --fail-if-present` is the CI gate
|
|
163
|
-
form.
|
|
169
|
+
form. `ULPWISE_CORPUS_STRICT=1 pytest tests/test_corpus.py` also fails on an unexpected pass, which
|
|
170
|
+
is how the weekly scheduled run of this repository (and a manual `workflow_dispatch` run) notices
|
|
171
|
+
that a release fixed a case before `fixed_in_release` says so. To run the corpus against an older release without touching your
|
|
172
|
+
environment, side-install it and put it first on the path:
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
pip install --no-deps --target /tmp/kornia-0.8.3 kornia==0.8.3
|
|
176
|
+
PYTHONPATH=/tmp/kornia-0.8.3 ulpwise corpus --repo kornia # 31 present, 0 fixed on kornia 0.8.3
|
|
177
|
+
```
|
|
164
178
|
|
|
165
179
|
| case | kind | merged |
|
|
166
180
|
|---|---|---|
|
|
@@ -177,18 +191,48 @@ form.
|
|
|
177
191
|
| timm #2792 Kron `__setstate__` shadowed | crash | 2026-09-18 |
|
|
178
192
|
| peft #3777 pointwise Conv3d took the conv2d 1x1 shortcut | shape | 2026-09-21 |
|
|
179
193
|
| ultralytics #26330 OBB train and val on plain box labels crashed in the validator or the loss instead of at load time | crash | 2026-09-25 |
|
|
194
|
+
| ultralytics #26379 `scale_masks` kept a padded row when the letterbox padding was odd, the bottom of every mask faded to 0 | rounding | 2026-09-28 |
|
|
195
|
+
| ultralytics #26377 `verify_image_label` dropped a polygon that shared its class and box with another | aliasing | 2026-09-28 |
|
|
196
|
+
| ultralytics #26357 `verify_image_label` read a pose row as a polygon for a detect task and dropped the image as corrupt | crash | 2026-09-27 |
|
|
197
|
+
| kornia #4941 `So3.log`, the `So3` Jacobians and `Se3.exp/log` lost all digits for small angles (issue #4897) | series | 2026-09-26 |
|
|
198
|
+
| kornia #5124 `axis_angle_to_rotation_matrix` dropped the `theta^2` terms below 1e-3 rad (issue #4838) | series | 2026-09-30 |
|
|
199
|
+
| kornia #4960 `Se2.exp` and `Se2.log` lost the translation at small angles | cancellation | 2026-09-26 |
|
|
200
|
+
| kornia #4975 `point_line_distance` ignored the weight of a homogeneous point | scale | 2026-09-27 |
|
|
201
|
+
| kornia #5003 `Quaternion.__pow__` returned the zero quaternion on the negative real axis | branch cut | 2026-09-27 |
|
|
202
|
+
| kornia #5005 `So2` from a `(B, 1)` angle rotated `(B, 2)` points into `(B, B, 2)` | shape | 2026-09-27 |
|
|
203
|
+
| kornia #5024 `solve_cubic` lost the root of a cubic with a tiny leading coefficient | scale | 2026-09-28 |
|
|
204
|
+
| kornia #5111 `RgbToGrayscale` turned a uint8 image into zeros | dtype | 2026-09-30 |
|
|
205
|
+
| kornia #5134 `conv_soft_argmax2d` shifted every window's exponent by the maximum of the whole map | normalisation | 2026-09-30 |
|
|
206
|
+
| kornia #5376 `get_gaussian_discrete_kernel1d(1, sigma)` returned 3 taps | shape | 2026-10-03 |
|
|
207
|
+
| kornia #4963 `Se3.exp` at `omega = 0`: `d t / d omega` was nan, then zero, instead of `-0.5 [upsilon]_x` | gradient | 2026-09-26 |
|
|
208
|
+
| kornia #4981 `Quaternion.polar_angle` had a nan gradient at the identity | gradient | 2026-09-27 |
|
|
209
|
+
| kornia #4998 `decompose_essential_matrix` added a batch dimension to a `(3, 3)` input | shape | 2026-09-27 |
|
|
210
|
+
| kornia #5058 `Hyperplane.through` had nan gradients for orthogonal edges of equal length | gradient | 2026-09-29 |
|
|
211
|
+
| kornia #5084 `Vector3.normalized` turned a float16 zero vector into NaN | dtype | 2026-09-29 |
|
|
212
|
+
| kornia #5182 `otsu_threshold(return_mask=True)` compared the thresholded image with 0 | sign | 2026-10-01 |
|
|
213
|
+
| kornia #4972 the Hessian of `So3.exp` at the identity was nan (issue #4966) | gradient | 2026-09-27 |
|
|
214
|
+
| kornia #5116 `sampson_epipolar_distance` scored an exact match `sqrt(eps)` and changed with the scale of `F` (issue #4881) | eps, scale | 2026-09-30 |
|
|
215
|
+
| kornia #5131 `RandomHue` shifted a float64 image by the float32 pi, 8.7e-8 past a half turn (issue #5127) | constant | 2026-09-30 |
|
|
216
|
+
| kornia #5143 `MS_SSIMLoss` built an even window for sigma 1.3 and lost a row and a column (issue #5126) | shape | 2026-10-01 |
|
|
217
|
+
| kornia #5353 `MS_SSIMLoss` raised on a uint8 image (issue #5351) | crash | 2026-10-02 |
|
|
218
|
+
| kornia #4967 `So3.right_jacobian` in float16 was off by 0.79 above 41 rad (issue #4965) | overflow | 2026-09-26 |
|
|
219
|
+
| kornia #4980 `average_quaternions` counted a member stored as `3 q` nine times (issue #4974) | normalisation | 2026-09-27 |
|
|
220
|
+
| kornia #5104 `Hyperplane.through` flipped the normal of a large float16 triangle | overflow | 2026-09-29 |
|
|
221
|
+
| kornia #5301 `filter2d` with one kernel per sample raised on a channels-last input (issue #5292) | crash | 2026-10-02 |
|
|
222
|
+
| kornia #5303 `lovasz_hinge_loss` returned a float32 loss for a float16 prediction (issue #5289) | dtype | 2026-10-02 |
|
|
223
|
+
| kornia #5357 `get_box_kernel1d` returned a stride-0 view, editing one tap rewrote them all (issue #5160) | aliasing | 2026-10-02 |
|
|
180
224
|
| pytorch #198448 `torch.sqrt` float64 not correctly rounded at 27 of 64 knife edges | rounding | open |
|
|
181
225
|
| pytorch #198583 `bessel_j0/j1/y0/y1`, `airy_ai` float64 lose up to 12 digits (`p1evl` leading 1) | digits | open |
|
|
182
|
-
| kornia #4838 `axis_angle_to_rotation_matrix` drops the `theta^2` terms below 1e-3 rad | series | open |
|
|
183
|
-
| kornia #4897 `So3.log`, the `So3` Jacobians and `Se3.exp/log` lose all digits for small angles | series | open |
|
|
184
226
|
| torchvision #9676 `clamp_bounding_boxes` collapses slightly tilted rotated boxes to a point | geometry | open |
|
|
185
227
|
| pytorch #198663 `polygamma(1, x)` float64 keeps 9 digits (series stops at `1/42`), float32 loses all for large negative `x` | truncation, rounding | open |
|
|
186
228
|
| pytorch #198664 `erfcx` off by `x*x/2` ulps for negative `x` (`exp` at the rounded square) | rounding | open |
|
|
187
229
|
|
|
188
|
-
Cases marked `open` have an issue with the complete patch attached and no merged fix yet
|
|
189
|
-
expected
|
|
190
|
-
`
|
|
191
|
-
|
|
230
|
+
Cases marked `open` have an issue with the complete patch attached and no merged fix yet. Every case
|
|
231
|
+
is an expected failure until the installed release contains the fix (`fixed_in_release` in
|
|
232
|
+
`cases.json`; the timm and ultralytics fixes above are released, the kornia, pytorch/rl, peft and
|
|
233
|
+
pytorch ones are merged but not in a release yet), and the `max_ulp` check type measures the digits
|
|
234
|
+
directly. The ultralytics dataset and label cases build their one-image
|
|
235
|
+
dataset in a temporary directory and need no weights; two more ultralytics fixes (#26240, #26246) are not in
|
|
192
236
|
the corpus yet because their repros need model weights or the COCO evaluator.
|
|
193
237
|
|
|
194
238
|
## Repository scan
|
|
@@ -208,7 +252,7 @@ overflows, the usual replacement and, where one exists, the upstream bug it come
|
|
|
208
252
|
| `exp-of-square` | high | `exp(x * x)`, `exp(x ** 2)`, `(-x.pow(2)).exp()`: the rounding error of the square is multiplied by `x * x / 2` ulps (pytorch #198664) |
|
|
209
253
|
| `sin-of-pi-times` | high | `sin(pi * x)`, `cos(pi * x)`: the product is rounded before the argument reduction (pytorch #198663) |
|
|
210
254
|
| `softplus-by-hand` | high | `log(1 + exp(x))` |
|
|
211
|
-
| `logsumexp-by-hand` | high | `log(exp(a) + exp(b))`, `log(sum(exp(x)))` |
|
|
255
|
+
| `logsumexp-by-hand` | high | `log(exp(a) + exp(b))`, `log(sum(exp(x)))`, `log(exp(x).sum(-1))` |
|
|
212
256
|
| `hypot-by-hand` | high | `sqrt(a * a + b * b)` |
|
|
213
257
|
| `sqrt-of-difference` | medium | `sqrt(a - b)` |
|
|
214
258
|
| `one-minus-cos` | medium | `1 - cos(x)` (kornia #4897) |
|
|
@@ -260,16 +304,23 @@ the reading list is where that judgement happens.
|
|
|
260
304
|
pip install 'ulpwise[survey]' torch scipy jax # mpmath is the reference, the rest are backends
|
|
261
305
|
ulpwise survey --out survey # results.csv and results.md, about 3 minutes
|
|
262
306
|
ulpwise survey --functions bessel_j0,polygamma_1 --backends torch,scipy --dtypes f64 --points 2000
|
|
307
|
+
ulpwise survey --functions erf,exp --backends torch --dtypes f16,bf16 # half precision rows
|
|
263
308
|
```
|
|
264
309
|
|
|
265
310
|
For every function in `ulpwise.survey.REGISTRY` (exp, log, trig and hyperbolic functions, erf and
|
|
266
311
|
friends, gamma family, torch.special Bessel and Airy functions, the activation functions), every
|
|
267
|
-
dtype
|
|
312
|
+
dtype (`f64`, `f32`, `f16` and `bf16`; the default is `f32,f64`) and every installed backend, the
|
|
313
|
+
survey evaluates a log spaced grid over the function's domain, clipped to the finite range of the dtype,
|
|
268
314
|
plus the named edge values of the dtype, computes the exact value with mpmath at the rounded input,
|
|
269
315
|
and reports max, p99 and median error in ulps, the fraction of inputs beyond 1 and 10 ulps, non
|
|
270
316
|
finite mismatches and the worst input. For torch it also reports how many inputs the vectorized
|
|
271
317
|
kernel and the scalar tail disagree on, and how many inputs would fail torch's reference test under
|
|
272
|
-
the dtype default tolerance and under the op's `OpInfo` override, read from `op_db`.
|
|
318
|
+
the dtype default tolerance and under the op's `OpInfo` override, read from `op_db`. Reading `op_db` needs
|
|
319
|
+
`expecttest`, a test-only dependency of torch that the `survey` extra installs; without it the survey logs
|
|
320
|
+
one line and uses the default. torch and jax have `f16` and `bf16` rows; numpy has no bfloat16 and
|
|
321
|
+
scipy.special computes a float16 input in float32, so neither has `bf16` rows and scipy has no `f16` rows.
|
|
322
|
+
A backend without a kernel for a function in a dtype (torch's Bessel functions in half precision) is
|
|
323
|
+
logged and skipped.
|
|
273
324
|
|
|
274
325
|
[`studies/accuracy-survey-2026-09`](https://github.com/Nicholas022400701/ulpwise/blob/main/studies/accuracy-survey-2026-09/README.md) is the first run
|
|
275
326
|
(torch 2.14.0+cpu, numpy 2.2.6, scipy 1.18.1, jax 0.11.2, Linux x86_64 AVX512). The short version:
|
|
@@ -307,10 +358,10 @@ cross-checks both against `fractions.Fraction` and `decimal.Decimal` at 80 digit
|
|
|
307
358
|
docstring and the checks for the shapes, and an mpmath reference for the scalar functions.
|
|
308
359
|
- Mutation scoring for numerical tests: single token mutants of the code under test (`abs`, a
|
|
309
360
|
dropped `sqrt`, `/ 4` for `/ 16`) run against the test suite, reporting which survive.
|
|
310
|
-
- `float16` and `bfloat16`
|
|
361
|
+
- `float16` and `bfloat16` knife edges and exact oracles (ulp distances, edge values, `spacing` and the neighbour functions are done).
|
|
311
362
|
- Exact references for transcendental functions in Rust (correctly rounded `exp`, `log`, ...) so
|
|
312
363
|
the `f64` knife-edge scans do not need mpmath.
|
|
313
|
-
- Survey backends for CUDA and MPS
|
|
364
|
+
- Survey backends for CUDA and MPS.
|
|
314
365
|
- Zero copy paths for numpy arrays and torch tensors.
|
|
315
366
|
- More corpus entries, with fixtures for the cases that need data.
|
|
316
367
|
|