4ceae9c68a
Rename the shared helper module to a number-resistant name. Update all 26 dependent scripts via sed. Add experiments/test_n_21_to_24.py — extends the empirical check beyond |V(G)| ≤ 20 to n_G ∈ [21, 24]. Checks per chord-apex+Kempe colouring: (1) h_φ constant on V(K_b)? (counterexample to Corollary 5.4) (2) h_φ constant on V(K_b) ∪ V(K_c)? (counterexample to Conj 5.1) (3) Deciding face exists? Writes results incrementally to test_n_21_to_24_results.jsonl (one JSON line per triangulation, plus n-level and grand summaries). Emits PROGRESS lines every 10 minutes (default) to stdout for live monitoring. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
130 lines
5.0 KiB
Python
130 lines
5.0 KiB
Python
"""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_final_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()
|