diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/chained_seam_findings.md b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/chained_seam_findings.md index f23fed6..18343ab 100644 --- a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/chained_seam_findings.md +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/chained_seam_findings.md @@ -56,23 +56,60 @@ cycle `σ_m` glues any tree with no pigeonhole. Per-size universal domain sizes at n=12: `3:1 4:1 5:1 6:2 7:0 8:4 9:1`. -## Finding 3 — interpretation +## Finding 3 — the failure is SPORADIC, not a monotone trend -Universals vanish as the tile population grows (more boundaries of a given size → more -sets to intersect → empty), so the "paint every seam identically" shortcut is doomed -at scale; the per-interface pigeonhole choice is genuinely necessary. **But this is -not an obstruction to gluing** — pairwise overlap still always holds, so chains still -glue by choosing states per interface. We have *located why the conjecture is hard*: -the difficulty lives in the non-uniform, branch-coupled selection across the tree, -exactly where the paper's pigeonhole sits — not in any single seam or a uniform -assignment. +`kempe_universal_trend_probe.py` tracks `|D[m]|` (universal count) per size across +`n = 6..13`. Legend `|D[m]| (best_coverage / num_boundaries)`; `*` = odd size: + +``` +n= 6: m3*=1(6/6) m4=2(2/2) m6=4(1/1) +n= 7: m3*=1(10/10) m4=1(8/8) m5*=1(2/2) m7*=3(1/1) +n= 8: m3*=1(28/28) m4=2(21/21) m5*=1(10/10) m6=2(3/3) m8=8(1/1) +n= 9: m3*=1(49/49) m4=1(52/52) m5*=1(28/28) m6=1(14/14) m7*=2(3/3) m9*=8(1/1) +n=10: m3*=1(118) m4=2(106) m5*=1(86) m6=2(46) m7*=1(16/16) m8=4(4/4) m10=18(1/1) +n=11: m3*=1(310) m4=1(263) m5*=1(188) m6=1(139) m7*=2(60/60) m8=2(20/20) m9*=3(4/4) m11*=21(1/1) +n=12: m3*=1(849) m4=1(691) m5*=1(534) m6=2(353) m7*=0(210/211) m8=3(88) m9*=1(24/24) m10=6(5/5) m12=48(1/1) +n=13: m3*=1(2457) m4=1(1805) m5*=1(1386) m6=1(1070) m7*=2(579/579) m8=1(315) m9*=2(110) m10=1(28) m11*=7(5) m13*=63(1/1) +``` + +The earlier "universals vanish as the tile population grows" reading is **wrong**. +Across all `n = 6..13` and all sizes, the **only** empty universal anywhere is +`(n=12, m=7)`. Size 7 has *more* boundaries at n=13 (579) than n=12 (211), yet its +universal is back (`|D|=2`). So the failure is **sporadic**, not monotone — a +number-theoretic quirk of `n=12`, not a scaling law. Note also that best_coverage is +always near-total: the most-shared necklace reaches all-but-rarely-one boundary, so +the universal is "almost there" even when it fails. + +### The single breaker (n=12, m=7) +The lone size-7 boundary that kills the universal is the **outer rim** of + +``` +word = UUUDUDUDUDUD bite = (3,11) (7 up teeth) +``` + +— the most-alternating word with a near-full-span bite. Its outer rim realises 9 of +the 10 admissible size-7 necklaces, missing exactly `0001112` (the balanced two-block +`3+3+1` pattern); every other size-7 boundary realises `0001112`. One exceptional tile +breaks the shortcut. + +## Finding 4 — interpretation + +The uniform "paint every seam the same" shortcut **almost always works** (universals +non-empty with near-total coverage, even at thousands of boundaries), but is +**fragile**: a single exceptional tile can empty a per-size universal, as at +`(n=12, m=7)`. So the shortcut cannot be relied on in general — yet its failures are +rare, not systematic. The per-interface pigeonhole choice is needed precisely to +absorb these sporadic breakers. **This is not an obstruction to gluing** — pairwise +overlap always holds, so chains still glue by choosing states per interface. The +conjecture's difficulty is thus concentrated in rare exceptional tiles and the +branch-coupled selection around them, not in any single seam or in a scaling trend. ## Open threads -- **Odd-size seams are the pressure point.** Uniformity first fails at size 7 (odd); - small odd sizes 3, 5 kept unique universals. Does every large odd size lose its - universal as `n` grows? (tracked below / in follow-up). -- **The 210/211 near-miss.** A single tile blocks the size-7 universal — identifying - the "universal-breaker" tiles may expose the combinatorial core. +- **Why n=12 / why this tile?** The breaker is the most-alternating 7-up word with an + antipodal bite. Is the sporadic failure tied to `n` even / specific bite spans? + Worth checking n=14, 16 for size-7 (and other) re-failures. +- **CSP at n=13.** With `|D[m]| > 0` for every size, the per-size obstruction is gone; + whether the joint uniform-family CSP is feasible again at n=13 needs a run. - **Branch-tree composition.** n=12 has 175 true branching tiles; composing `R_T` along actual trees (not just per-size uniformity) is the conjecture proper. diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/kempe_universal_trend_probe.py b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/kempe_universal_trend_probe.py new file mode 100644 index 0000000..bf860b7 --- /dev/null +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/kempe_universal_trend_probe.py @@ -0,0 +1,84 @@ +"""Do per-size universal boundary states survive as n grows? + +For each annular size n and each boundary size m, D[m] is the set of boundary +necklaces realisable on EVERY size-m boundary (outer up-rim or inner >=3-singleton +face) across all tiles. |D[m]| > 0 means a uniform state of size m exists; |D[m]|=0 +means no state threads every size-m seam (the uniform shortcut is dead at that size). + +This tracks |D[m]| as a function of n, to see whether/when each size -- especially +odd sizes -- loses its universal. Also reports, per (n,m), the best coverage +(how many of the size-m boundaries the most-shared necklace reaches). + +Run: python3 kempe_universal_trend_probe.py --min-n 6 --max-n 12 +""" + +from __future__ import annotations + +import argparse +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_transfer_relation_probe import necklace, admissible_necklaces + + +def per_size_universals(n: int): + """size m -> (num boundaries, |D[m]|, best coverage count).""" + boundaries: dict[int, list[set]] = defaultdict(list) + for g in generate(n, min_up_teeth=3, dedup=True): + valid = [c for c, v in classify_colorings(g, dedup_colors=True) if v.valid] + if not valid: + continue + p = len(g.up_edges) + if p >= 3: + boundaries[p].append( + {necklace(tuple(c[f"u{e}"] for e in g.up_edges)) for c in valid}) + faces = defaultdict(list) + for e in g.singleton_down_edges: + faces[innermost_bite(e, g.bites)].append(e) + for es in faces.values(): + if len(es) >= 3: + es = sorted(es) + boundaries[len(es)].append( + {necklace(tuple(c[f"d{e}"] for e in es)) for c in valid}) + out = {} + for m, sets in boundaries.items(): + inter = set.intersection(*sets) + cnt: dict = defaultdict(int) + for s in sets: + for x in s: + cnt[x] += 1 + best = max(cnt.values()) if cnt else 0 + out[m] = (len(sets), len(inter), best) + return out + + +def run(args): + print("|D[m]| = number of universal boundary necklaces of size m " + "(0 => uniform shortcut dead at that size)\n") + print("legend per cell: |D[m]| (best_coverage/num_boundaries)\n") + for n in range(args.min_n, args.max_n + 1): + t0 = time.time() + res = per_size_universals(n) + dt = time.time() - t0 + cells = [] + for m in sorted(res): + nb, dsz, best = res[m] + tag = "*" if (m % 2 == 1) else " " + cells.append(f"m{m}{tag}={dsz}({best}/{nb})") + print(f"n={n:>2} [{dt:>4.0f}s]: " + " ".join(cells)) + sys.stdout.flush() + print("\n(* = odd size)") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--min-n", type=int, default=6) + parser.add_argument("--max-n", type=int, default=12) + run(parser.parse_args()) + + +if __name__ == "__main__": + main()