No-separating-triangle restriction removes the chained-seam obstruction

Add --no-tri filter (exclude tiles with a length-3 boundary = separating/non-facial
triangle in G: outer rim of 3 up teeth, or an inner face of exactly 3 singleton
downs) to the trend and uniform-family probes.

The n=12 breaker UUUDUDUDUDUD bite=(3,11) has a size-3 inner face (encloses d5,d7,d9)
and is excluded. With the restriction the size-7 universal at n=12 is restored
(|D[7]| 0->2), every |D[m]|>=1 across n=6..13, and the uniform-family CSP becomes
FEASIBLE at n=12 with the simplest family (monochromatic on even sizes, min-cut on
odd). So the only universal failure was an artifact of admitting non-4-connected
configs; on the 4CT-relevant class gluing is constructively trivial in range.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:21:26 -04:00
parent 1aa76a5226
commit bacbdaaf26
3 changed files with 68 additions and 9 deletions
@@ -104,12 +104,46 @@ 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 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. branch-coupled selection around them, not in any single seam or in a scaling trend.
## Finding 5 — restricting to no separating triangles REMOVES the obstruction
The 4CT reduces to triangulations with no separating triangles (internally
4-connected). In the tread model, a separating (non-facial) triangle in `G` shows up
as a **length-3 boundary walk** of a tread: an outer rim of 3 up teeth, or an inner
face holding exactly 3 singleton down teeth (the `012` seam). Restricting to tiles
with **no length-3 boundary** (`kempe_universal_trend_probe.py --no-tri`,
`kempe_uniform_family_probe.py --no-tri`):
- The n=12 breaker `UUUDUDUDUDUD` bite=(3,11) has a size-3 inner face (the bite
encloses exactly `d5,d7,d9`), so it is **excluded** — along with its kin.
- The size-7 universal at n=12 is **restored**: `|D[7]|` goes `0 → 2`, on a reduced
population (211 → 76 size-7 boundaries). Across `n = 6..13` and all sizes, **every
`|D[m]| ≥ 1`** — no empty universal anywhere.
- The uniform-family CSP becomes **FEASIBLE at n=12** (was infeasible). The threading
family is now the simplest one — **monochromatic on even sizes, min-cut on odd**:
```
σ4 = 0000 σ5 = 00012 σ6 = 000000 σ7 = 0000012 σ8 = 00000000
```
(Without the restriction, `σ4` had to be `0011`, not monochromatic, because
separating-triangle tiles blocked the all-0 rim. Removing them lets monochromatic
even seams work.)
So the **only** universal failure we found (n=12, size 7) was an artifact of admitting
tiles that correspond to **non-4-connected** triangulations. On the 4CT-relevant class
(no separating triangles), the uniform seam family exists and gluing is constructively
trivial throughout the tested range — no pigeonhole needed.
Caveat: even at n=12 the restricted population has **0 branching tiles** (multi-inner
faces all require a size-3 face at this n), so the branching case stays untested under
the restriction; and this is `n ≤ 13` only.
## Open threads ## Open threads
- **Why n=12 / why this tile?** The breaker is the most-alternating 7-up word with an - **Branching under the restriction.** No-separating-triangle branching tiles need
antipodal bite. Is the sporadic failure tied to `n` even / specific bite spans? larger `n` (every inner face ≥ 4). Find the smallest `n` that has them and test the
Worth checking n=14, 16 for size-7 (and other) re-failures. uniform family there — the genuine conjecture case.
- **CSP at n=13.** With `|D[m]| > 0` for every size, the per-size obstruction is gone; - **Does the restricted uniform family persist?** It holds for `n ≤ 13`; a single
whether the joint uniform-family CSP is feasible again at n=13 needs a run. later failure (as in the unrestricted n=12 case) would be very informative. Push to
- **Branch-tree composition.** n=12 has 175 true branching tiles; composing `R_T` n=14, 16.
along actual trees (not just per-size uniformity) is the conjecture proper. - **`R_T` composition along real trees** rather than per-size uniformity.
@@ -26,6 +26,7 @@ from collections import defaultdict
from full_medial_tire_generator import generate, innermost_bite from full_medial_tire_generator import generate, innermost_bite
from kempe_valid_colorings import classify_colorings from kempe_valid_colorings import classify_colorings
from kempe_transfer_relation_probe import necklace, cuts from kempe_transfer_relation_probe import necklace, cuts
from kempe_universal_trend_probe import has_length3_boundary
from kempe_up_tooth_sequences import seq_str from kempe_up_tooth_sequences import seq_str
@@ -64,6 +65,8 @@ def run(args):
for g in generate(n, min_up_teeth=3, dedup=True): for g in generate(n, min_up_teeth=3, dedup=True):
if not inner_faces(g): if not inner_faces(g):
continue continue
if args.no_tri and has_length3_boundary(g):
continue
t = Tile(g) t = Tile(g)
tiles.append(t) tiles.append(t)
# marginal realisable sets per boundary (for domain restriction) # marginal realisable sets per boundary (for domain restriction)
@@ -135,6 +138,9 @@ def run(args):
def main(): def main():
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--n", type=int, default=9) parser.add_argument("--n", type=int, default=9)
parser.add_argument("--no-tri", action="store_true",
help="exclude tiles with a length-3 boundary "
"(no separating triangle in G)")
run(parser.parse_args()) run(parser.parse_args())
@@ -24,10 +24,23 @@ from kempe_valid_colorings import classify_colorings
from kempe_transfer_relation_probe import necklace, admissible_necklaces from kempe_transfer_relation_probe import necklace, admissible_necklaces
def per_size_universals(n: int): def has_length3_boundary(g) -> bool:
"""A length-3 boundary walk = a separating (non-facial) triangle in G:
outer rim of 3 up teeth, or an inner face holding exactly 3 singleton downs."""
if len(g.up_edges) == 3:
return True
faces = defaultdict(list)
for e in g.singleton_down_edges:
faces[innermost_bite(e, g.bites)].append(e)
return any(len(es) == 3 for es in faces.values())
def per_size_universals(n: int, no_tri: bool = False):
"""size m -> (num boundaries, |D[m]|, best coverage count).""" """size m -> (num boundaries, |D[m]|, best coverage count)."""
boundaries: dict[int, list[set]] = defaultdict(list) boundaries: dict[int, list[set]] = defaultdict(list)
for g in generate(n, min_up_teeth=3, dedup=True): for g in generate(n, min_up_teeth=3, dedup=True):
if no_tri and has_length3_boundary(g):
continue
valid = [c for c, v in classify_colorings(g, dedup_colors=True) if v.valid] valid = [c for c, v in classify_colorings(g, dedup_colors=True) if v.valid]
if not valid: if not valid:
continue continue
@@ -59,9 +72,12 @@ def run(args):
print("|D[m]| = number of universal boundary necklaces of size m " print("|D[m]| = number of universal boundary necklaces of size m "
"(0 => uniform shortcut dead at that size)\n") "(0 => uniform shortcut dead at that size)\n")
print("legend per cell: |D[m]| (best_coverage/num_boundaries)\n") print("legend per cell: |D[m]| (best_coverage/num_boundaries)\n")
if args.no_tri:
print("[restricted to tiles with NO length-3 boundary "
"(no separating triangle in G)]\n")
for n in range(args.min_n, args.max_n + 1): for n in range(args.min_n, args.max_n + 1):
t0 = time.time() t0 = time.time()
res = per_size_universals(n) res = per_size_universals(n, no_tri=args.no_tri)
dt = time.time() - t0 dt = time.time() - t0
cells = [] cells = []
for m in sorted(res): for m in sorted(res):
@@ -77,6 +93,9 @@ def main():
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--min-n", type=int, default=6) parser.add_argument("--min-n", type=int, default=6)
parser.add_argument("--max-n", type=int, default=12) parser.add_argument("--max-n", type=int, default=12)
parser.add_argument("--no-tri", action="store_true",
help="exclude tiles with a length-3 boundary "
"(no separating triangle in G)")
run(parser.parse_args()) run(parser.parse_args())