100 lines
3.4 KiB
Python
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()
|