From b7271a1ce4e4efeb1f7464bcd59a8f3021ea4933 Mon Sep 17 00:00:00 2001 From: Valeri Sitnikov Date: Mon, 21 Sep 2026 14:58:20 +0100 Subject: [PATCH] Why the analogy entry is late on the flat families (diagnostics only, method unchanged). At flag moments before entry: unlike-past 118, no-memory 7, m<2 2 - the order is not the bottleneck. The period-4 cycle: the flag rises in the phases whose configuration does NOT recur (the undershoot whose relative step shrinks every cycle, and the crossing); the recurring phases have the flag down and read the true order (fun4 = 6(x-2)^5: m ~ 4.9). The entry demands both at the same state. Remembering the analogy until the next flag: flat entry state 12.9 -> 8.2; problems entered APS 3 -> 8, fun9 0 -> 1. Co-Authored-By: Claude Opus 5 --- diag_analogy_decoupled.py | 69 ++++++++++++++++++++++++ diag_analogy_late.py | 99 +++++++++++++++++++++++++++++++++++ diag_analogy_sequence.py | 58 ++++++++++++++++++++ results_diag_analogy_late.txt | 36 +++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 diag_analogy_decoupled.py create mode 100644 diag_analogy_late.py create mode 100644 diag_analogy_sequence.py create mode 100644 results_diag_analogy_late.txt diff --git a/diag_analogy_decoupled.py b/diag_analogy_decoupled.py new file mode 100644 index 0000000..50e6f20 --- /dev/null +++ b/diag_analogy_decoupled.py @@ -0,0 +1,69 @@ +"""Consequence check for diag_analogy_sequence.py: the flag rises in the phases of the +period-4 cycle whose configuration does NOT recur (the undershoot whose relative step +shrinks every cycle, the crossing), while the recurring phases are the ones with the flag +down. The entry demands both AT THE SAME STATE, so it waits until a flag state happens +to recur. + +Candidate (not in the method): the analogy is a property of the RUN, the flag of the +MOMENT - once any state has been analogous to its past with m >= 2, enter at the next +flag. Per problem, on traces of the proposed rule with NO regime (so both rules see the +same states): the state of entry under 'same state' and under 'analogy remembered', and +how many problems would enter at all, per group. Nothing in the method changes. + + python -u diag_analogy_decoupled.py +""" +from __future__ import annotations + +from collections import defaultdict + +from simroot.pure import _analogous_and_steep, _config3, pure_root +from sweep_full import MAXFEV, XTOL, load_problems + +FLAT = ("fun3", "fun4", "fun5", "fun6") + + +def entries(trace): + past, seen = [], False + same = remembered = None + for k, s in enumerate(trace): + cur = _config3(s["x"], s["fx"], s["xp"], s["fp"], s["xp2"], s["fp2"]) + flag = s["fp"] != 0.0 and s["fx"] / s["fp"] >= 0.25 + ana = _analogous_and_steep(cur, past) + if flag and ana and same is None: + same = k + if flag and (seen or ana) and remembered is None: + remembered = k + seen = seen or ana + if cur is not None: + past.append(cur) + return same, remembered + + +def main(): + per = defaultdict(lambda: [0, 0, 0, 0, 0]) # problems, same, remembered, sum same, sum rem + for s, pid, f, a, b, _root in load_problems(): + st = {"trace": []} + pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3", + guard="project", stats=st) + fam = pid.split(".")[0] + g = "APS" if s == "APS" else ("CHA-flat" if fam in FLAT else f"CHA-{fam}") + same, rem = entries(st["trace"]) + p = per[g] + p[0] += 1 + p[1] += same is not None + p[2] += rem is not None + if g == "CHA-flat" and same is not None and rem is not None: + p[3] += same + p[4] += rem + print(f"{'group':<10} {'problems':>8} {'enter: same state':>18} {'analogy remembered':>19}") + for g in sorted(per): + n, a_, b_, s1, s2 = per[g] + print(f"{g:<10} {n:8d} {a_:18d} {b_:19d}") + fl = per["CHA-flat"] + print(f"\nflat: mean entry state {fl[3] / fl[1]:.1f} (same state) -> " + f"{fl[4] / fl[1]:.1f} (analogy remembered)") + print("diag done") + + +if __name__ == "__main__": + main() diff --git a/diag_analogy_late.py b/diag_analogy_late.py new file mode 100644 index 0000000..85a2b45 --- /dev/null +++ b/diag_analogy_late.py @@ -0,0 +1,99 @@ +"""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() diff --git a/diag_analogy_sequence.py b/diag_analogy_sequence.py new file mode 100644 index 0000000..35c0d92 --- /dev/null +++ b/diag_analogy_sequence.py @@ -0,0 +1,58 @@ +"""The state-by-state picture behind diag_analogy_late.py, for a few flat problems. + +Every state the gate sees (trace hook, flatanalog, projection), in order: + n evaluations spent (the trace does not carry it; states are numbered instead) + flag r = f/f_prev >= 1/4 (the promise failed, sign kept) + xi, phi the configuration + ratio d_past / d_line (< 1 means analogous to the nearest past configuration) + lag how many states back that nearest past configuration is + m the pair's similarity exponent +Nothing in the method changes. + + python -u diag_analogy_sequence.py fun6.3 fun4.1 fun3.4 +""" +from __future__ import annotations + +import math +import sys + +from simroot.pure import _config3, pure_root +from sweep_full import MAXFEV, XTOL, load_problems + + +def show(pid, f, a, b): + st = {"trace": []} + pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3", guard="project", + prestep="flatanalog", stats=st) + print(f"\n== {pid} (state, flag, xi, phi, d_past/d_line, lag, m)") + past = [] + for k, s in enumerate(st["trace"][:30]): + cur = _config3(s["x"], s["fx"], s["xp"], s["fp"], s["xp2"], s["fp2"]) + flag = s["fp"] != 0.0 and s["fx"] / s["fp"] >= 0.25 + if cur is None: + print(f" {k:3d} {'F' if flag else '.'} (no configuration)") + continue + line = abs(cur[1] - cur[0]) / math.sqrt(2.0) + if past: + j = min(range(len(past)), key=lambda i: math.hypot(cur[0] - past[i][0], + cur[1] - past[i][1])) + q = past[j] + ratio = math.hypot(cur[0] - q[0], cur[1] - q[1]) / line if line > 0 else math.inf + lx, lf = cur[2] / q[2], cur[3] / q[3] + m = (math.log(lf) / math.log(lx)) if lx not in (0.0, 1.0) and lf > 0 else math.nan + lag = len(past) - j + print(f" {k:3d} {'F' if flag else '.'} xi {cur[0]:+9.4f} phi {cur[1]:+10.4f} " + f"ratio {ratio:7.3f} lag {lag:2d} m {m:6.2f}") + else: + print(f" {k:3d} {'F' if flag else '.'} xi {cur[0]:+9.4f} phi {cur[1]:+10.4f} (no memory)") + past.append(cur) + + +def main(ids): + for s, pid, f, a, b, _root in load_problems(): + if s == "CHA" and pid in ids: + show(pid, f, a, b) + + +if __name__ == "__main__": + main(sys.argv[1:] or ["fun6.3", "fun4.1"]) diff --git a/results_diag_analogy_late.txt b/results_diag_analogy_late.txt new file mode 100644 index 0000000..79eff7c --- /dev/null +++ b/results_diag_analogy_late.txt @@ -0,0 +1,36 @@ +problem exit: n analog: n first flag @ entry @ reasons before entry +fun3.1 46 49 4 11 unlike-past unlike-past unlike-past unlike-past +fun3.2 48 53 4 16 unlike-past unlike-past unlike-past +fun3.3 58 71 4 17 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun3.4 64 82 4 22 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun3.5 77 76 6 12 unlike-past unlike-past unlike-past +fun4.1 46 60 4 18 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun4.2 48 62 4 18 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun4.3 58 68 4 14 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun4.4 65 68 6 13 m<2 unlike-past unlike-past +fun4.5 78 82 6 13 m<2 unlike-past unlike-past +fun5.1 46 49 3 9 no-memory unlike-past unlike-past unlike-past unlike-past +fun5.2 46 50 3 9 no-memory unlike-past unlike-past unlike-past unlike-past +fun5.3 47 50 3 9 no-memory unlike-past unlike-past unlike-past unlike-past +fun5.4 49 52 3 9 no-memory unlike-past unlike-past unlike-past unlike-past +fun5.5 50 53 3 9 no-memory unlike-past unlike-past unlike-past unlike-past +fun6.1 39 58 3 18 no-memory unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun6.2 46 59 3 16 no-memory unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun6.3 47 63 4 22 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun6.4 49 66 4 22 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past +fun6.5 50 67 4 22 unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past unlike-past + +reasons at flag moments before entry, all 20 problems: {'unlike-past': 118, 'm<2': 2, 'no-memory': 7} +m read at analogous-but-refused moments: 0.92 0.92 +diag done +group problems enter: same state analogy remembered +APS 154 3 8 +CHA-flat 20 20 20 +CHA-fun1 5 1 1 +CHA-fun2 5 0 0 +CHA-fun7 5 3 3 +CHA-fun8 5 0 0 +CHA-fun9 5 0 1 + +flat: mean entry state 12.9 (same state) -> 8.2 (analogy remembered) +diag done