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
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
- **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.
- **Branching under the restriction.** No-separating-triangle branching tiles need
larger `n` (every inner face ≥ 4). Find the smallest `n` that has them and test the
uniform family there — the genuine conjecture case.
- **Does the restricted uniform family persist?** It holds for `n ≤ 13`; a single
later failure (as in the unrestricted n=12 case) would be very informative. Push to
n=14, 16.
- **`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 kempe_valid_colorings import classify_colorings
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
@@ -64,6 +65,8 @@ def run(args):
for g in generate(n, min_up_teeth=3, dedup=True):
if not inner_faces(g):
continue
if args.no_tri and has_length3_boundary(g):
continue
t = Tile(g)
tiles.append(t)
# marginal realisable sets per boundary (for domain restriction)
@@ -135,6 +138,9 @@ def run(args):
def main():
parser = argparse.ArgumentParser(description=__doc__)
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())
@@ -24,10 +24,23 @@ from kempe_valid_colorings import classify_colorings
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)."""
boundaries: dict[int, list[set]] = defaultdict(list)
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]
if not valid:
continue
@@ -59,9 +72,12 @@ 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")
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):
t0 = time.time()
res = per_size_universals(n)
res = per_size_universals(n, no_tri=args.no_tri)
dt = time.time() - t0
cells = []
for m in sorted(res):
@@ -77,6 +93,9 @@ 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)
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())