"""For every chord-apex+Kempe colouring, record whether each of a set of named-vertex pairs has differing Heawood numbers. If a pair *always* disagrees, that gives a structural identity h_phi(u) != h_phi(v) that can be plugged into Lemma 5.3 to prove Conjecture 5.1. Pairs we track (all lie in V(K_b) cap V(K_c)): (v_n, A_i), (v_n, A_{i+1}), (v_n, A_{i+2}), (v_n, A_{i+3}), (v_n, A_{i+4}), (A_i, A_{i+1}), (A_{i+1}, A_{i+2}), (A_{i+2}, A_{i+3}), (A_{i+3}, A_{i+4}), (A_{i+4}, A_i), (A_i, A_{i+2}), (A_i, A_{i+3}), (A_i, A_{i+4}), (A_{i+1}, A_{i+3}), (A_{i+1}, A_{i+4}), (A_{i+2}, A_{i+4}) Run with: sage experiments/check_heawood_pair_mismatch.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, ) from check_heawood_on_kempe import dual_of, heawood_numbers def extract_named_vertices(named, v_n_label=9999): """Pull the labels A_i ... A_{i+4} out of the named-edge dict. named['side_0'] = frozenset((v_n, A_i)) named['spike'] = frozenset((v_n, A_{i+1})) named['side_1'] = frozenset((v_n, A_{i+2})) named['merged'] = frozenset((A_{i+3}, A_{i+4})) """ def other(edge_fs, v): return next(iter(edge_fs - {v})) A_i = other(named['side_0'], v_n_label) A_i1 = other(named['spike'], v_n_label) A_i2 = other(named['side_1'], v_n_label) merged_set = named['merged'] # A_{i+3}, A_{i+4} are the two endpoints of merged; we can't tell them # apart from the named dict alone, so we just pick an ordering. a, b = sorted(merged_set) A_i3, A_i4 = a, b return v_n_label, A_i, A_i1, A_i2, A_i3, A_i4 def test_one(D): """Tally pair-mismatches over all chord-apex+Kempe colourings of D.""" D.is_planar(set_embedding=True) n_col = 0 pair_diff = {} # pair_label -> count of colourings where h differs pair_same = {} 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)] v_n, A_i, A_i1, A_i2, A_i3, A_i4 = extract_named_vertices(named) named_pairs = [ ('v_n, A_i', v_n, A_i ), ('v_n, A_{i+1}', v_n, A_i1), ('v_n, A_{i+2}', v_n, A_i2), ('v_n, A_{i+3}', v_n, A_i3), ('v_n, A_{i+4}', v_n, A_i4), ('A_i, A_{i+1}', A_i, A_i1), ('A_{i+1}, A_{i+2}', A_i1, A_i2), ('A_{i+2}, A_{i+3}', A_i2, A_i3), ('A_{i+3}, A_{i+4}', A_i3, A_i4), ('A_{i+4}, A_i', A_i4, A_i ), ('A_i, A_{i+2}', A_i, A_i2), ('A_i, A_{i+3}', A_i, A_i3), ('A_i, A_{i+4}', A_i, A_i4), ('A_{i+1}, A_{i+3}', A_i1, A_i3), ('A_{i+1}, A_{i+4}', A_i1, A_i4), ('A_{i+2}, A_{i+4}', A_i2, A_i4), ] for col in cand: n_col += 1 try: h = heawood_numbers(H, edges, col) except RuntimeError: continue for label, u, w in named_pairs: if h[u] != h[w]: pair_diff[label] = pair_diff.get(label, 0) + 1 else: pair_same[label] = pair_same.get(label, 0) + 1 return n_col, pair_diff, pair_same def main(max_n=18, time_budget_per_n=1800): print(f"Pair-mismatch check on chord-apex+Kempe colourings, " f"n in [12, {max_n}]\n") grand_col = 0 grand_diff = {} grand_same = {} 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 diff_n = {}; same_n = {} 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) n_col_i, diff_i, same_i = test_one(D) n_col_n += n_col_i for k, v in diff_i.items(): diff_n[k] = diff_n.get(k, 0) + v for k, v in same_i.items(): same_n[k] = same_n.get(k, 0) + v elapsed = time.time() - start print(f"n={n}: {n_col_n} colourings [{elapsed:.0f}s]") sys.stdout.flush() grand_col += n_col_n for k, v in diff_n.items(): grand_diff[k] = grand_diff.get(k, 0) + v for k, v in same_n.items(): grand_same[k] = grand_same.get(k, 0) + v print() print("=" * 78) print(f"Grand totals (n in [12, {max_n}], {grand_col} colourings)") print("-" * 78) print(f"{'pair':<22} {'#diff':>10} {'#same':>10} {'always diff?':>14}") print("-" * 78) # Stable order: track all keys seen. all_keys = sorted(set(list(grand_diff.keys()) + list(grand_same.keys()))) for k in all_keys: d = grand_diff.get(k, 0); s = grand_same.get(k, 0) always_diff = (s == 0 and d > 0) marker = ' YES <==' if always_diff else '' print(f"{k:<22} {d:>10} {s:>10} {marker:>14}") if __name__ == '__main__': main()