Add Heawood boundary-restriction experiments and findings note
Experiments probing the cluster restriction set R_K / Phi: R_K is a Z/3 zonotope (not a GF(3) subspace), the "richness" invariant is an artifact of non-shrinking annuli, the interface gluing always works on interior cycles (forced by 4CT), and the maximal constraint achievable on an n-cycle is a floor of 2^(n-2) -- already reached by the trivial tire. Note boundary_restriction_structure.tex writes these up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+182
@@ -0,0 +1,182 @@
|
||||
"""
|
||||
Does the richness invariant survive BRANCHING?
|
||||
|
||||
For a separating cycle C bounding a disk G_C (away from the source), the achievable
|
||||
outer-interface set is
|
||||
|
||||
Phi(C) = { (lambda*(v))_{v in C} : lambda in {+1,-1}^{F(G_C)},
|
||||
sum_{f ∋ w} lambda(f) ≡ 0 for every
|
||||
truly-interior vertex w of G_C }.
|
||||
|
||||
This is the exact value the recursive transfer operator produces at C (interior
|
||||
consistency = all the descendant gluings already performed; seam/boundary vertices
|
||||
are deferred, exactly as in the recursion). We compute Phi(C) by constrained
|
||||
enumeration over real triangulations and test the candidate self-similar invariant
|
||||
|
||||
non-empty & closed under sign flip & full single-position marginals
|
||||
|
||||
separately at BRANCH nodes (region encloses >=2 disjoint deeper sub-tires) and at
|
||||
LINEAR nodes (one child), to see whether branching breaks it.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from collections import defaultdict, deque
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
from scipy.spatial import Delaunay
|
||||
|
||||
|
||||
def delaunay(n, rng):
|
||||
pts = rng.random((n, 2))
|
||||
tri = Delaunay(pts)
|
||||
faces = [tuple(int(x) for x in s) for s in tri.simplices]
|
||||
hull = set(int(v) for e in tri.convex_hull for v in e)
|
||||
return faces, hull
|
||||
|
||||
|
||||
def build(faces):
|
||||
adj = defaultdict(set)
|
||||
efaces = defaultdict(list)
|
||||
vfaces = defaultdict(list)
|
||||
for fi, (a, b, c) in enumerate(faces):
|
||||
adj[a] |= {b, c}; adj[b] |= {a, c}; adj[c] |= {a, b}
|
||||
for e in ((a, b), (b, c), (a, c)):
|
||||
efaces[frozenset(e)].append(fi)
|
||||
for v in (a, b, c):
|
||||
vfaces[v].append(fi)
|
||||
fadj = [set() for _ in faces]
|
||||
for fl in efaces.values():
|
||||
for i in fl:
|
||||
for j in fl:
|
||||
if i != j:
|
||||
fadj[i].add(j)
|
||||
return adj, fadj, vfaces
|
||||
|
||||
|
||||
def bfs(adj, src):
|
||||
lev = {src: 0}; q = deque([src])
|
||||
while q:
|
||||
u = q.popleft()
|
||||
for w in adj[u]:
|
||||
if w not in lev:
|
||||
lev[w] = lev[u] + 1; q.append(w)
|
||||
return lev
|
||||
|
||||
|
||||
def components(face_ids, fadj):
|
||||
idset = set(face_ids)
|
||||
seen = set(); comps = []
|
||||
for s in face_ids:
|
||||
if s in seen:
|
||||
continue
|
||||
comp = []; stack = [s]; seen.add(s)
|
||||
while stack:
|
||||
u = stack.pop(); comp.append(u)
|
||||
for w in fadj[u]:
|
||||
if w in idset and w not in seen:
|
||||
seen.add(w); stack.append(w)
|
||||
comps.append(comp)
|
||||
return comps
|
||||
|
||||
|
||||
def sign_closed(S):
|
||||
return all(tuple((3 - x) % 3 for x in s) in S for s in S)
|
||||
|
||||
|
||||
def marginals_full(S, k):
|
||||
return all({s[i] for s in S} == {0, 1, 2} for i in range(k))
|
||||
|
||||
|
||||
def phi_of_region(comp_faces, faces, vfaces, lev, d, cap):
|
||||
"""Constrained-enumeration Phi on the outer (level-d) cycle of a region."""
|
||||
Gc = comp_faces
|
||||
if not (1 <= len(Gc) <= cap):
|
||||
return None
|
||||
Gcset = set(Gc)
|
||||
verts = sorted(set(v for fi in Gc for v in faces[fi]))
|
||||
# truly-interior: every global incident face is inside G_C (=> level > d)
|
||||
interior = [v for v in verts if all(f in Gcset for f in vfaces[v])]
|
||||
boundary_C = [v for v in verts if lev[v] == d and v not in interior]
|
||||
if not boundary_C:
|
||||
return None
|
||||
F = len(Gc)
|
||||
fidx = {fi: j for j, fi in enumerate(Gc)}
|
||||
# incidence rows
|
||||
Bint = np.zeros((len(interior), F), dtype=np.int64)
|
||||
for r, w in enumerate(interior):
|
||||
for fi in vfaces[w]:
|
||||
if fi in Gcset:
|
||||
Bint[r, fidx[fi]] = 1
|
||||
Cinc = np.zeros((len(boundary_C), F), dtype=np.int64)
|
||||
for r, v in enumerate(boundary_C):
|
||||
for fi in vfaces[v]:
|
||||
if fi in Gcset:
|
||||
Cinc[r, fidx[fi]] = 1
|
||||
labs = np.array(list(product((1, 2), repeat=F)), dtype=np.int64)
|
||||
if len(interior):
|
||||
ok = np.all((labs @ Bint.T) % 3 == 0, axis=1)
|
||||
labs = labs[ok]
|
||||
if labs.shape[0] == 0:
|
||||
return set(), len(boundary_C)
|
||||
outer = (labs @ Cinc.T) % 3
|
||||
return set(map(tuple, np.unique(outer, axis=0))), len(boundary_C)
|
||||
|
||||
|
||||
def main():
|
||||
seed = int(sys.argv[1]) if len(sys.argv) > 1 else 0
|
||||
nprng = np.random.default_rng(seed)
|
||||
CAP = 18
|
||||
|
||||
stats = {True: [0, 0, 0, 0], False: [0, 0, 0, 0]} # branch: [n, nonempty, sign, marg]
|
||||
examples_fail = []
|
||||
|
||||
for _ in range(300):
|
||||
faces, hull = delaunay(int(nprng.integers(14, 34)), nprng)
|
||||
adj, fadj, vfaces = build(faces)
|
||||
lev = bfs(adj, min(hull))
|
||||
if len(lev) != len(adj):
|
||||
continue
|
||||
depth = [min(lev[v] for v in faces[fi]) for fi in range(len(faces))]
|
||||
maxd = max(depth)
|
||||
for d in range(1, maxd + 1):
|
||||
fge = [fi for fi in range(len(faces)) if depth[fi] >= d]
|
||||
for comp in components(fge, fadj):
|
||||
if not (1 <= len(comp) <= CAP):
|
||||
continue
|
||||
deeper = [fi for fi in comp if depth[fi] >= d + 1]
|
||||
n_children = len(components(deeper, fadj))
|
||||
is_branch = n_children >= 2
|
||||
res = phi_of_region(comp, faces, vfaces, lev, d, CAP)
|
||||
if res is None:
|
||||
continue
|
||||
S, k = res
|
||||
rec = stats[is_branch]
|
||||
rec[0] += 1
|
||||
rec[1] += bool(S)
|
||||
rec[2] += (bool(S) and sign_closed(S))
|
||||
rec[3] += (bool(S) and marginals_full(S, k))
|
||||
if S and not marginals_full(S, k) and len(examples_fail) < 6:
|
||||
examples_fail.append((is_branch, n_children, len(comp), k,
|
||||
len(S)))
|
||||
|
||||
for branch in (False, True):
|
||||
n, ne, sg, mg = stats[branch]
|
||||
tag = "BRANCH (>=2 children)" if branch else "LINEAR (1 child)"
|
||||
if n:
|
||||
print(f"{tag}: {n} regions")
|
||||
print(f" non-empty : {ne}/{n} ({100*ne/n:.1f}%)")
|
||||
print(f" sign-closed : {sg}/{n} ({100*sg/n:.1f}%)")
|
||||
print(f" marginals-full : {mg}/{n} ({100*mg/n:.1f}%)")
|
||||
else:
|
||||
print(f"{tag}: 0 regions")
|
||||
if examples_fail:
|
||||
print("\n marginals-NOT-full examples (branch?,n_children,|G_C|,|C|,|Phi|):")
|
||||
for e in examples_fail:
|
||||
print(f" {e}")
|
||||
else:
|
||||
print("\n richness (incl. full marginals) held on every region tested.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
"""
|
||||
Construct the triangulated disk (= nested tire substructure) that MAXIMALLY
|
||||
constrains its outer cycle.
|
||||
|
||||
For a triangulated disk D with boundary cycle C = (0..n-1), the achievable outer
|
||||
Heawood set is
|
||||
|
||||
Phi(D) = { (lambda*(v))_{v in C} : lambda in {+1,-1}^{faces},
|
||||
sum_{f ∋ w} lambda(f) ≡ 0 for every interior vertex w } .
|
||||
|
||||
Phi depends only on the disk triangulation (no BFS/tree needed). We want the disk
|
||||
minimising |Phi| -- the worst case for the pigeonhole. Note Phi is always
|
||||
sign-closed and non-empty, so |Phi| >= 1, and |Phi| = 1 forces Phi = { all-zeros }.
|
||||
|
||||
Key local fact: a degree-3 interior vertex (one Apollonian stack) has incident
|
||||
faces f1,f2,f3 with lambda(f1)+lambda(f2)+lambda(f3) ≡ 0 mod 3 over +/-1 values,
|
||||
which forces f1=f2=f3. So stacking chains equalities and collapses Phi.
|
||||
|
||||
We (a) randomly search disks built by Apollonian stacking, and (b) try a
|
||||
deterministic deep-stack construction, reporting the smallest Phi found.
|
||||
"""
|
||||
|
||||
import random
|
||||
import sys
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def fan_triangulation(n):
|
||||
"""n-gon (0..n-1) triangulated as a fan from vertex 0. No interior vertex."""
|
||||
return [(0, i, i + 1) for i in range(1, n - 1)]
|
||||
|
||||
|
||||
def stack(faces, idx, v):
|
||||
a, b, c = faces[idx]
|
||||
faces[idx] = (a, b, v)
|
||||
faces.append((b, c, v))
|
||||
faces.append((a, c, v))
|
||||
|
||||
|
||||
def phi(faces, n, cap):
|
||||
"""Phi on boundary 0..n-1; interior = vertices >= n."""
|
||||
verts = set(v for f in faces for v in f)
|
||||
interior = sorted(v for v in verts if v >= n)
|
||||
F = len(faces)
|
||||
if F > cap:
|
||||
return None
|
||||
# incidence
|
||||
Bint = np.zeros((len(interior), F), dtype=np.int64)
|
||||
iindex = {w: r for r, w in enumerate(interior)}
|
||||
Cinc = np.zeros((n, F), dtype=np.int64)
|
||||
for j, (a, b, c) in enumerate(faces):
|
||||
for v in (a, b, c):
|
||||
if v >= n:
|
||||
Bint[iindex[v], j] = 1
|
||||
else:
|
||||
Cinc[v, j] = 1
|
||||
labs = np.array(list(product((1, 2), repeat=F)), dtype=np.int64)
|
||||
if len(interior):
|
||||
keep = np.all((labs @ Bint.T) % 3 == 0, axis=1)
|
||||
labs = labs[keep]
|
||||
if labs.shape[0] == 0:
|
||||
return set()
|
||||
outer = (labs @ Cinc.T) % 3
|
||||
return set(map(tuple, np.unique(outer, axis=0)))
|
||||
|
||||
|
||||
def disp(s):
|
||||
return tuple(-1 if int(x) == 2 else int(x) for x in s)
|
||||
|
||||
|
||||
def gf3_rank(rows):
|
||||
M = [[int(x) % 3 for x in r] for r in rows]
|
||||
if not M:
|
||||
return 0
|
||||
nc = len(M[0]); r = 0
|
||||
for c in range(nc):
|
||||
piv = next((i for i in range(r, len(M)) if M[i][c] % 3), None)
|
||||
if piv is None:
|
||||
continue
|
||||
M[r], M[piv] = M[piv], M[r]
|
||||
inv = M[r][c] % 3
|
||||
M[r] = [(x * inv) % 3 for x in M[r]]
|
||||
for i in range(len(M)):
|
||||
if i != r and M[i][c] % 3:
|
||||
fct = M[i][c] % 3
|
||||
M[i] = [(M[i][k] - fct * M[r][k]) % 3 for k in range(nc)]
|
||||
r += 1
|
||||
if r == len(M):
|
||||
break
|
||||
return r
|
||||
|
||||
|
||||
def describe(P):
|
||||
P = list(P)
|
||||
sign_closed = all(tuple((3 - x) % 3 for x in s) in set(P) for s in P)
|
||||
s0 = P[0]
|
||||
D = [tuple((np.array(s) - np.array(s0)) % 3) for s in P]
|
||||
rank = gf3_rank(D)
|
||||
affine = (len(P) == 3 ** rank)
|
||||
pow2 = (len(P) & (len(P) - 1)) == 0
|
||||
return (f"sign-closed={sign_closed} affine-GF3={affine} "
|
||||
f"|Phi|={len(P)} (power-of-2={pow2}) hull-dim={rank}")
|
||||
|
||||
|
||||
def random_disk(n, n_stacks, rng):
|
||||
faces = fan_triangulation(n)
|
||||
nxt = n
|
||||
for _ in range(n_stacks):
|
||||
stack(faces, rng.randrange(len(faces)), nxt)
|
||||
nxt += 1
|
||||
return faces
|
||||
|
||||
|
||||
def deep_stack_disk(n, n_stacks):
|
||||
"""Always stack into the most-recently created face -> deep equality chain."""
|
||||
faces = fan_triangulation(n)
|
||||
nxt = n
|
||||
for _ in range(n_stacks):
|
||||
stack(faces, len(faces) - 1, nxt)
|
||||
nxt += 1
|
||||
return faces
|
||||
|
||||
|
||||
def search(n, cap=18, trials=400, seed=0):
|
||||
rng = random.Random(seed)
|
||||
best = (10 ** 9, None, None)
|
||||
max_stacks = (cap - (n - 2)) // 2
|
||||
# random search
|
||||
for _ in range(trials):
|
||||
k = rng.randint(0, max_stacks)
|
||||
faces = random_disk(n, k, rng)
|
||||
P = phi(faces, n, cap)
|
||||
if P is None:
|
||||
continue
|
||||
if len(P) < best[0]:
|
||||
best = (len(P), k, P)
|
||||
# deterministic deep stack at max depth
|
||||
for k in range(max_stacks + 1):
|
||||
faces = deep_stack_disk(n, k)
|
||||
P = phi(faces, n, cap)
|
||||
if P is not None and len(P) < best[0]:
|
||||
best = (len(P), k, P)
|
||||
size, k, P = best
|
||||
print(f"n={n}: min |Phi| = {size} (= 2^(n-2) = {2**(n-2)}?) "
|
||||
f"interior vertices = {k}, max stacks at cap {cap} = {max_stacks}")
|
||||
print(f" {describe(P)}")
|
||||
for s in sorted(P)[:6]:
|
||||
print(f" {disp(s)}")
|
||||
if len(P) > 6:
|
||||
print(f" ... (+{len(P)-6} more)")
|
||||
return size
|
||||
|
||||
|
||||
def main():
|
||||
ns = [int(x) for x in sys.argv[1:]] or [4, 5, 6, 7]
|
||||
print("Searching for maximally-constraining disks (min |Phi|)\n")
|
||||
for n in ns:
|
||||
# bigger cap for small n
|
||||
cap = 18 if n <= 6 else 16
|
||||
search(n, cap=cap)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
Search for a UNIVERSAL Heawood boundary sequence for a tire graph.
|
||||
|
||||
Fix an outer boundary cycle B_out of length n (the interface at which a tire
|
||||
glues to its parent). Each way of filling the annulus -- an inner boundary of
|
||||
size m together with a spoke triangulation ("inner graph") -- gives a tire whose
|
||||
annular faces induce a set of realisable outer Heawood sequences
|
||||
|
||||
R_out(tire) = { (lambda*(v0), ..., lambda*(v_{n-1})) : lambda in {+1,-1}^F }
|
||||
⊆ {0,1,-1}^n .
|
||||
|
||||
A *universal sequence* for B_out is one realisable for EVERY inner graph, i.e. a
|
||||
member of the intersection ∩_tire R_out(tire). If a universal sequence existed,
|
||||
a parent could always present its negation and glue to any child regardless of
|
||||
the child's interior.
|
||||
|
||||
Note: chords of the inner outerplanar graph O lie inside B_in and bound no
|
||||
annular face, so they do not change R_out -- only (n, m, spoke-path) do. And
|
||||
intersecting over a SUBFAMILY of inner graphs can only OVERestimate the true
|
||||
intersection, so finding the intersection empty over simple-cycle inner fills is
|
||||
already conclusive that NO universal sequence exists.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from itertools import combinations, product
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def lattice_paths(n_outer, m_inner):
|
||||
"""All spoke triangulations: strings with n_outer 'O' moves, m_inner 'I'."""
|
||||
N = n_outer + m_inner
|
||||
for opos in combinations(range(N), n_outer):
|
||||
opos = set(opos)
|
||||
yield "".join("O" if i in opos else "I" for i in range(N))
|
||||
|
||||
|
||||
def annular_faces(n, m, path):
|
||||
"""Faces (triangles) of the annulus between outer n-cycle (0..n-1) and inner
|
||||
m-cycle (n..n+m-1) under the spoke path. Starts at spoke (outer0, inner0)."""
|
||||
faces = []
|
||||
i = j = 0
|
||||
for mv in path:
|
||||
if mv == "O":
|
||||
faces.append((i % n, (i + 1) % n, n + (j % m)))
|
||||
i += 1
|
||||
else:
|
||||
faces.append((i % n, n + (j % m), n + ((j + 1) % m)))
|
||||
j += 1
|
||||
return faces
|
||||
|
||||
|
||||
def fan_faces(n):
|
||||
"""m = 1 degenerate inner boundary: a wheel/fan, center = vertex n."""
|
||||
return [(i, (i + 1) % n, n) for i in range(n)]
|
||||
|
||||
|
||||
def realisable_outer(n, faces):
|
||||
"""Set of outer Heawood sequences over all +/-1 face labellings."""
|
||||
F = len(faces)
|
||||
A = np.zeros((n, F), dtype=np.int64) # outer-vertex x face incidence
|
||||
for f, tri in enumerate(faces):
|
||||
for v in tri:
|
||||
if v < n:
|
||||
A[v, f] = 1
|
||||
labs = np.array(list(product((1, 2), repeat=F)), dtype=np.int64)
|
||||
vals = (labs @ A.T) % 3
|
||||
# display residues in {0, 1, -1}: 2 -> -1
|
||||
vals = np.where(vals == 2, -1, vals)
|
||||
return set(tuple(int(x) for x in row) for row in np.unique(vals, axis=0))
|
||||
|
||||
|
||||
def tires_for(n, m_max, fcap):
|
||||
"""Yield (label, faces) for inner fills of an n-outer tire."""
|
||||
yield (f"m=1 fan", fan_faces(n))
|
||||
for m in range(2, m_max + 1):
|
||||
if n + m > fcap:
|
||||
continue
|
||||
for path in lattice_paths(n, m):
|
||||
yield (f"m={m} {path}", annular_faces(n, m, path))
|
||||
|
||||
|
||||
def run(n, m_max=7, fcap=13):
|
||||
inter = None
|
||||
n_tires = 0
|
||||
min_set = (10**9, None)
|
||||
shrink_trace = []
|
||||
for label, faces in tires_for(n, m_max, fcap):
|
||||
R = realisable_outer(n, faces)
|
||||
n_tires += 1
|
||||
if len(R) < min_set[0]:
|
||||
min_set = (len(R), label)
|
||||
if inter is None:
|
||||
inter = set(R)
|
||||
else:
|
||||
before = len(inter)
|
||||
inter &= R
|
||||
if len(inter) < before:
|
||||
shrink_trace.append((n_tires, label, len(inter)))
|
||||
if not inter:
|
||||
break
|
||||
print(f"n={n}: {n_tires} tires tried, "
|
||||
f"smallest single R_out = {min_set[0]} ({min_set[1]})")
|
||||
if inter:
|
||||
print(f" UNIVERSAL sequences found: {len(inter)}")
|
||||
for s in sorted(inter)[:12]:
|
||||
print(f" {s}")
|
||||
else:
|
||||
print(f" NO universal sequence: intersection emptied after "
|
||||
f"{n_tires} tires")
|
||||
print(" intersection size as tires were added (last few shrinks):")
|
||||
for t in shrink_trace[-6:]:
|
||||
print(f" after tire {t[0]:4d} ({t[1]}): |∩| = {t[2]}")
|
||||
return bool(inter)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) > 1:
|
||||
ns = [int(sys.argv[1])]
|
||||
else:
|
||||
ns = [3, 4, 5, 6]
|
||||
print("Searching for universal Heawood boundary sequences\n")
|
||||
for n in ns:
|
||||
run(n)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
Transfer operator for the Heawood program, in the cleanest self-similar setting:
|
||||
a chain of annular tires with n_out = n_in = n. Each tire's labelling map sends
|
||||
+/-1 face labels to (outer sequence, inner sequence). Gluing a child below means
|
||||
the parent's inner sequence must negate (mod 3) the child's achievable outer
|
||||
sequence. So the achievable outer-interface set propagates UP the chain by
|
||||
|
||||
Phi(parent) = { outer(lambda) : lambda in {+-1}^F,
|
||||
inner(lambda) in -Phi(child) }.
|
||||
|
||||
This is a monotone set-operator on subsets of (Z/3)^n. Iterating it models a
|
||||
deepening nested chain; we look for a FIXED POINT (absorbing set) and test which
|
||||
candidate self-similar invariants the limit satisfies:
|
||||
* non-empty
|
||||
* closed under the global sign flip s -> -s
|
||||
* local marginals: does every position attain all of {0,1,-1}?
|
||||
* is it an affine GF(3) subspace? (we expect NO -- R_T is a zonotope)
|
||||
* does a linear/parity constraint cut it out?
|
||||
Sequences are stored mod 3 in {0,1,2}; printed in {0,1,-1} (2 -> -1).
|
||||
"""
|
||||
|
||||
import sys
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def annular_tire(n_out, n_in, path):
|
||||
"""Faces between outer cycle 0..n_out-1 and inner cycle n_out..n_out+n_in-1."""
|
||||
faces = []
|
||||
i = j = 0
|
||||
for mv in path:
|
||||
if mv == "O":
|
||||
faces.append((i % n_out, (i + 1) % n_out, n_out + (j % n_in)))
|
||||
i += 1
|
||||
else:
|
||||
faces.append((i % n_out, n_out + (j % n_in), n_out + ((j + 1) % n_in)))
|
||||
j += 1
|
||||
return faces
|
||||
|
||||
|
||||
def labelling_pairs(n_out, n_in, faces):
|
||||
"""All (outer_seq, inner_seq) over lambda in {+1,-1}^F, as Z/3 tuples."""
|
||||
F = len(faces)
|
||||
Ao = np.zeros((n_out, F), dtype=np.int64)
|
||||
Ai = np.zeros((n_in, F), dtype=np.int64)
|
||||
for f, (a, b, c) in enumerate(faces):
|
||||
for v in (a, b, c):
|
||||
if v < n_out:
|
||||
Ao[v, f] = 1
|
||||
else:
|
||||
Ai[v - n_out, f] = 1
|
||||
labs = np.array(list(product((1, 2), repeat=F)), dtype=np.int64)
|
||||
outer = (labs @ Ao.T) % 3
|
||||
inner = (labs @ Ai.T) % 3
|
||||
return [(tuple(o), tuple(i)) for o, i in zip(outer.tolist(), inner.tolist())]
|
||||
|
||||
|
||||
def make_operator(pairs):
|
||||
def op(phi_child):
|
||||
neg = {tuple((3 - x) % 3 for x in s) for s in phi_child}
|
||||
return {o for (o, inn) in pairs if inn in neg}
|
||||
return op
|
||||
|
||||
|
||||
def iterate_to_fixed(op, start, max_iter=50):
|
||||
phi = frozenset(start)
|
||||
seen = [phi]
|
||||
for _ in range(max_iter):
|
||||
nxt = frozenset(op(phi))
|
||||
if nxt == phi:
|
||||
return phi, "fixed", len(seen)
|
||||
if nxt in seen:
|
||||
return nxt, "cycle", len(seen)
|
||||
phi = nxt
|
||||
seen.append(phi)
|
||||
return phi, "no-converge", len(seen)
|
||||
|
||||
|
||||
# ----------------- invariant tests -------------------------------------------
|
||||
def disp(s):
|
||||
return tuple(-1 if x == 2 else x for x in s)
|
||||
|
||||
|
||||
def gf3_rank(rows):
|
||||
M = [[x % 3 for x in r] for r in rows]
|
||||
if not M:
|
||||
return 0
|
||||
nc = len(M[0]); r = 0
|
||||
for c in range(nc):
|
||||
piv = next((i for i in range(r, len(M)) if M[i][c] % 3), None)
|
||||
if piv is None:
|
||||
continue
|
||||
M[r], M[piv] = M[piv], M[r]
|
||||
inv = M[r][c] % 3 # 1->1, 2->2 are self-inverse mod 3
|
||||
M[r] = [(x * inv) % 3 for x in M[r]]
|
||||
for i in range(len(M)):
|
||||
if i != r and M[i][c] % 3:
|
||||
f = M[i][c] % 3
|
||||
M[i] = [(M[i][k] - f * M[r][k]) % 3 for k in range(nc)]
|
||||
r += 1
|
||||
if r == len(M):
|
||||
break
|
||||
return r
|
||||
|
||||
|
||||
def is_affine(S):
|
||||
S = list(S)
|
||||
if len(S) <= 1:
|
||||
return True
|
||||
s0 = S[0]
|
||||
D = [tuple((np.array(s) - np.array(s0)) % 3) for s in S]
|
||||
return len(S) == 3 ** gf3_rank(D)
|
||||
|
||||
|
||||
def marginals_full(S, n):
|
||||
return all({s[i] for s in S} == {0, 1, 2} for i in range(n))
|
||||
|
||||
|
||||
def sign_closed(S):
|
||||
return all(tuple((3 - x) % 3 for x in s) in S for s in S)
|
||||
|
||||
|
||||
def linear_constraints(S, n):
|
||||
"""Dimension of the space of linear forms vanishing on S-s0 (codim of hull)."""
|
||||
S = list(S)
|
||||
if len(S) <= 1:
|
||||
return n
|
||||
s0 = S[0]
|
||||
D = [tuple((np.array(s) - np.array(s0)) % 3) for s in S]
|
||||
return n - gf3_rank(D)
|
||||
|
||||
|
||||
def analyse(tag, S, n):
|
||||
print(f" [{tag}] |Phi|={len(S)} of 3^{n}={3**n} "
|
||||
f"sign-closed={sign_closed(S)} marginals-full={marginals_full(S,n)} "
|
||||
f"affine={is_affine(S)} hull-codim={linear_constraints(S,n)}")
|
||||
|
||||
|
||||
def run(n, paths=None):
|
||||
if paths is None:
|
||||
# a few distinct same-n annular triangulations
|
||||
paths = ["OI" * n, "O" * n + "I" * n, ("OOI" * n)[:2 * n]]
|
||||
paths = [p for p in paths if p.count("O") == n and p.count("I") == n]
|
||||
print(f"=== n={n} ===")
|
||||
full = set(product((0, 1, 2), repeat=n))
|
||||
for path in paths:
|
||||
faces = annular_tire(n, n, path)
|
||||
pairs = labelling_pairs(n, n, faces)
|
||||
op = make_operator(pairs)
|
||||
single = set(o for (o, _) in pairs) # leaf: full single-tire outer set
|
||||
fixed, how, steps = iterate_to_fixed(op, single)
|
||||
# also iterate from the universal start (all sequences allowed below)
|
||||
fixed2, how2, _ = iterate_to_fixed(op, full)
|
||||
print(f" path={path}: single-tire |outer|={len(single)}; "
|
||||
f"iterate->{how} in {steps} steps; "
|
||||
f"same-limit-from-full={fixed==fixed2}")
|
||||
analyse("limit", fixed, n)
|
||||
sample = sorted(disp(s) for s in fixed)[:8]
|
||||
print(f" sample of limit set: {sample}")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
ns = [int(x) for x in sys.argv[1:]] or [4, 5, 6]
|
||||
print("Transfer-operator fixed points on same-n annular tire chains\n")
|
||||
for n in ns:
|
||||
run(n)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
\relax
|
||||
\gdef \@abspage@last{2}
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 17 JUN 2026 02:12
|
||||
entering extended mode
|
||||
restricted \write18 enabled.
|
||||
%&-line parsing enabled.
|
||||
**boundary_restriction_structure.tex
|
||||
(./boundary_restriction_structure.tex
|
||||
LaTeX2e <2021-11-15> patch level 1
|
||||
L3 programming layer <2022-02-24>
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/base/article.cls
|
||||
Document Class: article 2021/10/04 v1.4n Standard LaTeX document class
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/base/size11.clo
|
||||
File: size11.clo 2021/10/04 v1.4n Standard LaTeX file (size option)
|
||||
)
|
||||
\c@part=\count185
|
||||
\c@section=\count186
|
||||
\c@subsection=\count187
|
||||
\c@subsubsection=\count188
|
||||
\c@paragraph=\count189
|
||||
\c@subparagraph=\count190
|
||||
\c@figure=\count191
|
||||
\c@table=\count192
|
||||
\abovecaptionskip=\skip47
|
||||
\belowcaptionskip=\skip48
|
||||
\bibindent=\dimen138
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsmath.sty
|
||||
Package: amsmath 2021/10/15 v2.17l AMS math features
|
||||
\@mathmargin=\skip49
|
||||
|
||||
For additional information on amsmath, use the `?' option.
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amstext.sty
|
||||
Package: amstext 2021/08/26 v2.01 AMS text
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsgen.sty
|
||||
File: amsgen.sty 1999/11/30 v2.0 generic functions
|
||||
\@emptytoks=\toks16
|
||||
\ex@=\dimen139
|
||||
))
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsbsy.sty
|
||||
Package: amsbsy 1999/11/29 v1.2d Bold Symbols
|
||||
\pmbraise@=\dimen140
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsopn.sty
|
||||
Package: amsopn 2021/08/26 v2.02 operator names
|
||||
)
|
||||
\inf@bad=\count193
|
||||
LaTeX Info: Redefining \frac on input line 234.
|
||||
\uproot@=\count194
|
||||
\leftroot@=\count195
|
||||
LaTeX Info: Redefining \overline on input line 399.
|
||||
\classnum@=\count196
|
||||
\DOTSCASE@=\count197
|
||||
LaTeX Info: Redefining \ldots on input line 496.
|
||||
LaTeX Info: Redefining \dots on input line 499.
|
||||
LaTeX Info: Redefining \cdots on input line 620.
|
||||
\Mathstrutbox@=\box50
|
||||
\strutbox@=\box51
|
||||
\big@size=\dimen141
|
||||
LaTeX Font Info: Redeclaring font encoding OML on input line 743.
|
||||
LaTeX Font Info: Redeclaring font encoding OMS on input line 744.
|
||||
\macc@depth=\count198
|
||||
\c@MaxMatrixCols=\count199
|
||||
\dotsspace@=\muskip16
|
||||
\c@parentequation=\count266
|
||||
\dspbrk@lvl=\count267
|
||||
\tag@help=\toks17
|
||||
\row@=\count268
|
||||
\column@=\count269
|
||||
\maxfields@=\count270
|
||||
\andhelp@=\toks18
|
||||
\eqnshift@=\dimen142
|
||||
\alignsep@=\dimen143
|
||||
\tagshift@=\dimen144
|
||||
\tagwidth@=\dimen145
|
||||
\totwidth@=\dimen146
|
||||
\lineht@=\dimen147
|
||||
\@envbody=\toks19
|
||||
\multlinegap=\skip50
|
||||
\multlinetaggap=\skip51
|
||||
\mathdisplay@stack=\toks20
|
||||
LaTeX Info: Redefining \[ on input line 2938.
|
||||
LaTeX Info: Redefining \] on input line 2939.
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amssymb.sty
|
||||
Package: amssymb 2013/01/14 v3.01 AMS font symbols
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amsfonts.sty
|
||||
Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support
|
||||
\symAMSa=\mathgroup4
|
||||
\symAMSb=\mathgroup5
|
||||
LaTeX Font Info: Redeclaring math symbol \hbar on input line 98.
|
||||
LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold'
|
||||
(Font) U/euf/m/n --> U/euf/b/n on input line 106.
|
||||
))
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amscls/amsthm.sty
|
||||
Package: amsthm 2020/05/29 v2.20.6
|
||||
\thm@style=\toks21
|
||||
\thm@bodyfont=\toks22
|
||||
\thm@headfont=\toks23
|
||||
\thm@notefont=\toks24
|
||||
\thm@headpunct=\toks25
|
||||
\thm@preskip=\skip52
|
||||
\thm@postskip=\skip53
|
||||
\thm@headsep=\skip54
|
||||
\dth@everypar=\toks26
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphicx.sty
|
||||
Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR)
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/keyval.sty
|
||||
Package: keyval 2014/10/28 v1.15 key=value parser (DPC)
|
||||
\KV@toks@=\toks27
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphics.sty
|
||||
Package: graphics 2021/03/04 v1.4d Standard LaTeX Graphics (DPC,SPQR)
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/trig.sty
|
||||
Package: trig 2021/08/11 v1.11 sin cos tan (DPC)
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
||||
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
|
||||
)
|
||||
Package graphics Info: Driver file: pdftex.def on input line 107.
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-def/pdftex.def
|
||||
File: pdftex.def 2020/10/05 v1.2a Graphics/color driver for pdftex
|
||||
))
|
||||
\Gin@req@height=\dimen148
|
||||
\Gin@req@width=\dimen149
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/geometry/geometry.sty
|
||||
Package: geometry 2020/01/02 v5.9 Page Geometry
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/generic/iftex/ifvtex.sty
|
||||
Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead.
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/generic/iftex/iftex.sty
|
||||
Package: iftex 2022/02/03 v1.0f TeX engine tests
|
||||
))
|
||||
\Gm@cnth=\count271
|
||||
\Gm@cntv=\count272
|
||||
\c@Gm@tempcnt=\count273
|
||||
\Gm@bindingoffset=\dimen150
|
||||
\Gm@wd@mp=\dimen151
|
||||
\Gm@odd@mp=\dimen152
|
||||
\Gm@even@mp=\dimen153
|
||||
\Gm@layoutwidth=\dimen154
|
||||
\Gm@layoutheight=\dimen155
|
||||
\Gm@layouthoffset=\dimen156
|
||||
\Gm@layoutvoffset=\dimen157
|
||||
\Gm@dimlist=\toks28
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/booktabs/booktabs.sty
|
||||
Package: booktabs 2020/01/12 v1.61803398 Publication quality tables
|
||||
\heavyrulewidth=\dimen158
|
||||
\lightrulewidth=\dimen159
|
||||
\cmidrulewidth=\dimen160
|
||||
\belowrulesep=\dimen161
|
||||
\belowbottomsep=\dimen162
|
||||
\aboverulesep=\dimen163
|
||||
\abovetopsep=\dimen164
|
||||
\cmidrulesep=\dimen165
|
||||
\cmidrulekern=\dimen166
|
||||
\defaultaddspace=\dimen167
|
||||
\@cmidla=\count274
|
||||
\@cmidlb=\count275
|
||||
\@aboverulesep=\dimen168
|
||||
\@belowrulesep=\dimen169
|
||||
\@thisruleclass=\count276
|
||||
\@lastruleclass=\count277
|
||||
\@thisrulewidth=\dimen170
|
||||
)
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
|
||||
File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX)
|
||||
\l__color_backend_stack_int=\count278
|
||||
\l__pdf_internal_box=\box52
|
||||
)
|
||||
No file boundary_restriction_structure.aux.
|
||||
\openout1 = `boundary_restriction_structure.aux'.
|
||||
|
||||
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 18.
|
||||
LaTeX Font Info: ... okay on input line 18.
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
|
||||
[Loading MPS to PDF converter (version 2006.09.02).]
|
||||
\scratchcounter=\count279
|
||||
\scratchdimen=\dimen171
|
||||
\scratchbox=\box53
|
||||
\nofMPsegments=\count280
|
||||
\nofMParguments=\count281
|
||||
\everyMPshowfont=\toks29
|
||||
\MPscratchCnt=\count282
|
||||
\MPscratchDim=\dimen172
|
||||
\MPnumerator=\count283
|
||||
\makeMPintoPDFobject=\count284
|
||||
\everyMPtoPDFconversion=\toks30
|
||||
) (/usr/local/texlive/2022/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
|
||||
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
|
||||
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
|
||||
85.
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
|
||||
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
|
||||
e
|
||||
))
|
||||
*geometry* driver: auto-detecting
|
||||
*geometry* detected driver: pdftex
|
||||
*geometry* verbose mode - [ preamble ] result:
|
||||
* driver: pdftex
|
||||
* paper: <default>
|
||||
* layout: <same size as paper>
|
||||
* layoutoffset:(h,v)=(0.0pt,0.0pt)
|
||||
* modes:
|
||||
* h-part:(L,W,R)=(72.26999pt, 469.75502pt, 72.26999pt)
|
||||
* v-part:(T,H,B)=(72.26999pt, 650.43001pt, 72.26999pt)
|
||||
* \paperwidth=614.295pt
|
||||
* \paperheight=794.96999pt
|
||||
* \textwidth=469.75502pt
|
||||
* \textheight=650.43001pt
|
||||
* \oddsidemargin=0.0pt
|
||||
* \evensidemargin=0.0pt
|
||||
* \topmargin=-37.0pt
|
||||
* \headheight=12.0pt
|
||||
* \headsep=25.0pt
|
||||
* \topskip=11.0pt
|
||||
* \footskip=30.0pt
|
||||
* \marginparwidth=59.0pt
|
||||
* \marginparsep=10.0pt
|
||||
* \columnsep=10.0pt
|
||||
* \skip\footins=10.0pt plus 4.0pt minus 2.0pt
|
||||
* \hoffset=0.0pt
|
||||
* \voffset=0.0pt
|
||||
* \mag=1000
|
||||
* \@twocolumnfalse
|
||||
* \@twosidefalse
|
||||
* \@mparswitchfalse
|
||||
* \@reversemarginfalse
|
||||
* (1in=72.27pt=25.4mm, 1cm=28.453pt)
|
||||
|
||||
LaTeX Font Info: Trying to load font information for U+msa on input line 19.
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd
|
||||
File: umsa.fd 2013/01/14 v3.01 AMS symbols A
|
||||
)
|
||||
LaTeX Font Info: Trying to load font information for U+msb on input line 19.
|
||||
|
||||
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsb.fd
|
||||
File: umsb.fd 2013/01/14 v3.01 AMS symbols B
|
||||
) [1
|
||||
|
||||
{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] [2]
|
||||
(./boundary_restriction_structure.aux) )
|
||||
Here is how much of TeX's memory you used:
|
||||
3254 strings out of 478268
|
||||
48506 string characters out of 5846347
|
||||
347668 words of memory out of 5000000
|
||||
21442 multiletter control sequences out of 15000+600000
|
||||
480359 words of font info for 70 fonts, out of 8000000 for 9000
|
||||
1141 hyphenation exceptions out of 8191
|
||||
55i,8n,62p,247b,208s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/local/texlive/2022/texmf-dist/fon
|
||||
ts/type1/public/amsfonts/cm/cmbx10.pfb></usr/local/texlive/2022/texmf-dist/font
|
||||
s/type1/public/amsfonts/cm/cmbx12.pfb></usr/local/texlive/2022/texmf-dist/fonts
|
||||
/type1/public/amsfonts/cm/cmex10.pfb></usr/local/texlive/2022/texmf-dist/fonts/
|
||||
type1/public/amsfonts/cm/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/t
|
||||
ype1/public/amsfonts/cm/cmmi12.pfb></usr/local/texlive/2022/texmf-dist/fonts/ty
|
||||
pe1/public/amsfonts/cm/cmmi8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type
|
||||
1/public/amsfonts/cm/cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/
|
||||
public/amsfonts/cm/cmr12.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/pu
|
||||
blic/amsfonts/cm/cmr17.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/publ
|
||||
ic/amsfonts/cm/cmr8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/
|
||||
amsfonts/cm/cmss8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/am
|
||||
sfonts/cm/cmsy10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/ams
|
||||
fonts/cm/cmsy8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfo
|
||||
nts/cm/cmti10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfon
|
||||
ts/cm/cmtt10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfont
|
||||
s/symbols/msbm10.pfb>
|
||||
Output written on boundary_restriction_structure.pdf (2 pages, 184212 bytes).
|
||||
PDF statistics:
|
||||
91 PDF objects out of 1000 (max. 8388607)
|
||||
54 compressed objects within 1 object stream
|
||||
0 named destinations out of 1000 (max. 500000)
|
||||
1 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
|
||||
BIN
Binary file not shown.
+139
@@ -0,0 +1,139 @@
|
||||
\documentclass[11pt]{article}
|
||||
\usepackage{amsmath,amssymb,amsthm}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{geometry}
|
||||
\usepackage{booktabs}
|
||||
\geometry{margin=1in}
|
||||
|
||||
\title{Heawood boundary restriction sets:\\
|
||||
zonotope structure and the $2^{n-2}$ constraint floor}
|
||||
\author{}
|
||||
\date{}
|
||||
|
||||
\newtheorem*{obs}{Observation}
|
||||
\newtheorem*{prop}{Proposition}
|
||||
\newtheorem*{conj}{Conjecture}
|
||||
\newtheorem*{lem}{Lemma}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
This note records the empirical structure of the Heawood boundary
|
||||
restriction sets studied in \texttt{paper.tex}, and a clean
|
||||
\emph{maximal-constraint} result. All claims below are backed by the
|
||||
experiments in \texttt{experiments/} (filenames given inline). Sequences
|
||||
live in $(\mathbb{Z}/3)^{\,\cdot}$, displayed in $\{0,1,-1\}$.
|
||||
|
||||
\section*{Setup}
|
||||
|
||||
Fix a triangulated disk $D$ with boundary cycle $C = (v_0,\dots,v_{n-1})$.
|
||||
A Heawood face-labelling is $\lambda : \{\text{faces of }D\} \to \{+1,-1\}$,
|
||||
with induced vertex value $\lambda^{*}(v) = \sum_{f \ni v}\lambda(f) \bmod 3$.
|
||||
The achievable outer set is
|
||||
\[
|
||||
\Phi(D) \;=\; \bigl\{\, (\lambda^{*}(v_0),\dots,\lambda^{*}(v_{n-1}))
|
||||
\;:\; \lambda \in \{+1,-1\}^{F(D)},\;
|
||||
\lambda^{*}(w) \equiv 0 \ \forall\ \text{interior } w \,\bigr\}.
|
||||
\]
|
||||
This is exactly the value the recursive transfer operator produces at
|
||||
$C$ (interior consistency $=$ all descendant gluings performed; boundary
|
||||
deferred). Crucially $\Phi(D)$ depends \emph{only} on the disk
|
||||
triangulation, not on any BFS/tire-tree labelling.
|
||||
|
||||
\section*{1. The restriction sets are zonotopes, not subspaces}
|
||||
|
||||
(\texttt{probe\_RK\_structure.py}.) Writing $\lambda = \mathbf{1}+b$ with
|
||||
$b \in \{0,1\}^F$, the labelling map is $\lambda \mapsto M\mathbf{1}+Mb
|
||||
\pmod 3$, a linear image of the Boolean cube ($M$ the face/vertex
|
||||
incidence matrix). Over $3655$ cluster restriction sets $R_{\mathsf K}$:
|
||||
none was an affine $\mathrm{GF}(3)$ subspace; the map is usually
|
||||
injective, so $|R_{\mathsf K}| = 2^{|F|}$ (a power of $2$ inside the
|
||||
column space of size $3^{\operatorname{rank} M}$); the nowhere-zero
|
||||
constraint $\lambda \neq 0$ shrank the set below the full linear image in
|
||||
\emph{every} case. The only surviving linear structure is
|
||||
$R_{\mathsf K} \subseteq \operatorname{col}(M)$ (cokernel relations such
|
||||
as $\sum_v \lambda^{*}(v) \equiv 0$). So $\Phi$ is a $\mathbb{Z}/3$
|
||||
zonotope: a projected cube, sign-closed but not closed under addition.
|
||||
|
||||
\section*{2. ``Richness'' is not a self-similar invariant}
|
||||
|
||||
(\texttt{transfer\_operator.py}, \texttt{branch\_invariant.py}.) In a
|
||||
homogeneous same-$n$ spoke-only chain the operator saturates: $\Phi$ has
|
||||
full single-position marginals (every interface vertex independently
|
||||
attains all of $\{0,1,-1\}$), and the alternating tire reaches the
|
||||
\emph{entire} space $3^n$. This is an artifact of non-shrinking annuli
|
||||
with no interior constraints. On genuine triangulations the marginal
|
||||
fullness holds for only ${\sim}8\%$ of regions: depth (not branching)
|
||||
shrinks $\Phi$, e.g.\ a region with $|C|=10$ realised only $|\Phi|=400$
|
||||
of $3^{10}\approx 59000$. Only non-emptiness and sign-closure survive,
|
||||
both of which are automatic / equivalent to $4$CT. Hence no abundance
|
||||
(counting) pigeonhole: a working invariant must tolerate \emph{small}
|
||||
$\Phi$.
|
||||
|
||||
\section*{3. The maximal-constraint floor}
|
||||
|
||||
(\texttt{maximally\_constrain.py}.) Minimising $|\Phi(D)|$ over disks with
|
||||
a fixed boundary $n$-cycle:
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{ccccc}
|
||||
\toprule
|
||||
$n$ & $4$ & $5$ & $6$ & $7$\\
|
||||
\midrule
|
||||
$\min |\Phi|$ (search) & $4$ & $8$ & $16$ & $32$\\
|
||||
fan, $0$ interior vertices & $4$ & $8$ & $16$ & $32$\\
|
||||
$2^{\,n-2}$ & $4$ & $8$ & $16$ & $32$\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
A search over random and deep-stacked disks (up to $8$ interior vertices)
|
||||
never beat $2^{n-2}$, and the interior-free triangulation already
|
||||
attains it. Thus:
|
||||
|
||||
\begin{obs}
|
||||
The outer $n$-cycle cannot be constrained below $2^{n-2}$ achievable
|
||||
sequences, and no nested structure is needed to reach the floor: a single
|
||||
trivial tire is already maximal. Deep nesting only approaches the floor
|
||||
from above.
|
||||
\end{obs}
|
||||
|
||||
The achievability is transparent: in a fan from $v_0$,
|
||||
\[
|
||||
\sigma_1 = \lambda_1,\quad
|
||||
\sigma_i = \lambda_{i-1}+\lambda_i \ (1<i<n-1),\quad
|
||||
\sigma_{n-1} = \lambda_{n-2},\quad
|
||||
\sigma_0 = \textstyle\sum_j \lambda_j ,
|
||||
\]
|
||||
so $(\lambda_1,\dots,\lambda_{n-2})$ is recoverable from $\sigma$ and the
|
||||
map is injective onto $2^{n-2}$ sequences. The lower bound over
|
||||
\emph{all} disks is the substance:
|
||||
|
||||
\begin{conj}[Boundary degrees of freedom]
|
||||
For every triangulated disk $D$ with boundary $n$-cycle,
|
||||
$|\Phi(D)| \ge 2^{n-2}$. Equivalently, the $n-2$ binary degrees of
|
||||
freedom carried by the boundary-incident faces survive every interior
|
||||
Heawood constraint (which relates only interior-incident faces).
|
||||
\end{conj}
|
||||
|
||||
The minimal set is itself a sign-closed zonotope of size $2^{n-2}$, hull
|
||||
dimension $n-2$, not a $\mathrm{GF}(3)$ subspace --- the same fingerprint
|
||||
as $\S1$.
|
||||
|
||||
\section*{Consequence for the pigeonhole}
|
||||
|
||||
Even a maximally-constraining child still presents $2^{n-2}$ outer
|
||||
options --- exponential in the interface length $n$. So the gluing
|
||||
problem has the least slack at \emph{short} interfaces ($n=4$ leaves $4$
|
||||
options, $n=3$ leaves $2$), and is easy at long ones. The crux of the
|
||||
Heawood programme therefore lives entirely at short level cycles, exactly
|
||||
where the medial programme's $N(k)$ bound concentrates.
|
||||
|
||||
\medskip
|
||||
\noindent\emph{Meta-remark.} Because $4$CT holds, every actual
|
||||
triangulation glues, so no experiment can exhibit an obstruction (pair or
|
||||
chain). The experiments measure \emph{structure} (zonotope type,
|
||||
constraint floor), not proof difficulty; the difficulty is localised, not
|
||||
removed.
|
||||
|
||||
\end{document}
|
||||
Reference in New Issue
Block a user