Verify Remark 5.8 on genuine bite treads
Bites arise when the inner outerplanar graph O has a bridge: the bridge edge is traversed twice by the outer-face walk, so its medial vertex is adjacent to four annular vertices. - check_remark58_bite.py: a minimal bite tread (outer 4-cycle + interior bridge u-w) restricts to Kempe-balanced on all colourings (outer face). - check_remark58_bite_rich.py: O = triangle abc + pendant bridge a-d gives one bite plus three singleton down teeth in the bite's inner-gap face; every restriction is Kempe-balanced (the three gap singletons are a rainbow in every global colouring). Update Remark 5.8's verification note: the bite case, including singletons in the bite-gap face, is now confirmed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
"""Directly test Remark 5.8 on a genuine tire piece that contains a BITE.
|
||||
|
||||
A bite arises when the inner outerplanar graph O has a bridge: the bridge edge
|
||||
is traversed twice by the outer-face walk, so it borders two tread triangles and
|
||||
its medial vertex is adjacent to four annular medial vertices.
|
||||
|
||||
Minimal construction. Outer 4-cycle o0,o1,o2,o3; two interior vertices u,w
|
||||
joined by a bridge u-w (V_in = {u,w}). Triangulate the disk so that u-w lies in
|
||||
two tread triangles:
|
||||
|
||||
(o0,o1,u) (o0,u,o3) (o1,w,u) (o1,o2,w) (o2,o3,w) (o3,u,w)
|
||||
|
||||
Cap the outer cycle with an apex N (the bridge bounds no inner hole, so no inner
|
||||
cap is needed). The result G is a closed plane triangulation; M(G) is 4-regular.
|
||||
|
||||
Edge classification (by endpoints): annular = one endpoint outer & one inner;
|
||||
up tooth = both endpoints outer (outer-cycle edge); down tooth = both endpoints
|
||||
inner (here only the bridge u-w). The bridge's medial vertex is the bite apex.
|
||||
|
||||
Remark 5.8 predicts every proper 3-colouring of M(G) restricts to a
|
||||
Kempe-balanced colouring. Here the only non-trivial condition is the outer face
|
||||
(the four up apexes), since the single bite contributes no singleton down teeth.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from check_remark58_bitefree import ekey, medial_graph, proper_3_colorings
|
||||
|
||||
PAIRS = ((0, 1), (0, 2), (1, 2))
|
||||
|
||||
OUTER = ["o0", "o1", "o2", "o3"]
|
||||
INNER = ["u", "w"]
|
||||
|
||||
TREAD_TRIANGLES = [
|
||||
("o0", "o1", "u"),
|
||||
("o0", "u", "o3"),
|
||||
("o1", "w", "u"),
|
||||
("o1", "o2", "w"),
|
||||
("o2", "o3", "w"),
|
||||
("o3", "u", "w"),
|
||||
]
|
||||
|
||||
|
||||
def build():
|
||||
g = nx.Graph()
|
||||
for tri in TREAD_TRIANGLES:
|
||||
a, b, c = tri
|
||||
g.add_edges_from([(a, b), (b, c), (a, c)])
|
||||
# outer cap
|
||||
for i in range(4):
|
||||
g.add_edges_from([("N", OUTER[i]), ("N", OUTER[(i + 1) % 4])])
|
||||
return g
|
||||
|
||||
|
||||
def classify_tread_edges(g):
|
||||
out = set(OUTER)
|
||||
inn = set(INNER)
|
||||
tread_edges = set()
|
||||
for tri in TREAD_TRIANGLES:
|
||||
a, b, c = tri
|
||||
tread_edges |= {ekey(a, b), ekey(b, c), ekey(a, c)}
|
||||
annular, up, down = [], [], []
|
||||
for e in tread_edges:
|
||||
a, b = e
|
||||
ao, bo = a in out, b in out
|
||||
ai, bi = a in inn, b in inn
|
||||
if (ao and bi) or (ai and bo):
|
||||
annular.append(e)
|
||||
elif ao and bo:
|
||||
up.append(e)
|
||||
elif ai and bi:
|
||||
down.append(e)
|
||||
return annular, up, down
|
||||
|
||||
|
||||
def run():
|
||||
g = build()
|
||||
assert nx.check_planarity(g)[0]
|
||||
M = medial_graph(g)
|
||||
annular, up, down = classify_tread_edges(g)
|
||||
annular_set = set(annular)
|
||||
|
||||
# confirm there is a bite: a down edge whose medial vertex has 4 annular nbrs
|
||||
bites = [e for e in down if sum(1 for nb in M.neighbors(e) if nb in annular_set) == 4]
|
||||
print(f"tread: annular={len(annular)} up={len(up)} down={len(down)} "
|
||||
f"bite apexes={len(bites)} (bite edge: {bites})")
|
||||
|
||||
colorings = proper_3_colorings(M, limit=20000)
|
||||
balanced = 0
|
||||
bad = []
|
||||
for col in colorings:
|
||||
ok = all(
|
||||
sum(1 for e in up if col[e] in pair) % 2 == 0
|
||||
for pair in PAIRS
|
||||
)
|
||||
if ok:
|
||||
balanced += 1
|
||||
else:
|
||||
bad.append(col)
|
||||
|
||||
print(f"|V(G)|={g.number_of_nodes()} |M(G)|={M.number_of_nodes()} "
|
||||
f"colourings tested={len(colorings)}")
|
||||
print(f" outer-face (up-apex) balanced={balanced} UNBALANCED={len(bad)}")
|
||||
if bad:
|
||||
print(f" first unbalanced up colours: {[bad[0][e] for e in up]}")
|
||||
print()
|
||||
print("Remark 5.8 holds on this bite tread"
|
||||
if not bad else
|
||||
"Remark 5.8 FAILS on this bite tread")
|
||||
return len(bad)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
"""Test Remark 5.8 on a bite tread that also has singleton down teeth in the
|
||||
bite's inner-gap face -- the subtle case of the condition.
|
||||
|
||||
Inner outerplanar graph O = triangle (a,b,c) plus a pendant bridge a-d. Its
|
||||
outer-face walk is the cyclic sequence W = [d, a, b, c, a]: the bridge a-d is
|
||||
traversed twice (-> a bite), the triangle edges a-b, b-c, c-a once each (-> three
|
||||
singleton down teeth, all sitting in the bite's inner-gap face).
|
||||
|
||||
We triangulate the annulus between an outer m-cycle and W by the lattice-path
|
||||
method, searching interleavings for one giving a simple closed triangulation
|
||||
after capping the outer cycle with an apex N. Then we test Remark 5.8: every
|
||||
proper 3-colouring of M(G) restricts to a Kempe-balanced colouring, i.e.
|
||||
|
||||
* the up apexes (outer edges) are even per colour pair, and
|
||||
* the three singleton down apexes (a-b, b-c, c-a), which share the bite-gap
|
||||
face, are even per colour pair (equivalently: a rainbow).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from check_remark58_bitefree import ekey, medial_graph, proper_3_colorings
|
||||
|
||||
PAIRS = ((0, 1), (0, 2), (1, 2))
|
||||
INNER_WALK = ["d", "a", "b", "c", "a"] # bridge a-d traversed twice
|
||||
SINGLETON_DOWN = [ekey("a", "b"), ekey("b", "c"), ekey("c", "a")]
|
||||
BITE_EDGE = ekey("a", "d")
|
||||
|
||||
|
||||
def build_tread(m: int, path: str):
|
||||
"""Build the annular triangulation for a given lattice path (m 'O', L 'I')."""
|
||||
outer = [f"o{t}" for t in range(m)]
|
||||
W = INNER_WALK
|
||||
L = len(W)
|
||||
g = nx.Graph()
|
||||
g.add_edge(outer[0], W[0]) # anchor
|
||||
i = j = 0
|
||||
tread_triangles = []
|
||||
for mv in path:
|
||||
if mv == "O":
|
||||
tri = (outer[i % m], W[j % L], outer[(i + 1) % m])
|
||||
i += 1
|
||||
else:
|
||||
tri = (outer[i % m], W[j % L], W[(j + 1) % L])
|
||||
j += 1
|
||||
a, b, c = tri
|
||||
g.add_edges_from([(a, b), (b, c), (a, c)])
|
||||
tread_triangles.append(tri)
|
||||
if (i, j) != (m, L):
|
||||
return None
|
||||
return g, outer, tread_triangles
|
||||
|
||||
|
||||
def cap_and_validate(g, outer):
|
||||
"""Cap the outer cycle with apex N; require a simple closed triangulation."""
|
||||
h = g.copy()
|
||||
for t in range(len(outer)):
|
||||
h.add_edges_from([("N", outer[t]), ("N", outer[(t + 1) % len(outer)])])
|
||||
if not nx.check_planarity(h)[0]:
|
||||
return None
|
||||
V, E = h.number_of_nodes(), h.number_of_edges()
|
||||
if E != 3 * V - 6: # maximal planar == triangulation
|
||||
return None
|
||||
return h
|
||||
|
||||
|
||||
def find_construction(m: int):
|
||||
L = len(INNER_WALK)
|
||||
for combo in itertools.combinations(range(m + L), L):
|
||||
path = "".join("I" if t in combo else "O" for t in range(m + L))
|
||||
built = build_tread(m, path)
|
||||
if built is None:
|
||||
continue
|
||||
g, outer, tris = built
|
||||
h = cap_and_validate(g, outer)
|
||||
if h is not None:
|
||||
return h, outer, tris, path
|
||||
return None
|
||||
|
||||
|
||||
def run():
|
||||
for m in (4, 5, 6, 7):
|
||||
found = find_construction(m)
|
||||
if found:
|
||||
break
|
||||
if not found:
|
||||
print("no valid bite-with-singletons triangulation found")
|
||||
return 1
|
||||
h, outer, tris, path = found
|
||||
M = medial_graph(h)
|
||||
|
||||
annular = set()
|
||||
for tri in tris:
|
||||
a, b, c = tri
|
||||
for e in (ekey(a, b), ekey(b, c), ekey(a, c)):
|
||||
x, y = e
|
||||
xo, yo = x in outer, y in outer
|
||||
if (xo and not yo) or (yo and not xo):
|
||||
annular.add(e)
|
||||
|
||||
n_bite_nbrs = sum(1 for nb in M.neighbors(BITE_EDGE) if nb in annular)
|
||||
up = [ekey(outer[t], outer[(t + 1) % len(outer)]) for t in range(len(outer))]
|
||||
up = [e for e in up if e in M]
|
||||
|
||||
print(f"m={len(outer)} path={path} |V(G)|={h.number_of_nodes()} "
|
||||
f"|M(G)|={M.number_of_nodes()}")
|
||||
print(f"bite edge {BITE_EDGE}: annular neighbours={n_bite_nbrs} (4 => bite)")
|
||||
print(f"up apexes={len(up)} singleton down apexes={SINGLETON_DOWN}")
|
||||
|
||||
colorings = proper_3_colorings(M, limit=50000)
|
||||
bad_outer = bad_bitegap = 0
|
||||
for col in colorings:
|
||||
if any(sum(1 for e in up if col[e] in p) % 2 for p in PAIRS):
|
||||
bad_outer += 1
|
||||
if any(sum(1 for e in SINGLETON_DOWN if col[e] in p) % 2 for p in PAIRS):
|
||||
bad_bitegap += 1
|
||||
|
||||
print(f"colourings tested={len(colorings)}")
|
||||
print(f" outer face unbalanced: {bad_outer}")
|
||||
print(f" bite-gap face (3 singletons) unbalanced: {bad_bitegap}")
|
||||
print()
|
||||
ok = (bad_outer == 0 and bad_bitegap == 0)
|
||||
print("Remark 5.8 holds on this bite-with-singletons tread"
|
||||
if ok else "Remark 5.8 FAILS on this bite-with-singletons tread")
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Reference in New Issue
Block a user