baseline: the published sim-numerics artifact as it stands (490 tests, verify_article.py checks all 72 paper numbers against sweep_full.json)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"""Tests for the three-point scale.
|
||||
|
||||
The claim being pinned: a divided difference is a COMPUTATION, not a fitted
|
||||
coefficient, so `derived3` introduces no parameter; and it must actually lift
|
||||
the order, otherwise the stage-4 explanation of the endgame deficit is wrong.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from simroot.pure import GUARDS, PURE_SIMS, _slope3, pure_root
|
||||
from stage5_three_point import PROBLEMS, order_of
|
||||
|
||||
XTOL = 1e-12
|
||||
MAXFEV = 300
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# the divided difference itself
|
||||
# --------------------------------------------------------------------------
|
||||
@pytest.mark.parametrize("q", [
|
||||
lambda t: 3.0 * t * t - 2.0 * t + 5.0,
|
||||
lambda t: -1.5 * t * t + 0.25 * t - 7.0,
|
||||
lambda t: t * t,
|
||||
])
|
||||
def test_slope3_is_exact_on_a_quadratic(q):
|
||||
"""The derivative of the parabola through three points of a quadratic is
|
||||
that quadratic's derivative, exactly. If this drifts, the order gain is
|
||||
not what it is claimed to be."""
|
||||
h = 1e-3
|
||||
for x2 in (-2.0, 0.3, 4.0):
|
||||
x1, x0 = x2 - h, x2 - 2.0 * h
|
||||
s = _slope3(x2, q(x2), x1, q(x1), x0, q(x0))
|
||||
exact = (q(x2 + 1e-7) - q(x2 - 1e-7)) / 2e-7
|
||||
assert s == pytest.approx(exact, rel=1e-6), (x2, s, exact)
|
||||
|
||||
|
||||
def test_slope3_is_exact_on_a_line():
|
||||
f = lambda t: 4.0 * t - 1.0
|
||||
s = _slope3(2.0, f(2.0), 1.0, f(1.0), 0.0, f(0.0))
|
||||
assert s == pytest.approx(4.0)
|
||||
|
||||
|
||||
def test_slope3_falls_back_to_two_points_when_the_third_is_missing():
|
||||
f = lambda t: t * t
|
||||
two = (f(2.0) - f(1.0)) / (2.0 - 1.0)
|
||||
assert _slope3(2.0, f(2.0), 1.0, f(1.0), None, None) == pytest.approx(two)
|
||||
# a repeated node must not divide by zero
|
||||
assert math.isfinite(_slope3(2.0, f(2.0), 1.0, f(1.0), 1.0, f(1.0)))
|
||||
assert math.isfinite(_slope3(2.0, f(2.0), 1.0, f(1.0), 2.0, f(2.0)))
|
||||
|
||||
|
||||
def test_slope3_never_returns_nan_or_inf_on_degenerate_input():
|
||||
for args in [(1.0, 0.0, 1.0, 0.0, 1.0, 0.0),
|
||||
(1.0, 1e308, 0.5, -1e308, 0.0, 1e308),
|
||||
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)]:
|
||||
s = _slope3(*args)
|
||||
assert math.isfinite(s) or s == 0.0
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# the method built on it
|
||||
# --------------------------------------------------------------------------
|
||||
@pytest.mark.parametrize("guard", GUARDS)
|
||||
@pytest.mark.parametrize("prob", PROBLEMS, ids=[p[0] for p in PROBLEMS])
|
||||
def test_derived3_converges(prob, guard):
|
||||
_, f, a, b, root = prob
|
||||
x, n, conv = pure_root(f, a, b, XTOL, MAXFEV, scale="derived3", guard=guard)
|
||||
assert conv, f"derived3.{guard} did not converge"
|
||||
assert abs(x - root) <= 1e-8
|
||||
assert n <= MAXFEV
|
||||
|
||||
|
||||
@pytest.mark.parametrize("sim", sorted(PURE_SIMS))
|
||||
def test_derived3_works_for_every_similarity(sim):
|
||||
f = lambda t: t * t - 2.0
|
||||
x, n, conv = pure_root(f, 0.0, 2.0, XTOL, MAXFEV, similarity=sim,
|
||||
scale="derived3", guard="both")
|
||||
assert conv and abs(x - math.sqrt(2.0)) <= 1e-8
|
||||
|
||||
|
||||
def test_three_points_raise_the_order_above_two_points():
|
||||
"""The whole point of the change. Measured on the problems where an
|
||||
order can be estimated at all."""
|
||||
two, three = [], []
|
||||
for _n, f, a, b, root in PROBLEMS:
|
||||
p2 = order_of(lambda f_, a_, b_, xt, mx: pure_root(
|
||||
f_, a_, b_, XTOL, MAXFEV, scale="derived", guard="step"),
|
||||
f, a, b, root)
|
||||
p3 = order_of(lambda f_, a_, b_, xt, mx: pure_root(
|
||||
f_, a_, b_, XTOL, MAXFEV, scale="derived3", guard="step"),
|
||||
f, a, b, root)
|
||||
if math.isfinite(p2) and math.isfinite(p3):
|
||||
two.append(p2)
|
||||
three.append(p3)
|
||||
assert len(two) >= 5
|
||||
m2 = sorted(two)[len(two) // 2]
|
||||
m3 = sorted(three)[len(three) // 2]
|
||||
assert 1.4 < m2 < 1.8, m2 # secant
|
||||
assert m3 > m2 + 0.15, (m2, m3) # a real lift, not noise
|
||||
|
||||
|
||||
def test_the_sound_guard_still_costs_the_order():
|
||||
"""Honest pin of the tension found in stage 5: `both` carries the 2x
|
||||
bisection bound and, on a method that shrinks the ERROR faster than the
|
||||
BRACKET, that bound forces a bisection every other step and flattens the
|
||||
order. This is a property of the guarantee, not a defect - but if
|
||||
someone later claims high order AND the 2x bound from the same
|
||||
configuration, this test says otherwise."""
|
||||
f = lambda t: t * t - 2.0
|
||||
root = math.sqrt(2.0)
|
||||
p_step = order_of(lambda f_, a_, b_, xt, mx: pure_root(
|
||||
f_, a_, b_, XTOL, MAXFEV, scale="derived3", guard="step"),
|
||||
f, 0.0, 2.0, root)
|
||||
p_both = order_of(lambda f_, a_, b_, xt, mx: pure_root(
|
||||
f_, a_, b_, XTOL, MAXFEV, scale="derived3", guard="both"),
|
||||
f, 0.0, 2.0, root)
|
||||
assert p_step > p_both, (p_step, p_both)
|
||||
Reference in New Issue
Block a user