Skip to content

A relative tolerance at a zero-valued optimum fails on the runs that search best - #304

Merged
sbryngelson merged 1 commit into
masterfrom
fix-degenerate-relative-tolerance-at-the-optimum
Sep 15, 2026
Merged

sbryngelson merged 1 commit into
masterfrom
fix-degenerate-relative-tolerance-at-the-optimum

Conversation

@sbryngelson

Copy link
Copy Markdown
Owner

What is failing

Nightly CI has failed since 8 September on 3.14, and since 9 September on 3.13, in tests/test_optimize.py::test_the_result_records_everything_it_evaluated. 3.12 is still green. Nothing else fails; the later steps just never run because the test step aborts the job.

Master has not changed since 19 August, so the trigger is environmental: numpy 2.4.6 -> 2.5.3.

Root cause

The test compared best_value (the GP posterior mean at the best point) against the raw observation there, with a relative tolerance:

assert result.best_value == pytest.approx(result.values[chosen], rel=1e-3)

On a noiseless objective those two differ by the GP's interpolation floor, an absolute error of ~1e-10 to 7e-08 set by _JITTER = 1e-10 and the conditioning of the Cholesky in pyimr/optimize.py:70.

But _bowl peaks at exactly zero. So the closer the search lands to the optimum, the smaller values[chosen] becomes while the numerator stays put. The bound is constant / (something -> 0): it tightens without limit as the optimiser improves, and fails precisely on the best runs.

The numpy upgrade perturbed L-BFGS-B rounding just enough to move seed 0 to a better point. Reproduced locally in a venv pinned to CI's versions:

numpy 2.4.6 (green) numpy 2.5.3 (CI, red)
|x*| from optimum 0.053 0.0029 (better)
values[chosen] -2.82e-03 -8.62e-06
absolute error 3.80e-08 3.06e-08 (unchanged)
relative error 1.3e-05 -> pass 3.5e-03 -> fail

The absolute error is flat; only the denominator collapsed. A seed sweep on the old numpy confirms the mechanism independently: seed 6 lands 0.003 from the optimum and fails identically, while seed 8 lands 0.096 away and passes with a 1e-08 relative error.

bayesian_maximize is not at fault and is not touched. The companion assertion, that the chosen point is still the best observation, passed throughout CI.

The change

Adds abs=1e-6 — the invariant that actually holds. It is ~14x the worst interpolation error measured over ten seeds (7.2e-08) and six orders of magnitude below the data range (~1.8), so it still genuinely tests that the GP interpolates. rel=1e-3 is kept to guard the large-magnitude end.

The docstring's claim of "~3e-05 relative" was never a property of the code, only of where seed 0 happened to land; it is replaced by the absolute floor and the reason the relative form degenerates.

Verification

  • CI-matched venv (Python 3.14, numpy 2.5.3, scipy 1.18.1): old assertion FAILS, new one PASSES.
  • The exact floats from the CI log (-9.288960770237331e-06 vs -9.330864970410784e-06): old FAILS, new PASSES.
  • Full tests/test_optimize.py in the development env: 11 passed.
  • The other twenty rel= assertions in the suite were checked; none compares against a quantity that approaches zero, so the pattern is isolated to this test.

…search best

The nightly CI has failed since 8 September on 3.14, and since 9 September on
3.13, in test_the_result_records_everything_it_evaluated. Master has not changed
since 19 August; numpy 2.4.6 -> 2.5.3 did.

The test compared best_value, the GP posterior mean at the best point, against
the raw observation there, with rel=1e-3. On a noiseless objective those differ
by the interpolation floor -- an ABSOLUTE error of 1e-10 to 7e-08, set by _JITTER
and the conditioning of the Cholesky. But _bowl peaks at exactly zero, so the
closer the search lands to the optimum, the smaller values[chosen] gets while the
numerator stays put. The relative bound therefore tightens without limit as the
optimiser improves, and fails precisely on the best runs.

That is what happened. The numpy upgrade perturbed the L-BFGS-B rounding enough
to move seed 0 from |x*| = 0.053 to |x*| = 0.0029 -- a more accurate answer -- and
values[chosen] fell from -2.8e-03 to -8.6e-06. The absolute error was unchanged
at 3e-08; the relative error rose from 1.3e-05 to 3.5e-03 and tripped the bound.
Pinning numpy 2.5.3 and scipy 1.18.1 locally reproduces the CI failure exactly.

bayesian_maximize is not at fault and is not touched. The companion assertion,
that the chosen point is still the best observation, passed throughout CI.

Adds abs=1e-6, the invariant that actually holds: ~14x the worst interpolation
error measured over ten seeds, and six orders of magnitude below the data range,
so it still tests that the GP interpolates. The docstring's "~3e-05 relative" was
never a property of the code, only of where seed 0 happened to land, and is
replaced by the absolute floor and the reason the relative form degenerates.

The other twenty rel= assertions in the suite were checked; none compares against
a quantity that approaches zero, so the pattern is isolated to this test.
@sbryngelson
sbryngelson merged commit ca1ce19 into master Sep 15, 2026
3 checks passed
@sbryngelson
sbryngelson deleted the fix-degenerate-relative-tolerance-at-the-optimum branch September 15, 2026 04:37
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