Files
sim-numerics/diag_analogy_late.py
T

100 lines
3.4 KiB
Python

"""Why does the analogy entry come late on the flat families? (Valeri, 2026-09-21)
The entry is flag AND _analogous_and_steep(cur, memory). The method calls the second
only when the flag has risen on a model step (short-circuit `and`), so a spy on it sees
exactly the flag moments. At each one it records how many evaluations of f were spent
and which condition held back the entry:
no-config the last three points give no (xi, phi)
no-memory nothing to compare with yet
unlike-past the configuration is nearer to the line than to any past one
m<2 analogous, but the pair's similarity exponent is below 2 (value kept)
ENTER
Compared with flatstickexit, which enters at the first flag. Nothing in the method
changes; problems are the 20 flat CHA problems (fun3-6), proposed rule (projection).
python -u diag_analogy_late.py
"""
from __future__ import annotations
import math
from collections import Counter as Tally
import simroot.pure as P
from sweep_full import MAXFEV, XTOL, load_problems
FLAT = ("fun3", "fun4", "fun5", "fun6")
_orig = P._analogous_and_steep
LOG = []
CALLS = [0]
def reason(cur, past):
if cur is None:
return "no-config", None
if not past:
return "no-memory", None
xi, phi = cur[0], cur[1]
best = min(past, key=lambda q: math.hypot(xi - q[0], phi - q[1]))
d_past = math.hypot(xi - best[0], phi - best[1])
d_line = abs(phi - xi) / math.sqrt(2.0)
if not d_past < d_line:
return "unlike-past", (d_past, d_line)
lx, lf = cur[2] / best[2], cur[3] / best[3]
m = math.log(lf) / math.log(lx) if lx not in (0.0, 1.0) and lf > 0.0 else math.nan
return ("ENTER" if m >= 2.0 else "m<2"), m
def spy(cur, past):
ok = _orig(cur, past)
why, val = reason(cur, past)
LOG.append((CALLS[0], why, val, len(past), ok))
return ok
def counted(f):
def g(x):
CALLS[0] += 1
return f(x)
return g
def run(f, a, b, gate):
st = {}
CALLS[0] = 0
_x, n, _ok = P.pure_root(counted(f), a, b, XTOL, MAXFEV, similarity="ratio",
scale="derived3", guard="project", prestep=gate, stats=st)
return n
def main():
P._analogous_and_steep = spy
tally = Tally()
ms_before = []
print(f"{'problem':<10} {'exit: n':>7} {'analog: n':>9} {'first flag @':>12} "
f"{'entry @':>8} reasons before entry")
for s, pid, f, a, b, _root in load_problems():
if s != "CHA" or pid.split(".")[0] not in FLAT:
continue
n_exit = run(f, a, b, "flatstickexit")
LOG.clear()
n_an = run(f, a, b, "flatanalog")
first = LOG[0][0] if LOG else None
entry = next((e for e, why, *_ in LOG if why == "ENTER"), None)
before = [(why, val) for e, why, val, _, _ in LOG if entry is None or e < entry]
for why, val in before:
tally[why] += 1
if why == "m<2" and val is not None and math.isfinite(val):
ms_before.append(val)
print(f"{pid:<10} {n_exit:7d} {n_an:9d} {str(first):>12} {str(entry):>8} "
+ " ".join(why for why, _ in before))
print("\nreasons at flag moments before entry, all 20 problems:", dict(tally))
if ms_before:
ms = sorted(ms_before)
print("m read at analogous-but-refused moments:",
" ".join(f"{m:.2f}" for m in ms))
print("diag done")
if __name__ == "__main__":
main()