saturate.py
Cited on: FLUSH.
To run it: save saturate.py, with flush.py, induct.py, ostrowski.py, tie.py beside it; standard library only.
"""
saturate.py -- is the pinned closure every valid key of one carry
interval per phase and flag?
QUESTION. induct.py's S_PIN (the forward closure of the pinned keys'
valid moves under every reply and every valid move) gives c_pin =
c_inf, and with TWIN c_saf <= c_inf, at 48 cells. At c_inf, each
phase t and flag f, is S_PIN every
valid lattice key (G, H, f, t) with G in [G_lo, G_top](t, f), S_PIN's
own least and greatest G there?
DESIGN (frozen before the engine).
Valid: y = H - G r_t in the closed range Window.V[(t, f)], decided
exactly by Window.ok. For G in [G_lo, G_top], H runs over floor(lo + G
r_t) - 1 .. ceil(hi + G r_t) + 1. c_inf is taken as induct.py's c_pin
(least c with the pinned moves and WI on S_PIN), equal to the pinned
game's c_inf at all 48 cells (induct.py), so no game is solved.
PREDICTION: saturation at all 48 cells. KILL: "missing valid keys
inside [G_lo, G_top]" > 0 at a cell; a (t, f) with no key of S_PIN is
printed empty, never counted. CONTROL: S_PIN with one key removed by
hand, its G strictly inside (G_lo, G_top) at its (t, f), must print
missing >= 1 at every cell with such a key. SIDE PRINTS: G_lo, G_top
per (t, f); the pre-reads' valid landings with G outside [G_lo, G_top]
of their flag at phase 0, and the reduced twins against the full
flag's range; G_lo(t, True) = 0 at every phase.
FINDINGS (the --wide run, copied from its prints).
KILL MISSED. Missing valid keys inside [G_lo, G_top]: 0 keys at 0 of
48 cells; every (t, f) prints S's count equal to the valid count
(golden x2: t0R [6, 9] 3/3, t0F [0, 5] 4/4; bronze x9: t0R [980,
3237] 841/841, t0F [0, 980] 358/358). Keys of S outside the valid
enumeration: 0. So at each cell the pinned closure is, per phase
and flag, every valid key of one carry interval.
THE ENDS (side prints). G_lo(t, full) = 0 at every row (frozen).
The reduced range starts where the full one ends (printed after
the frozen run): G_lo(t, R) - G_top(t, F) over the 104 phases is
0 at 43, 1 at 33, 2 at 17 and 3 at 11 (silver x2 9 - 9, golden x2 6 - 5).
G_top(t, R) < G_top(t, F) at 0 phases.
LANDING. Valid landings with G outside their flag's range at phase
0: 28, at 28 cells; landings off S on the reduced flag: 28, all
inside the full flag's range. The two counts are printed apart;
that they are the same 28 keys follows from saturation (a valid
landing inside its flag's range lies in S) with the equal counts,
and is not printed.
CONTROLS. One interior key removed prints missing >= 1 at 48 of 48
cells: caught by construction, given 0 keys of S outside the
valid enumeration, so it reads only missing()'s bookkeeping. The
H window holds the exact valid range ceil(lo + G r_t) .. floor(hi
+ G r_t) by construction, so the kill's only blind side is the
float wall; the H WINDOW control (added after the frozen run)
reads that: searched 10 past floor and ceiling it adds 0
keys. Its narrow plant (-1) drops 208 keys over the 208 (cell,
phase, flag) rows and reads nothing of the kill. (t, f) rows
holding no key of S: 0, so no row was skipped.
RUN RECORD. Bare 0.3 s, --wide 1.2 s with the H WINDOW control, no
game solved. The first run (bare, m = 2 .. 5) had the side print "keys
of S outside the valid enumeration" count S's keys of every (t, f)
against each row (18460); fixed to the row's own keys, it prints 0; the
kill and the control were unchanged. THE ENDS print was added after
that run. A name reused in the examples loop broke THE ENDS print
whenever the kill fired (TypeError, read by forcing one interior key
out of S in a scratch copy); renamed, the forced run prints the kill at
24 of 24 cells and THE ENDS whole.
Usage: python saturate.py [--wide] (--wide adds m = 6 .. 9)
"""
import math
import os
import sys
import time
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
from flush import PERIODIC # noqa: E402
from induct import Field, Window, least # noqa: E402
WIDE = "--wide" in sys.argv
def valid_keys(win, F, t, f, g_lo, g_top, pad=1):
"""Every valid lattice key at (t, f) with G in [g_lo, g_top], H
searched pad beyond the range's floor and ceiling."""
lo, hi = win.V[(t, f)]
out = set()
for G in range(g_lo, g_top + 1):
base = F.r[t] * G
for H in range(math.floor((lo + base).f) - pad,
math.ceil((hi + base).f) + pad + 1):
if win.ok(G, H, f, t):
out.add((G, H, f, t))
return out
def ranges(S, p):
"""{(t, f): (G_lo, G_top)} over the keys of S."""
out = {}
for G, _H, f, t in S:
lo, hi = out.get((t, f), (G, G))
out[(t, f)] = (min(lo, G), max(hi, G))
return out
def missing(win, F, S):
"""[(t, f, n missing, n valid)] per (t, f) holding a key of S."""
rows = []
for (t, f), (g0, g1) in sorted(ranges(S, F.p).items()):
full = valid_keys(win, F, t, f, g0, g1)
mine = {k for k in S if k[2] == f and k[3] == t}
rows.append((t, f, len(full - S), len(full), g0, g1,
len(mine - full)))
return rows
def main():
t0 = time.time()
maps = (2, 3, 4, 5) + ((6, 7, 8, 9) if WIDE else ())
cells = kill_cells = kill_keys = 0
ctl_cells = ctl_hit = 0
off_valid = 0
floor_bad = 0
land_out = twin_in_full = twin_n = 0
land_out_cells = []
examples = []
gaps, tops_bad = [], 0
wide_add = narrow_drop = empty_rows = 0
for name, per in PERIODIC:
F = Field(per)
for m in maps:
c = least(F, m, "pin")
win = Window(F, m, c)
st, _miss = win.pin_starts()
_ok, S, _bad = win.closure(st)
rows = missing(win, F, S)
n_miss = sum(r[2] for r in rows)
off_valid += sum(r[6] for r in rows)
cells += 1
kill_cells += n_miss > 0
kill_keys += n_miss
floor_bad += sum(r[4] != 0 for r in rows if r[1])
if n_miss and len(examples) < 8:
for t, f, nm, nv, g0, g1, _x in rows:
if nm:
holes = sorted(valid_keys(win, F, t, f, g0, g1) - S)
examples.append(f" {name:20s} x{m} t {t} "
f"{'full' if f else 'red.'}: {nm} "
f"of {nv} missing, G in [{g0}, "
f"{g1}], first {holes[:3]}")
break
rng = ranges(S, F.p)
empty_rows += sum((t, f) not in rng for t in range(F.p)
for f in (True, False))
for (t, f), (g0, g1) in rng.items():
base_k = valid_keys(win, F, t, f, g0, g1)
wide_add += len(valid_keys(win, F, t, f, g0, g1, 10)
- base_k)
narrow_drop += len(base_k - valid_keys(win, F, t, f, g0, g1,
-1))
for t in range(F.p):
if (t, True) in rng and (t, False) in rng:
gaps.append(rng[(t, False)][0] - rng[(t, True)][1])
tops_bad += rng[(t, False)][1] < rng[(t, True)][1]
# control: drop one interior key
inner = sorted(k for k in S
if rng[(k[3], k[2])][0] < k[0]
< rng[(k[3], k[2])][1])
if inner:
ctl_cells += 1
drop = inner[len(inner) // 2]
S2 = S - {drop}
ctl_hit += sum(r[2] for r in missing(win, F, S2)) >= 1
# LANDING side print
for digs, N, _P in win.prereads():
f = digs[-1] == 0
for G, H in win.landings(N, f):
k = (G, H, f, 0)
r = rng.get((0, f))
if r is None or not r[0] <= G <= r[1]:
land_out += 1
if f"{name.split()[0]} x{m}" not in land_out_cells:
land_out_cells.append(f"{name.split()[0]} x{m}")
if k not in S and not f:
twin_n += 1
rf = rng.get((0, True))
twin_in_full += bool(rf and rf[0] <= G <= rf[1])
lines = " ".join(
f"t{t}{'F' if f else 'R'} [{g0},{g1}] {nv - nm}/{nv}"
for t, f, nm, nv, g0, g1, _x in rows)
print(f" {name:20s} x{m} c {c} |S| {len(S)} {lines}",
flush=True)
print(f" ... {name} done, {time.time() - t0:.1f} s", flush=True)
print("== SATURATION: the pinned closure against the valid keys of its "
"carry intervals")
print(f" CONTROL: one interior key removed prints missing >= 1 at "
f"{ctl_hit} of {ctl_cells} cells with such a key")
print(f" keys of S outside the valid enumeration (must be 0): "
f"{off_valid}")
print(f" H WINDOW: searched 10 past floor and ceiling it adds "
f"{wide_add} keys (must be 0); the planted narrow window (-1) "
f"drops {narrow_drop} (must be > 0)")
print(f" (t, f) rows holding no key of S: {empty_rows}")
print(f" G_lo(t, full) != 0 at {floor_bad} (t, full) rows")
print(f" G_lo(t, R) - G_top(t, F) over {len(gaps)} phases: "
f"{sorted(set(gaps))}, counts "
f"{[gaps.count(g) for g in sorted(set(gaps))]}; "
f"G_top(t, R) < G_top(t, F) at {tops_bad}")
print(f" LANDING: valid landings with G outside their flag's range at "
f"phase 0: {land_out} (cells {', '.join(land_out_cells) or '-'});"
f" landings off S on the reduced flag {twin_n}, of them inside "
f"the full flag's range {twin_in_full}")
if examples:
print(" missing keys, first cells:")
print("\n".join(examples))
print(f" KILL: missing valid keys inside [G_lo, G_top]: {kill_keys} "
f"keys at {kill_cells} of {cells} cells")
print(f" {time.time() - t0:.1f} s")
if __name__ == "__main__":
main()