PREREG before code and run: repair the two base-step gaps - close (Dekker/Brent's tolerance step, named as borrowed) and sub (the second branch of |1 - 1/m| > 1/2: enter the regime for m <= 2/3 on an overshooting unkept promise) - and test A' = A + close + sub once on a FOURTH corpus (corpus_fourth.py, seed 20260922, 240 problems in six new families, two of them targeted), committed now; development only on the seen corpora. F1-F3 transfer, F4 the repairs earned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
vvs
2026-09-21 19:54:13 +01:00
co-authored by Claude Opus 5
parent c2b0eea0f6
commit 657df10634
2 changed files with 199 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
"""The FOURTH out-of-sample corpus for PREREG_close_sub_2026-09-21 - written and committed before
the two repairs are implemented and before any method is run on it. Seed 20260922; the
function families are NEW (none of corpus_new's closed forms is reused). 40 per class:
D1 random polynomial, one simple root in the bracket (others outside) - neutral
D2 random polynomial with one MULTIPLE root of odd multiplicity 3..9 - flat side
D3 fractional power |x - r|^p sign(x - r) * g(x), p ~ U(0.15, 0.95) - TARGETED (repair 2)
D4 sums of exponentials and sines with one root in the bracket - neutral
D5 compositions tanh(q(x)), atan(q(x)), log1p(q(x)^2) - c with a cubic q - neutral
D6 odd around the root: s(x - r) with s in {sin(k.), (.)+a(.)^3, arcsinh(k.)} - TARGETED (repair 1)
Roots are known by construction for D1-D3 and D6; for D4-D5 success is judged by a sign
change within 1e-6 (corpus_roots_jl.solved), which is used for every class for uniformity.
Brackets: width log-uniform on [0.1, 100], the root at a fraction U(0.02, 0.98). A problem
without finite values of opposite sign at the ends is redrawn (redraws reported).
"""
from __future__ import annotations
import math
import numpy as np
SEED = 20260922
PER_CLASS = 40
def _bracket(rng, r, lo=0.1, hi=100.0):
L = math.exp(rng.uniform(math.log(lo), math.log(hi)))
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 _poly_from_roots(roots, scale):
def f(x, roots=tuple(roots), scale=scale):
v = scale
for q in roots:
v *= (x - q)
return v
return f
def _d1(rng):
r = rng.uniform(-1, 1)
a, b = _bracket(rng, r)
deg = int(rng.integers(3, 10))
others = []
while len(others) < deg - 1: # the other roots outside the bracket, or complex pairs
if rng.uniform() < 0.5 and len(others) <= deg - 3:
c, d = rng.uniform(-3, 3), rng.uniform(0.1, 3)
others.append(("c", c, d))
others.append(None)
else:
side = a - rng.uniform(0.1, 5) if rng.uniform() < 0.5 else b + rng.uniform(0.1, 5)
others.append(("r", side))
others = [o for o in others if o is not None][: deg - 1]
def f(x, r=r, others=tuple(others)):
v = x - r
for o in others:
v *= (x - o[1]) if o[0] == "r" else ((x - o[1]) ** 2 + o[2] ** 2)
return v
return f, r, (a, b)
def _d2(rng):
r = rng.uniform(-1, 1)
m = int(rng.choice([3, 5, 7, 9]))
a, b = _bracket(rng, r, hi=10.0)
c = rng.uniform(0.5, 3)
return (lambda x, r=r, m=m, c=c: (x - r) ** m * (1 + c * x * x)), r, (a, b)
def _d3(rng):
r = rng.uniform(-1, 1)
p = rng.uniform(0.15, 0.95)
a0, c = rng.uniform(0, 1), rng.uniform(0.5, 5)
a, b = _bracket(rng, r)
return (lambda x, r=r, p=p, a0=a0, c=c:
math.copysign(abs(x - r) ** p, x - r) * math.exp(a0 * math.sin(c * x))), r, (a, b)
def _d4(rng):
r = rng.uniform(-1, 1)
k1, k2 = rng.uniform(-2, 2), rng.uniform(-2, 2)
w, ph = rng.uniform(0.5, 4), rng.uniform(0, 2 * math.pi)
a, b = _bracket(rng, r, hi=20.0)
def g(x, k1=k1, k2=k2, w=w, ph=ph):
return math.exp(k1 * x) + 0.5 * math.exp(k2 * x) + math.sin(w * x + ph)
gr = g(r)
slope = abs(k1) + abs(k2) + w + 1.0
return (lambda x, g=g, gr=gr, r=r, s=slope: g(x) - gr + 1.5 * s * (x - r)), r, (a, b)
def _d5(rng):
r = rng.uniform(-1, 1)
c1, c2 = rng.uniform(0.5, 3), rng.uniform(-1, 1)
kind = int(rng.integers(3))
a, b = _bracket(rng, r, hi=10.0)
q = lambda x, r=r, c1=c1, c2=c2: c1 * (x - r) + c2 * (x - r) ** 2 + (x - r) ** 3
if kind == 0:
return (lambda x, q=q: math.tanh(q(x))), r, (a, b)
if kind == 1:
return (lambda x, q=q: math.atan(q(x))), r, (a, b)
return (lambda x, q=q: math.copysign(math.log1p(q(x) ** 2), q(x))), r, (a, b)
def _d6(rng):
r = rng.uniform(-1, 1)
k = math.exp(rng.uniform(math.log(0.3), math.log(3)))
kind = int(rng.integers(3))
a, b = _bracket(rng, r, hi=3.0 / k if kind == 0 else 100.0) # sin: a single root
if kind == 0:
return (lambda x, r=r, k=k: math.sin(k * (x - r))), r, (a, b)
if kind == 1:
aa = rng.uniform(0.1, 2)
return (lambda x, r=r, aa=aa: (x - r) + aa * (x - r) ** 3), r, (a, b)
return (lambda x, r=r, k=k: math.asinh(k * (x - r))), r, (a, b)
def build():
out, redraws = [], {}
rng = np.random.default_rng(SEED)
for cls, gen in (("D1", _d1), ("D2", _d2), ("D3", _d3), ("D4", _d4), ("D5", _d5), ("D6", _d6)):
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__":
from collections import Counter
probs, red = build()
print(len(probs), dict(Counter(p[0] for p in probs)), "redraws", red)