PREREG before any run: out-of-sample corpus and ablation for the two strongest variants. corpus_new.py (seed 20260921): SciPy 'original' (6) + five generated classes x 40 (smooth simple, transcendental simple, multiple/flat |x-r|^p p~U(2,20), exponential flank, near-cancelling terms), built and validity-checked only (no method has seen it). O1-O3 = transfers (total below Chandrupatla, per-problem sign test p<0.05, guarantee); O4 vs the plain rule; per class. Leave-one-out ablation of every component on APS, CHA and the new corpus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+124
@@ -0,0 +1,124 @@
|
||||
"""An OUT-OF-SAMPLE corpus for PREREG_out_of_sample_2026-09-21: problems no method variant
|
||||
was developed on. Fixed before any method is run on it; seed and distributions below are
|
||||
part of the pre-registration and are never changed afterwards.
|
||||
|
||||
Six classes. C0 is SciPy's 'original' collection (6 problems, never used here before).
|
||||
C1-C5 are generated, 40 problems each, from one numpy Generator(seed=20260921):
|
||||
|
||||
C1 smooth simple root f = (x - r) * g(x), g = exp(a sin(c x)) + b x^2 > 0
|
||||
C2 transcendental simple f = one of atan(k(x-r)), tanh(k(x-r)), expm1(k(x-r)), sinh(k(x-r))
|
||||
C3 multiple / flat root f = sign(x-r) |x-r|^p * g(x), p ~ U(2, 20) (real, not only odd)
|
||||
C4 exponential flank f = exp(k x) - exp(k r) (steep one-sided growth)
|
||||
C5 near-cancelling terms f = exp(x) - e^r + a (1/x^2 - 1/r^2) - b (1/x^3 - 1/r^3), x > 0
|
||||
(the kind of end that misled us on fun9, with random parameters)
|
||||
|
||||
Brackets: root r ~ U(-1, 1) (C5: r ~ U(0.3, 2)); width L log-uniform on [0.1, 1000] (C4:
|
||||
capped so that k * right end <= 600, C5: left end >= r/1000); the root sits at a fraction
|
||||
u ~ U(0.02, 0.98) of the bracket. Every problem is checked for finite values of opposite
|
||||
sign at the ends; one that fails is redrawn (the redraw count is reported).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
SEED = 20260921
|
||||
PER_CLASS = 40
|
||||
|
||||
|
||||
def _bracket(rng, r, lo_w=0.1, hi_w=1000.0):
|
||||
L = math.exp(rng.uniform(math.log(lo_w), math.log(hi_w)))
|
||||
u = rng.uniform(0.02, 0.98)
|
||||
return r - u * L, r + (1.0 - u) * L
|
||||
|
||||
|
||||
def _ok(f, a, b):
|
||||
try:
|
||||
fa, fb = f(a), f(b)
|
||||
except (OverflowError, ValueError, ZeroDivisionError):
|
||||
return False
|
||||
return (math.isfinite(fa) and math.isfinite(fb) and fa != 0.0 and fb != 0.0
|
||||
and (fa < 0.0) != (fb < 0.0))
|
||||
|
||||
|
||||
def _c1(rng):
|
||||
r = rng.uniform(-1, 1)
|
||||
a, b, c = rng.uniform(0, 1), rng.uniform(0, 0.5), rng.uniform(0.5, 5)
|
||||
f = lambda x, r=r, a=a, b=b, c=c: (x - r) * (math.exp(a * math.sin(c * x)) + b * x * x)
|
||||
return f, r, _bracket(rng, r)
|
||||
|
||||
|
||||
def _c2(rng):
|
||||
r = rng.uniform(-1, 1)
|
||||
k = math.exp(rng.uniform(math.log(0.2), math.log(20)))
|
||||
kind = int(rng.integers(4))
|
||||
base = (math.atan, math.tanh, math.expm1, math.sinh)[kind]
|
||||
a, b = _bracket(rng, r)
|
||||
if kind in (2, 3): # keep exp/sinh finite on the bracket
|
||||
span = max(abs(a - r), abs(b - r))
|
||||
if k * span > 600:
|
||||
k = 600 / span
|
||||
return (lambda x, r=r, k=k, g=base: g(k * (x - r))), r, (a, b)
|
||||
|
||||
|
||||
def _c3(rng):
|
||||
r = rng.uniform(-1, 1)
|
||||
p = rng.uniform(2, 20)
|
||||
a, c = rng.uniform(0, 1), rng.uniform(0.5, 5)
|
||||
lo, hi = _bracket(rng, r, hi_w=30.0) # |x - r|^20 stays finite
|
||||
f = lambda x, r=r, p=p, a=a, c=c: math.copysign(abs(x - r) ** p, x - r) * math.exp(a * math.sin(c * x))
|
||||
return f, r, (lo, hi)
|
||||
|
||||
|
||||
def _c4(rng):
|
||||
r = rng.uniform(-1, 1)
|
||||
k = math.exp(rng.uniform(math.log(0.5), math.log(5)))
|
||||
lo, hi = _bracket(rng, r)
|
||||
if k * hi > 600:
|
||||
hi = 600 / k
|
||||
er = math.exp(k * r)
|
||||
return (lambda x, k=k, er=er: math.exp(k * x) - er), r, (lo, hi)
|
||||
|
||||
|
||||
def _c5(rng):
|
||||
r = rng.uniform(0.3, 2.0)
|
||||
a = math.exp(rng.uniform(math.log(1e-3), math.log(1e-1)))
|
||||
b = a * rng.uniform(0.01, 0.5) * r
|
||||
L = math.exp(rng.uniform(math.log(0.1), math.log(100)))
|
||||
u = rng.uniform(0.02, 0.98)
|
||||
lo, hi = max(r - u * L, r / 1000.0), r + (1.0 - u) * L
|
||||
hi = min(hi, 600.0)
|
||||
er = math.exp(r)
|
||||
f = lambda x, r=r, a=a, b=b, er=er: (math.exp(x) - er + a * (1 / x ** 2 - 1 / r ** 2)
|
||||
- b * (1 / x ** 3 - 1 / r ** 3))
|
||||
return f, r, (lo, hi)
|
||||
|
||||
|
||||
def build():
|
||||
"""[(class, id, f, a, b, root)] and the number of redraws per class."""
|
||||
from scipy.optimize._tstutils import _ORIGINAL_TESTS_DICTS
|
||||
out, redraws = [], {}
|
||||
for i, d in enumerate(_ORIGINAL_TESTS_DICTS):
|
||||
a, b = d["bracket"]
|
||||
args = d.get("args", ())
|
||||
f = (lambda ff, aa: lambda x: ff(x, *aa))(d["f"], args)
|
||||
out.append(("C0", f"C0.{i:02d}", f, float(a), float(b), float(d["root"])))
|
||||
rng = np.random.default_rng(SEED)
|
||||
for cls, gen in (("C1", _c1), ("C2", _c2), ("C3", _c3), ("C4", _c4), ("C5", _c5)):
|
||||
n = red = 0
|
||||
while n < PER_CLASS:
|
||||
f, r, (a, b) = gen(rng)
|
||||
if _ok(f, a, b) and min(a, b) < r < max(a, b):
|
||||
out.append((cls, f"{cls}.{n:02d}", f, float(a), float(b), float(r)))
|
||||
n += 1
|
||||
else:
|
||||
red += 1
|
||||
redraws[cls] = red
|
||||
return out, redraws
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
probs, red = build()
|
||||
from collections import Counter
|
||||
print("problems per class:", dict(Counter(p[0] for p in probs)), " redraws:", red)
|
||||
Reference in New Issue
Block a user