"""For every chord-apex+Kempe colouring of every reduced dual (up to |V(G)| ≤ 18), check whether there exists a "deciding face": a face f of the reduced dual whose boundary lies entirely in V(K_b) ∪ V(K_c) and whose length is not divisible by 3. If a deciding face exists, then under the (hypothetical) constancy hypothesis h_φ ≡ ε on V(K_b) ∪ V(K_c), Heawood's face-sum identity gives ε · |f| ≡ 0 (mod 3), which forces ε ≡ 0 since |f| ≢ 0 (mod 3) -- contradicting ε ∈ {±1}. So existence of a deciding face in every chord-apex+Kempe colouring of every reduced dual would *prove* Conjecture 5.1 via the contrapositive of Lemma 5.3. This script reports, per (n_G, colouring): does a deciding face exist? And if not, why not (insufficient coverage, all in-coverage faces have length ≡ 0 mod 3, etc.). Run with: sage experiments/check_deciding_face.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, kempe_cycle_set, edge_idx, ) from check_heawood_on_kempe import ( dual_of, vertices_of_kempe, ) def find_deciding_face(H, V_union): """Return (face, |face| mod 3) of a deciding face (all boundary in V_union, length not divisible by 3). None if no such face exists.""" for face in H.faces(): verts = {u for (u, v) in face} | {v for (u, v) in face} if not verts.issubset(V_union): continue L = len(face) if L % 3 != 0: return face, L, L % 3 return None def test_one(D): D.is_planar(set_embedding=True) n_col = 0 n_with_deciding = 0 no_deciding_examples = [] deciding_lengths = {} # |f| of deciding face -> count 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 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])) kc_c = kempe_cycle_set(edges, col, merged_idx, (a, bs[1])) V_b = vertices_of_kempe(edges, kc_b) V_c = vertices_of_kempe(edges, kc_c) V_union = V_b | V_c deciding = find_deciding_face(H, V_union) if deciding is not None: n_with_deciding += 1 _, L, mod_r = deciding deciding_lengths[L] = deciding_lengths.get(L, 0) + 1 else: cov_ratio = len(V_union) / H.order() in_cov_face_lens = sorted([ len(f) for f in H.faces() if {u for (u, v) in f} | {v for (u, v) in f} <= V_union]) if len(no_deciding_examples) < 3: no_deciding_examples.append({ 'cov_ratio': cov_ratio, 'in_cov_face_lens': in_cov_face_lens, 'V_union_size': len(V_union), 'V_total': H.order(), }) return n_col, n_with_deciding, deciding_lengths, no_deciding_examples def main(max_n=20, time_budget_per_n=1800): print(f"Deciding-face existence on chord-apex+Kempe colourings, " f"n_G ∈ [12, {max_n}]\n") grand_col = 0 grand_dec = 0 grand_lens = {} grand_no_dec_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 n_dec_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, ndec, lens, no_dec_ex = test_one(D) n_col_n += ni n_dec_n += ndec for k, v in lens.items(): grand_lens[k] = grand_lens.get(k, 0) + v if len(grand_no_dec_examples) < 5: grand_no_dec_examples.extend( no_dec_ex[:5 - len(grand_no_dec_examples)]) elapsed = time.time() - start pct = 100 * n_dec_n / max(n_col_n, 1) print(f"n={n}: {n_col_n} col., {n_dec_n} with deciding face " f"({pct:.2f}%) [{elapsed:.0f}s]") sys.stdout.flush() grand_col += n_col_n grand_dec += n_dec_n print() print("=" * 70) print(f"Grand totals: {grand_col} chord-apex+Kempe colourings") pct = 100 * grand_dec / max(grand_col, 1) print(f" with deciding face: {grand_dec} ({pct:.2f}%)") print(f" without : {grand_col - grand_dec} " f"({100 - pct:.2f}%)") if grand_lens: print() print(" Deciding face length distribution:") for L in sorted(grand_lens): print(f" |f| = {L} (mod 3 = {L % 3}): {grand_lens[L]}") if grand_no_dec_examples: print() print(" Sample colourings WITHOUT a deciding face:") for ex in grand_no_dec_examples[:5]: print(f" coverage = {ex['V_union_size']}/{ex['V_total']} " f"({100*ex['cov_ratio']:.0f}%); in-coverage face " f"lengths = {ex['in_cov_face_lens']}") if __name__ == '__main__': main()