Add tile-overlap probe: per-tile interface subsets always glue

Each tile realises only a subset of the parity-admissible alphabet on its rim,
and tiles genuinely omit interfaces (n=12 m=8: max 273/274, min 43). But any
two tiles always glue: interface subsets always overlap (n=9 m=3-6, n=12 m=3-8)
-- usually via a global universal seam present on every inner+outer rim, and
where none exists (n=12 m=7) the worst pair still shares 14 seams. The universal
seams are the low-complexity ones (<=2 colours, single contiguous block). No
local gluing obstruction; any obstruction must be global across a nested stack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 22:52:38 -04:00
parent c56da7bb23
commit aecbc5ed28
@@ -0,0 +1,131 @@
"""Per-tile interface subsets and gluing overlap.
The achievable interface alphabet for size m is the full parity-admissible set
(see kempe_interface_admissibility_probe.py). But each individual tile realizes
only a SUBSET of it on its rim. Gluing tile A inside tile B along an m-seam
needs A's outer-rim subset and B's inner-face subset to share a common sequence
(census sets are already orientation-closed, so plain set intersection is the
right test).
For each n and interface size m this reports, separately for outer (up-tooth)
and inner (down-face) interfaces:
* subset-size distribution (how much of the alphabet each tile realizes);
* number of distinct subsets (= gluing "signatures");
* the universal sequences (in EVERY tile's subset) for up, for down, and the
global universal (in every up AND every down subset -- a seam any inner tile
can use inside any outer tile);
* whether every (inner up-rim, outer down-face) pair overlaps -- i.e. can any
two tiles be glued at this seam size -- and the worst (smallest-overlap) pair.
Summary numbers only.
Run: python3 kempe_tile_overlap_probe.py --n 9 --m 3 4 5 6
"""
from __future__ import annotations
import argparse
import statistics
import sys
import time
from collections import defaultdict
from full_medial_tire_generator import generate, innermost_bite
from kempe_valid_colorings import classify_colorings
from kempe_interface_admissibility_probe import admissible_sequences
from kempe_up_tooth_sequences import dihedral_reading_sequences, seq_str
def collect(n: int, m: int):
"""Per-tile up-subsets and down-subsets (census sequence sets) for size m."""
up_subsets: list[frozenset] = []
down_subsets: list[frozenset] = []
for g in generate(n, min_up_teeth=3, dedup=True):
do_up = len(g.up_edges) == m
faces = defaultdict(list)
for e in g.singleton_down_edges:
faces[innermost_bite(e, g.bites)].append(e)
down_faces = [sorted(es) for es in faces.values() if len(es) == m]
if not do_up and not down_faces:
continue
valid = [c for c, v in classify_colorings(g, dedup_colors=True) if v.valid]
if do_up:
s: set = set()
for c in valid:
s |= dihedral_reading_sequences(n, c, g.up_edges, "u")
up_subsets.append(frozenset(s))
for edges in down_faces:
s = set()
for c in valid:
s |= dihedral_reading_sequences(n, c, edges, "d")
down_subsets.append(frozenset(s))
return up_subsets, down_subsets
def min_pairwise_overlap(ups, downs):
"""Smallest |U ∩ D| over all (up, down) pairs, and a witnessing pair size."""
worst = None
for u in ups:
for d in downs:
k = len(u & d)
if worst is None or k < worst:
worst = k
if worst == 0:
return 0
return worst if worst is not None else None
def describe(label, subsets, adm):
sizes = sorted(len(s) for s in subsets)
universal = frozenset.intersection(*subsets) if subsets else frozenset()
n_full = sum(1 for s in subsets if len(s) == len(adm))
sigs = len(set(subsets))
return {
"n": len(subsets),
"min": sizes[0] if sizes else 0,
"med": int(statistics.median(sizes)) if sizes else 0,
"max": sizes[-1] if sizes else 0,
"sigs": sigs,
"universal": universal,
"n_full": n_full,
}
def run(args):
n = args.n
print(f"n={n}: per-tile interface subsets and gluing overlap\n")
for m in args.m:
t0 = time.time()
adm = admissible_sequences(m)
ups, downs = collect(n, m)
u = describe("up", ups, adm)
d = describe("down", downs, adm)
gu = (frozenset.intersection(*ups) if ups else frozenset()) & \
(frozenset.intersection(*downs) if downs else frozenset())
# can any inner tile glue inside any outer tile?
if gu:
glue = f"yes (global universal {sorted(seq_str(x) for x in gu)})"
else:
mo = min_pairwise_overlap(ups, downs)
glue = f"{'yes' if mo and mo > 0 else 'NO'} (min up×down overlap = {mo})"
dt = time.time() - t0
print(f"m={m} |adm|={len(adm)}")
print(f" up : {u['n']:>4} tiles subset size {u['min']}..{u['med']}..{u['max']}"
f" ({u['n_full']} realise all) {u['sigs']} signatures"
f" universal={sorted(seq_str(x) for x in u['universal'])}")
print(f" down : {d['n']:>4} tiles subset size {d['min']}..{d['med']}..{d['max']}"
f" ({d['n_full']} realise all) {d['sigs']} signatures"
f" universal={sorted(seq_str(x) for x in d['universal'])}")
print(f" glue-any-pair: {glue} ({dt:.0f}s)\n")
sys.stdout.flush()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--n", type=int, default=9)
parser.add_argument("--m", type=int, nargs="+", default=[3, 4, 5, 6])
run(parser.parse_args())
if __name__ == "__main__":
main()