diff --git a/papers/face_monochromatic_pairs/experiments/check_S8_hit8_pG.py b/papers/face_monochromatic_pairs/experiments/check_S8_hit8_pG.py new file mode 100644 index 0000000..6967d37 --- /dev/null +++ b/papers/face_monochromatic_pairs/experiments/check_S8_hit8_pG.py @@ -0,0 +1,151 @@ +"""For |S| = 8 bad colourings with hit = 8 G'-pentagons, +report the distribution of p_G (# total G'-pentagons in the reduced +dual). The structural claim is that p_G ≥ 9 always in this sub-case. + +If p_G ≥ 9 always: hit = 8 < p_G ≥ 9, so ≥ 1 G'-pentagon uncovered. ✓ +If p_G = 8 sometimes: those would be true structural gaps. + +Run with: sage experiments/check_S8_hit8_pG.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, + kempe_cycle_set, + edge_idx, +) +from check_heawood_on_kempe import dual_of, vertices_of_kempe + + +def is_g_prime_pentagon(f, named): + if len(f) != 5: return False + fset = {frozenset(e) for e in f} + return not (named['side_0'] in fset or named['side_1'] in fset + or named['spike'] in fset or named['merged'] in fset) + + +def test_one(D, n_G, tri_idx): + D.is_planar(set_embedding=True) + s8_hit8_cases = [] # list of (p_G, parent_v_cyc_degs) + # Get cyclic degree sequence of triangulation vertices for the + # face we're examining + 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 = 9999 + for col in cand: + target = {named['side_0'], named['spike']} + lower_flank = None + for f in H.faces(): + if target.issubset({frozenset(e) for e in f}): + lower_flank = f; break + if lower_flank is None or len(lower_flank) != 5: continue + arc_verts = [e[0] for e in lower_flank] + if v_n not in arc_verts: continue + k = arc_verts.index(v_n) + cyc = arc_verts[k:] + arc_verts[:k] + A_i = next(iter(named['side_0'] - {v_n})) + A_ip1 = next(iter(named['spike'] - {v_n})) + if cyc[1] == A_i and cyc[4] == A_ip1: + P_1, P_2 = cyc[2], cyc[3] + elif cyc[1] == A_ip1 and cyc[4] == A_i: + P_2, P_1 = cyc[2], cyc[3] + else: continue + merged_idx = edge_idx(edges, named['merged']) + c_col = col[merged_idx] + c_0_col = col[edge_idx(edges, named['side_0'])] + c_1_col = col[edge_idx(edges, named['side_1'])] + e_AiP1 = edge_idx(edges, frozenset((A_i, P_1))) + e_P1P2 = edge_idx(edges, frozenset((P_1, P_2))) + if e_AiP1 is None or e_P1P2 is None: continue + if col[e_AiP1] != c_1_col or col[e_P1P2] != c_0_col: + continue + a = c_col + other = [x for x in range(3) if x != a] + kc_b = kempe_cycle_set(edges, col, merged_idx, (a, other[0])) + kc_c = kempe_cycle_set(edges, col, merged_idx, (a, other[1])) + V_b = vertices_of_kempe(edges, kc_b) + V_c = vertices_of_kempe(edges, kc_c) + V_union = V_b | V_c + S = set(H.vertices()) - V_union + if P_1 in V_union: continue + if len(S) != 8: continue + + p_total = 0 + p_hit = 0 + for f in H.faces(): + if not is_g_prime_pentagon(f, named): continue + p_total += 1 + verts = {u for (u, v) in f} | {v for (u, v) in f} + if verts & S: p_hit += 1 + + if p_hit == 8: + s8_hit8_cases.append({ + 'p_G': p_total, 'n_G': n_G, 'tri_idx': tri_idx, + 'i_red': i_red, + }) + return s8_hit8_cases + + +def main(max_n=20, time_budget_per_n=1800): + print("|S| = 8 colourings with hit = 8: p_G distribution\n") + grand_cases = [] + 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_count = 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}") + break + G.is_planar(set_embedding=True) + D = dual_of(G) + cases = test_one(D, n, tri_idx) + grand_cases.extend(cases) + n_count += len(cases) + elapsed = time.time() - start + print(f"n={n}: {n_count} (|S|=8, hit=8) cases [{elapsed:.0f}s]") + sys.stdout.flush() + print() + print("=" * 70) + print(f"Total |S|=8 hit=8 cases: {len(grand_cases)}") + p_G_dist = {} + for c in grand_cases: + p_G_dist[c['p_G']] = p_G_dist.get(c['p_G'], 0) + 1 + print("\np_G distribution among |S|=8 hit=8 cases:") + for p in sorted(p_G_dist): + print(f" p_G = {p}: {p_G_dist[p]}") + if 8 in p_G_dist and p_G_dist[8] > 0: + print("\n⚠ STRUCTURAL GAP: p_G = 8 occurs with hit = 8") + print(" So the G'-pentagon fallback is genuinely contradicted") + print(" in these cases.") + else: + print("\n✓ p_G = 8 NEVER co-occurs with hit = 8. So in |S| = 8") + print(" bad colourings, p_G ≥ 9 whenever hit = 8, giving") + print(" ≥ 1 uncovered G'-pentagon. The fallback is empirically") + print(" closed.") + + +if __name__ == '__main__': + main() diff --git a/papers/face_monochromatic_pairs/paper.pdf b/papers/face_monochromatic_pairs/paper.pdf index 65041e9..cd30bb8 100644 Binary files a/papers/face_monochromatic_pairs/paper.pdf and b/papers/face_monochromatic_pairs/paper.pdf differ diff --git a/papers/face_monochromatic_pairs/paper.tex b/papers/face_monochromatic_pairs/paper.tex index 4a16263..d2c2911 100644 --- a/papers/face_monochromatic_pairs/paper.tex +++ b/papers/face_monochromatic_pairs/paper.tex @@ -1347,14 +1347,20 @@ $8$ & $252$ & $8$ & $\boldsymbol{9}$\,$^\dag$ & $1$ \\ $10$ & $36$ & $7$ & $8$ & $1$ \\ \end{tabular} \end{center} -$^\dag$ Empirically, the combination $|S| = 8$ with \# pent. hit $= 8$ -\emph{and} $p_G = 8$ never occurs --- so although both $\text{hit} = 8$ -and $p_G = 8$ are individually possible at $|S| = 8$, they are never -simultaneous. (\texttt{experiments/check\_30\_residual\_v2.py}.) -This is a structural fact about chord-apex+Kempe colourings that we -do not have a non-empirical proof of, but its empirical confirmation -shows the G'-pentagon fallback closes the $|S| = 8$ case along with -the other $|S|$ values. +$^\dag$ Empirically, every chord-apex+Kempe colouring with $|S| = 8$ +and \# pent. hit $= 8$ has $p_G = 11$ exactly --- not merely $p_G \ge 9$. +That is, every one of the $30$ cases that achieves hit $= 8$ at $|S| = 8$ +has all $5$ of $v$'s neighbours in the parent triangulation of +degree $\ge 6$, so no $F_k$ adjacent to $F_v$ is pentagonal, leaving +$p_G = 12 - 1 - 0 = 11$ G'-pentagons in the reduced dual. So when +hit $= 8$, $3$ G'-pentagons are uncovered --- +\emph{not merely~$1$}. (\texttt{experiments/check\_S8\_hit8\_pG.py}.) +This is a much stronger structural regularity than we expected, +suggesting that in chord-apex+Kempe colourings, the size of +$S = V \setminus (V(K_b) \cup V(K_c))$ is structurally tied to +the count of pentagonal $F_k$ adjacent to $F_v$ in a way that always +leaves G'-pentagons uncovered. A structural (non-empirical) proof of +this regularity is open. Combining Theorem~\ref{thm:deciding-face-partial-extended} with the empirical structural facts above, the $G'$-pentagon fallback