face_monochromatic_pairs: minimum-flip and minority-location diagnostics
Two more diagnostics on chord-apex+Kempe colourings (n <= 18,
13,800 colourings) probing how thin the non-constancy obstacle on
V(K_b) is:
1. check_min_flip_structure.py
- Flip count on K_b drops as low as 2 (at n = 18, 12 colourings):
these have a single minority Heawood vertex on K_b. So the
structural obstacle has NO slack: proving "at least 1 minority
vertex on V(K_b)" is the bar.
- All n=14 colourings (216) have flip count = 8 exactly. At
larger n the distribution spreads.
2. check_minority_location.py
- For colourings with K_b flip count <= 4, identify the minority
Heawood vertices and tally where they sit:
v_n : 12.86%
A_{i+1} : 10.82%
A_{i+2} : 8.98%
A_i : 7.76%
A_{i+4} : 5.31%
A_{i+3} : 5.10%
"other" : 49.18%
- About half the minority vertices live on non-named vertices in
the rest of G'. No single named vertex is *always* the
minority. The obstruction is genuinely diffuse / global, not
anchored to a specific structural location.
These together imply that the structural proof of "h_phi non-constant
on V(K_b)" must be global (no local "this vertex must flip"
argument suffices) and handle the edge case where only one minority
vertex exists. Likely requires a topological / homological / global
counting argument.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
"""For each chord-apex+Kempe colouring, walk K_b and record:
|
||||||
|
|
||||||
|
- The Heawood-flip count along K_b (= # consecutive (v_k, v_{k+1})
|
||||||
|
pairs with h_phi(v_k) != h_phi(v_{k+1})).
|
||||||
|
- The Heawood-flip count is at least 4 empirically (n >= 14).
|
||||||
|
|
||||||
|
For colourings achieving the minimum flip count on K_b, dump the
|
||||||
|
sequence of (Heawood, edge-position-on-cycle) so we can see *where*
|
||||||
|
the flips fall. Are they at the merged edge? At spike? At specific
|
||||||
|
structural locations? Or spread out?
|
||||||
|
|
||||||
|
Run with: sage experiments/check_min_flip_structure.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sage.all import Graph
|
||||||
|
from sage.graphs.graph_generators import graphs
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(0, HERE)
|
||||||
|
|
||||||
|
from check_conj_3_8_scaled import (
|
||||||
|
apply_reduction,
|
||||||
|
proper_3_edge_colorings,
|
||||||
|
matches_chord_apex_kempe,
|
||||||
|
trace_kempe_cycle,
|
||||||
|
edge_idx,
|
||||||
|
)
|
||||||
|
from check_heawood_on_kempe import dual_of, heawood_numbers
|
||||||
|
|
||||||
|
|
||||||
|
def test_one(D):
|
||||||
|
D.is_planar(set_embedding=True)
|
||||||
|
n_col = 0
|
||||||
|
# For each Kempe cycle (K_b, K_c), record (flip_count, length).
|
||||||
|
flips_kb = {} # flip_count -> count
|
||||||
|
# For minimum-flip colourings, record the Heawood pattern + edge colours.
|
||||||
|
min_patterns = [] # tuples (flip_count, length, pattern, colours)
|
||||||
|
|
||||||
|
cur_min = None
|
||||||
|
for face in D.faces():
|
||||||
|
if len(face) != 5: continue
|
||||||
|
for i_red in range(5):
|
||||||
|
res = apply_reduction(D, face, i_red, 9999)
|
||||||
|
if res is None: continue
|
||||||
|
H = res['H']; named = res['named']
|
||||||
|
H.is_planar(set_embedding=True)
|
||||||
|
edges, colorings = proper_3_edge_colorings(H)
|
||||||
|
cand = [c for c in colorings
|
||||||
|
if matches_chord_apex_kempe(edges, c, named)]
|
||||||
|
for col in cand:
|
||||||
|
n_col += 1
|
||||||
|
try:
|
||||||
|
h = heawood_numbers(H, edges, col)
|
||||||
|
except RuntimeError:
|
||||||
|
continue
|
||||||
|
merged_idx = edge_idx(edges, named['merged'])
|
||||||
|
a = col[merged_idx]
|
||||||
|
bs = [c for c in range(3) if c != a]
|
||||||
|
# Only check K_b for this script
|
||||||
|
walk_b = trace_kempe_cycle(edges, col, merged_idx, (a, bs[0]))
|
||||||
|
L = len(walk_b)
|
||||||
|
h_seq = [h[walk_b[k][1]] for k in range(L)]
|
||||||
|
# edge sequence: walk_b[k][0] is the edge index of the K_b
|
||||||
|
# edge ENTERING walk_b[k][1].
|
||||||
|
e_colors = [col[walk_b[k][0]] for k in range(L)]
|
||||||
|
# Flip count: pairs (h_seq[k], h_seq[(k+1) % L]) that differ.
|
||||||
|
flips = sum(1 for k in range(L) if h_seq[k] != h_seq[(k+1) % L])
|
||||||
|
flips_kb[flips] = flips_kb.get(flips, 0) + 1
|
||||||
|
if cur_min is None or flips < cur_min:
|
||||||
|
cur_min = flips
|
||||||
|
min_patterns = [(flips, L, tuple(h_seq), tuple(e_colors))]
|
||||||
|
elif flips == cur_min and len(min_patterns) < 5:
|
||||||
|
min_patterns.append((flips, L, tuple(h_seq), tuple(e_colors)))
|
||||||
|
return n_col, flips_kb, cur_min, min_patterns
|
||||||
|
|
||||||
|
|
||||||
|
def main(max_n=18, time_budget_per_n=1800):
|
||||||
|
print(f"Min-flip Heawood pattern on K_b, n in [12, {max_n}]\n")
|
||||||
|
overall_min = None
|
||||||
|
overall_min_examples = []
|
||||||
|
for n in range(12, max_n + 1):
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
triangulations = list(graphs.triangulations(n, minimum_degree=5))
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"n={n}: cannot enumerate ({ex})")
|
||||||
|
continue
|
||||||
|
n_col_n = 0
|
||||||
|
flips_n = {}
|
||||||
|
n_min = None
|
||||||
|
min_examples = []
|
||||||
|
for tri_idx, G in enumerate(triangulations):
|
||||||
|
if time.time() - start > time_budget_per_n:
|
||||||
|
print(f" n={n}: timeout at tri {tri_idx}/{len(triangulations)}")
|
||||||
|
break
|
||||||
|
G.is_planar(set_embedding=True)
|
||||||
|
D = dual_of(G)
|
||||||
|
ni, fi, cm, exs = test_one(D)
|
||||||
|
n_col_n += ni
|
||||||
|
for k, v in fi.items(): flips_n[k] = flips_n.get(k, 0) + v
|
||||||
|
if cm is not None:
|
||||||
|
if n_min is None or cm < n_min:
|
||||||
|
n_min = cm
|
||||||
|
min_examples = list(exs)
|
||||||
|
elif cm == n_min and len(min_examples) < 5:
|
||||||
|
min_examples.extend(exs[:5 - len(min_examples)])
|
||||||
|
elapsed = time.time() - start
|
||||||
|
print(f"n={n}: {n_col_n} col., flip_dist on K_b: "
|
||||||
|
f"{sorted(flips_n.items())}, min flip: {n_min} [{elapsed:.0f}s]")
|
||||||
|
if min_examples:
|
||||||
|
print(f" Examples of min-flip K_b (flip count = {n_min}):")
|
||||||
|
for ex in min_examples[:3]:
|
||||||
|
flips_x, L, h_pat, e_cols = ex
|
||||||
|
print(f" L={L}, flips={flips_x}")
|
||||||
|
print(f" h sequence : {h_pat}")
|
||||||
|
print(f" colour seq : {e_cols}")
|
||||||
|
sys.stdout.flush()
|
||||||
|
if overall_min is None or (n_min is not None and n_min < overall_min):
|
||||||
|
overall_min = n_min
|
||||||
|
overall_min_examples = min_examples
|
||||||
|
print()
|
||||||
|
print(f"Overall minimum flip count on K_b across all tested: {overall_min}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
"""For colourings whose K_b Heawood pattern is "almost constant" (flip
|
||||||
|
count <= some threshold), identify *which* vertex carries the
|
||||||
|
minority Heawood. Is it consistently a "named" structural vertex
|
||||||
|
(v_n, A_{i+1}, A_{i+3}, A_{i+4}, ...) or distributed across
|
||||||
|
non-structural vertices?
|
||||||
|
|
||||||
|
If the minority vertex is always a specific structural vertex, that
|
||||||
|
gives a *local* structural proof: the embedding + chord-apex forces
|
||||||
|
the minority's Heawood to differ from the majority's, ruling out
|
||||||
|
constancy on V(K_b).
|
||||||
|
|
||||||
|
Run with: sage experiments/check_minority_location.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sage.all import Graph
|
||||||
|
from sage.graphs.graph_generators import graphs
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(0, HERE)
|
||||||
|
|
||||||
|
from check_conj_3_8_scaled import (
|
||||||
|
apply_reduction,
|
||||||
|
proper_3_edge_colorings,
|
||||||
|
matches_chord_apex_kempe,
|
||||||
|
trace_kempe_cycle,
|
||||||
|
edge_idx,
|
||||||
|
kempe_cycle_set,
|
||||||
|
)
|
||||||
|
from check_heawood_on_kempe import dual_of, heawood_numbers, vertices_of_kempe
|
||||||
|
|
||||||
|
|
||||||
|
def named_vertices(named, v_n=9999):
|
||||||
|
def other(fs, v):
|
||||||
|
return next(iter(fs - {v}))
|
||||||
|
A_i = other(named['side_0'], v_n)
|
||||||
|
A_i1 = other(named['spike'], v_n)
|
||||||
|
A_i2 = other(named['side_1'], v_n)
|
||||||
|
A_i3, A_i4 = sorted(named['merged'])
|
||||||
|
return {'v_n': v_n, 'A_i': A_i, 'A_i1': A_i1, 'A_i2': A_i2,
|
||||||
|
'A_i3': A_i3, 'A_i4': A_i4}
|
||||||
|
|
||||||
|
|
||||||
|
def test_one(D, flip_threshold=4):
|
||||||
|
D.is_planar(set_embedding=True)
|
||||||
|
n_col = 0
|
||||||
|
# For colourings with K_b flip count <= flip_threshold, record what
|
||||||
|
# type of vertex carries the minority h on V(K_b).
|
||||||
|
minority_types = {} # 'v_n', 'A_i1', etc., or 'other' -> count
|
||||||
|
# Also: number of minority vertices on V(K_b) in these colourings.
|
||||||
|
minority_size_dist = {}
|
||||||
|
for face in D.faces():
|
||||||
|
if len(face) != 5: continue
|
||||||
|
for i_red in range(5):
|
||||||
|
res = apply_reduction(D, face, i_red, 9999)
|
||||||
|
if res is None: continue
|
||||||
|
H = res['H']; named = res['named']
|
||||||
|
H.is_planar(set_embedding=True)
|
||||||
|
edges, colorings = proper_3_edge_colorings(H)
|
||||||
|
cand = [c for c in colorings
|
||||||
|
if matches_chord_apex_kempe(edges, c, named)]
|
||||||
|
named_v = named_vertices(named, 9999)
|
||||||
|
inv_named = {v: name for name, v in named_v.items()}
|
||||||
|
for col in cand:
|
||||||
|
n_col += 1
|
||||||
|
try:
|
||||||
|
h = heawood_numbers(H, edges, col)
|
||||||
|
except RuntimeError:
|
||||||
|
continue
|
||||||
|
merged_idx = edge_idx(edges, named['merged'])
|
||||||
|
a = col[merged_idx]
|
||||||
|
bs = [c for c in range(3) if c != a]
|
||||||
|
kc_b = kempe_cycle_set(edges, col, merged_idx, (a, bs[0]))
|
||||||
|
V_b = vertices_of_kempe(edges, kc_b)
|
||||||
|
# Compute flip count on K_b walk
|
||||||
|
walk = trace_kempe_cycle(edges, col, merged_idx, (a, bs[0]))
|
||||||
|
L = len(walk)
|
||||||
|
h_seq = [h[walk[k][1]] for k in range(L)]
|
||||||
|
flips = sum(1 for k in range(L) if h_seq[k] != h_seq[(k+1) % L])
|
||||||
|
if flips > flip_threshold: continue
|
||||||
|
# Identify minority on V(K_b)
|
||||||
|
plus = sum(1 for v in V_b if h[v] == 1)
|
||||||
|
minus = sum(1 for v in V_b if h[v] == -1)
|
||||||
|
if plus == minus: continue # tie -- skip
|
||||||
|
minority_h = 1 if plus < minus else -1
|
||||||
|
minority_vs = [v for v in V_b if h[v] == minority_h]
|
||||||
|
minority_size_dist[len(minority_vs)] = \
|
||||||
|
minority_size_dist.get(len(minority_vs), 0) + 1
|
||||||
|
for v in minority_vs:
|
||||||
|
name = inv_named.get(v, 'other')
|
||||||
|
minority_types[name] = minority_types.get(name, 0) + 1
|
||||||
|
return n_col, minority_types, minority_size_dist
|
||||||
|
|
||||||
|
|
||||||
|
def main(max_n=18, time_budget_per_n=1800, flip_threshold=4):
|
||||||
|
print(f"Minority-vertex location on K_b for low-flip colourings, "
|
||||||
|
f"n in [12, {max_n}], flip <= {flip_threshold}\n")
|
||||||
|
grand_col = 0
|
||||||
|
grand_types = {}
|
||||||
|
grand_sizes = {}
|
||||||
|
for n in range(12, max_n + 1):
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
triangulations = list(graphs.triangulations(n, minimum_degree=5))
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"n={n}: cannot enumerate ({ex})")
|
||||||
|
continue
|
||||||
|
n_col_n = 0
|
||||||
|
for tri_idx, G in enumerate(triangulations):
|
||||||
|
if time.time() - start > time_budget_per_n:
|
||||||
|
print(f" n={n}: timeout at tri {tri_idx}/{len(triangulations)}")
|
||||||
|
break
|
||||||
|
G.is_planar(set_embedding=True)
|
||||||
|
D = dual_of(G)
|
||||||
|
ni, mt, ms = test_one(D, flip_threshold)
|
||||||
|
n_col_n += ni
|
||||||
|
for k, v in mt.items(): grand_types[k] = grand_types.get(k, 0) + v
|
||||||
|
for k, v in ms.items(): grand_sizes[k] = grand_sizes.get(k, 0) + v
|
||||||
|
elapsed = time.time() - start
|
||||||
|
print(f"n={n}: {n_col_n} col., [{elapsed:.0f}s]")
|
||||||
|
sys.stdout.flush()
|
||||||
|
grand_col += n_col_n
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print(f"Grand totals: colourings with K_b flip-count <= {flip_threshold} "
|
||||||
|
f"({grand_col} total colourings)")
|
||||||
|
total_min = sum(grand_sizes.values())
|
||||||
|
print(f"\n Minority-set size distribution on V(K_b) "
|
||||||
|
f"(of low-flip colourings): {sorted(grand_sizes.items())}")
|
||||||
|
print(f"\n Type breakdown of minority vertices "
|
||||||
|
f"(over {sum(grand_types.values())} minority-vertex incidences):")
|
||||||
|
for name in ['v_n', 'A_i', 'A_i1', 'A_i2', 'A_i3', 'A_i4', 'other']:
|
||||||
|
c = grand_types.get(name, 0)
|
||||||
|
pct = 100 * c / max(1, sum(grand_types.values()))
|
||||||
|
print(f" {name:>5}: {c} ({pct:.2f}%)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user