Add interface-admissibility probe; confirm parity characterization at n=12
For each interface size m, compare the realized census vocabulary (outer up-tooth apexes and inner singleton-down apexes) against the full parity-admissible set. At n=12, m=3..8 every parity-admissible sequence is realized on both faces (counts 1,4,10,31,91,274; none missing), and up==down throughout -- the n=9 result is n-independent and scales to m=8. Validated against the known n=9 answer before running n=12. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
|||||||
|
"""Does the realized gluing-interface vocabulary still equal the full
|
||||||
|
parity-admissible set at larger n?
|
||||||
|
|
||||||
|
For a given n, and each interface size m, compare:
|
||||||
|
* REALIZED: the distinct canonical apex colour sequences (census reading,
|
||||||
|
i.e. orientation-honest) that Kempe-balanced colourings actually present on
|
||||||
|
an interface of m apexes -- separately for the outer face (up-tooth apexes)
|
||||||
|
and an inner face (singleton down-tooth apexes on one face);
|
||||||
|
* ADMISSIBLE: every colour-permutation-canonical length-m sequence whose three
|
||||||
|
colour counts share m's parity (the outer-face Kempe-parity necessary
|
||||||
|
condition).
|
||||||
|
|
||||||
|
At n=9 realized == admissible for every m (3..6), and up == down. This probe
|
||||||
|
checks whether that persists. Summary numbers only -- no notes, no figures.
|
||||||
|
|
||||||
|
Run: python3 kempe_interface_admissibility_probe.py --n 12 --m 3 4 5 6
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import itertools
|
||||||
|
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_up_tooth_sequences import (
|
||||||
|
canonical_sequence,
|
||||||
|
dihedral_reading_sequences,
|
||||||
|
seq_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def admissible_sequences(m: int) -> set[tuple[int, ...]]:
|
||||||
|
"""Every canonical length-m sequence with all three colour counts sharing
|
||||||
|
m's parity (the parity-admissible interface alphabet)."""
|
||||||
|
out: set[tuple[int, ...]] = set()
|
||||||
|
for combo in itertools.product((0, 1, 2), repeat=m):
|
||||||
|
counts = [combo.count(k) for k in (0, 1, 2)]
|
||||||
|
if all(c % 2 == m % 2 for c in counts):
|
||||||
|
out.add(canonical_sequence(combo))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def realized(n: int, m: int):
|
||||||
|
"""(up_set, down_set): census sequences realized on outer / inner interfaces
|
||||||
|
of exactly m apexes, over all Kempe-balanced colourings of all M(T)."""
|
||||||
|
up: set[tuple[int, ...]] = set()
|
||||||
|
down: set[tuple[int, ...]] = set()
|
||||||
|
up_graphs = down_configs = 0
|
||||||
|
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:
|
||||||
|
up_graphs += 1
|
||||||
|
for c in valid:
|
||||||
|
up |= dihedral_reading_sequences(n, c, g.up_edges, "u")
|
||||||
|
for edges in down_faces:
|
||||||
|
down_configs += 1
|
||||||
|
for c in valid:
|
||||||
|
down |= dihedral_reading_sequences(n, c, edges, "d")
|
||||||
|
return up, down, up_graphs, down_configs
|
||||||
|
|
||||||
|
|
||||||
|
def run(args):
|
||||||
|
n = args.n
|
||||||
|
print(f"n={n}: realized gluing-interface vocabulary vs full parity-admissible set\n")
|
||||||
|
header = f"{'m':>2} {'admiss':>7} | {'up real':>8} {'up=adm':>7} {'#M(T)':>6} | " \
|
||||||
|
f"{'dn real':>8} {'dn=adm':>7} {'#cfg':>6} | {'up=dn':>6} {'sec':>5}"
|
||||||
|
print(header)
|
||||||
|
print("-" * len(header))
|
||||||
|
for m in args.m:
|
||||||
|
t0 = time.time()
|
||||||
|
adm = admissible_sequences(m)
|
||||||
|
up, down, ng, nc = realized(n, m)
|
||||||
|
dt = time.time() - t0
|
||||||
|
print(f"{m:>2} {len(adm):>7} | {len(up):>8} {str(up == adm):>7} {ng:>6} | "
|
||||||
|
f"{len(down):>8} {str(down == adm):>7} {nc:>6} | "
|
||||||
|
f"{str(up == down):>6} {dt:>5.0f}")
|
||||||
|
sys.stdout.flush()
|
||||||
|
if up != adm:
|
||||||
|
missing = sorted(seq_str(s) for s in (adm - up))
|
||||||
|
print(f" up missing {len(missing)}: {missing[:12]}{' ...' if len(missing) > 12 else ''}")
|
||||||
|
if down != adm:
|
||||||
|
missing = sorted(seq_str(s) for s in (adm - down))
|
||||||
|
print(f" down missing {len(missing)}: {missing[:12]}{' ...' if len(missing) > 12 else ''}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--n", type=int, default=12)
|
||||||
|
parser.add_argument("--m", type=int, nargs="+", default=[3, 4, 5, 6])
|
||||||
|
run(parser.parse_args())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user