face_monochromatic_pairs: try winding-number approach (option 4) — does not yield contradiction
Approach: at each vertex of a simple closed cycle C in a 3-regular planar graph, define turn-sign(v) = +1 if the third edge (off-cycle) is in C's bounded region (interior), -1 if exterior. Compute Σ_v turn-sign(v). Empirical check on standard graphs (K_4, Q_3, dodecahedron, 3-prism): For a FACE boundary, Σ = -L_face (all third edges outside the face). For a NON-face cycle, Σ can range from -L to +L. Plan: under Lemma 5.2's alternation hypothesis (constancy on V(K_b) forces third edges to alternate sides along K_b), the signs alternate +,-,+,-,... yielding Σ = 0 for K_b of even length. This shows K_b is NOT a face boundary (= it bounds a region containing other vertices/edges), which is true but not a contradiction. A simple closed planar curve can have Σ = 0; that just means equal numbers of off-cycle edges are inside vs outside. So the winding-number approach (option 4) does not yield a direct contradiction under the chord-apex+Kempe + constancy hypothesis. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
"""Verify the combinatorial winding invariant for simple closed cycles
|
||||
in a 3-regular planar graph:
|
||||
|
||||
Claim: For a simple closed cycle C in a 3-regular planar graph H
|
||||
(with C ⊆ E(H), |C|/2 edges of C at each vertex of C), walked CCW
|
||||
around its interior, define for each v ∈ V(C):
|
||||
turn-sign(v) = +1 if the third edge (= the one of v's 3 edges NOT
|
||||
on C) is on the OUTSIDE of C, locally at v.
|
||||
(= the walker turns LEFT into the interior).
|
||||
-1 if the third edge is on the INSIDE.
|
||||
|
||||
Hypothesis: Σ_v turn-sign(v) = ±6 (= ±(L_C / |V|_C-cycle-curvature)).
|
||||
|
||||
This is a combinatorial Gauss-Bonnet for cubic plane graphs:
|
||||
internal/external turn parity around any face / cycle.
|
||||
|
||||
Sanity-check this on:
|
||||
- Triangle (length 3) in K_4.
|
||||
- Quadrilateral (length 4) in Q_3.
|
||||
- Pentagon (length 5) in dodecahedron.
|
||||
- Hexagon (length 6) in some triangulation dual.
|
||||
|
||||
Also on Kempe cycles in chord-apex+Kempe colourings of reduced duals.
|
||||
|
||||
If the invariant holds (always ±6), then combining with Lemma 5.2's
|
||||
alternation (= turn signs alternate +,-,+,- on any K_b under constancy),
|
||||
we'd have #(+) = #(-) = L_b / 2, so #(+) - #(-) = 0 ≠ ±6, giving a
|
||||
direct contradiction.
|
||||
|
||||
Run with: sage experiments/check_combinatorial_winding.py
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
from sage.all import Graph
|
||||
from sage.graphs.graph_generators import graphs
|
||||
|
||||
|
||||
def cycle_winding(G, cycle_vertices):
|
||||
"""Compute Σ_v turn-sign(v) for a simple closed cycle in 3-reg planar G.
|
||||
|
||||
cycle_vertices: list of vertices in walking order around the cycle.
|
||||
Walking direction: CCW (= traversal such that interior is on the LEFT).
|
||||
|
||||
For each v in the cycle, the 3 edges at v are split as: 2 cycle
|
||||
edges (to v's neighbours in the cycle) and 1 third edge (off-cycle).
|
||||
The third edge is at some position in v's CW rotation system.
|
||||
|
||||
turn-sign(v) = +1 if the third edge is OUTSIDE the cycle (= on the
|
||||
right of the CCW walk locally), -1 if INSIDE.
|
||||
"""
|
||||
G.is_planar(set_embedding=True)
|
||||
emb = G.get_embedding()
|
||||
L = len(cycle_vertices)
|
||||
total = 0
|
||||
for k in range(L):
|
||||
v = cycle_vertices[k]
|
||||
prev = cycle_vertices[(k - 1) % L]
|
||||
nxt = cycle_vertices[(k + 1) % L]
|
||||
# CW rotation at v: emb[v] is the list of neighbours in CW
|
||||
# order.
|
||||
nbrs = emb[v]
|
||||
# We need positions of prev, nxt, and third.
|
||||
if prev not in nbrs or nxt not in nbrs:
|
||||
return None
|
||||
# The third edge endpoint:
|
||||
third = [u for u in nbrs if u != prev and u != nxt]
|
||||
if len(third) != 1:
|
||||
return None
|
||||
third = third[0]
|
||||
# CW order of (prev, nxt, third) in emb[v]:
|
||||
idx_prev = nbrs.index(prev)
|
||||
idx_nxt = nbrs.index(nxt)
|
||||
idx_third = nbrs.index(third)
|
||||
# In CW order, what's the cyclic arrangement?
|
||||
# Walking CCW around the cycle means walker is on edge (prev, v),
|
||||
# at v, and exits via (v, nxt). The interior of the cycle is
|
||||
# on the LEFT of the walker.
|
||||
# The third edge is at idx_third in CW rotation.
|
||||
# If we go CW from idx_prev to idx_nxt:
|
||||
# - if idx_third is in this CW interval, third is on one side;
|
||||
# - else on the other side.
|
||||
# For 3-regular vertex with CW order (positions 0, 1, 2 cyclically),
|
||||
# idx_prev, idx_nxt, idx_third are some permutation of {0, 1, 2}.
|
||||
# Define: third is on the RIGHT of walker (= going CW from prev
|
||||
# to nxt without passing through third) ⟺ third is NOT between
|
||||
# prev and nxt in CW direction.
|
||||
# The CW order at v: nbrs[0], nbrs[1], nbrs[2].
|
||||
# CW direction at v: 0 → 1 → 2 → 0.
|
||||
# CW from prev to nxt: starting at idx_prev, going CW (= +1 mod 3),
|
||||
# we reach idx_nxt after some steps.
|
||||
# If we pass through idx_third in 1 step: third is between prev
|
||||
# and nxt in CW direction (i.e., on one specific side).
|
||||
# If we don't pass through idx_third (= go directly from prev to
|
||||
# nxt in 1 CW step): third is on the OTHER side.
|
||||
# 1 CW step from idx_prev: (idx_prev + 1) % 3.
|
||||
if (idx_prev + 1) % 3 == idx_nxt:
|
||||
# CW step from prev to nxt is direct (1 step).
|
||||
# Third is at the "other" position, NOT between in CW.
|
||||
# This means third is on the "left" of walker going CCW
|
||||
# along the cycle (= interior side).
|
||||
# turn-sign = -1 (third inside).
|
||||
sign = -1
|
||||
elif (idx_prev + 2) % 3 == idx_nxt:
|
||||
# CW step from prev to nxt is 2 steps (passing through
|
||||
# idx_third).
|
||||
# Third is BETWEEN prev and nxt in CW direction.
|
||||
# turn-sign = +1 (third outside, walker turns left into
|
||||
# interior).
|
||||
sign = +1
|
||||
else:
|
||||
return None
|
||||
total += sign
|
||||
return total
|
||||
|
||||
|
||||
def face_to_cycle(face):
|
||||
"""Convert face (= list of edges) to ordered vertex cycle."""
|
||||
if not face:
|
||||
return None
|
||||
verts = [face[0][0]]
|
||||
for e in face:
|
||||
verts.append(e[1])
|
||||
return verts[:-1] # last vertex = first vertex
|
||||
|
||||
|
||||
def main():
|
||||
tests = []
|
||||
# K_4 = tetrahedron
|
||||
K4 = graphs.CompleteGraph(4)
|
||||
K4.is_planar(set_embedding=True)
|
||||
tests.append(('K_4', K4))
|
||||
# Q_3 = cube
|
||||
Q3 = graphs.CubeGraph(3)
|
||||
Q3.is_planar(set_embedding=True)
|
||||
tests.append(('Q_3', Q3))
|
||||
# Dodecahedron
|
||||
Dod = graphs.DodecahedralGraph()
|
||||
Dod.is_planar(set_embedding=True)
|
||||
tests.append(('Dodecahedron', Dod))
|
||||
# Triangular prism
|
||||
Tprism = graphs.CompleteBipartiteGraph(2, 3) # not 3-regular
|
||||
# Use Q3 plus more
|
||||
tests.append(('Triangular prism (3-prism)', graphs.GeneralizedPetersenGraph(3, 1)))
|
||||
|
||||
for name, G in tests:
|
||||
if not all(G.degree(v) == 3 for v in G.vertex_iterator()):
|
||||
print(f"\n{name}: NOT 3-regular (degree set = "
|
||||
f"{sorted(set(G.degree()))}); skipping")
|
||||
continue
|
||||
if not G.is_planar(set_embedding=True):
|
||||
print(f"\n{name}: NOT planar; skipping")
|
||||
continue
|
||||
print(f"\n{name}: |V|={G.order()}, |E|={G.size()}, faces:")
|
||||
faces = G.faces()
|
||||
for f in faces:
|
||||
cyc = face_to_cycle(f)
|
||||
if cyc is None:
|
||||
print(f" bad face")
|
||||
continue
|
||||
w = cycle_winding(G, cyc)
|
||||
print(f" face length {len(cyc)}, cycle={cyc}, winding={w}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user