Files
math-research/papers/face_monochromatic_pairs/experiments/check_kb_kc_coverage.py
T
didericis d7e9b6af2f face_monochromatic_pairs: reduce Conj 5.1 to a "deciding face" conjecture
NEW PROOF STRATEGY for Conjecture 5.1 (face-monochromatic-pair):

1. NEW Conjecture (Deciding face): For every chord-apex+Kempe
   colouring φ of every reduced dual, the reduced dual has a face f
   with ∂f ⊆ V(K_b) ∪ V(K_c) and |f| ≢ 0 (mod 3).

2. NEW Theorem: Deciding-face conjecture implies Conj 5.1.

   Proof: contradiction. Assume no clauses-(1)-(3) witness for some
   chord-apex+Kempe φ. By Lemma 5.3, h_φ ≡ ε ∈ {±1} on V(K_b) ∪ V(K_c).
   By the deciding-face conjecture, ∃ face f with ∂f ⊆ V(K_b) ∪ V(K_c),
   |f| ≢ 0 (mod 3). Heawood's face-sum identity (Heawood 1898) gives
   Σ_{v ∈ ∂f} h_φ(v) = ε|f| ≡ 0 (mod 3). Since gcd(|f|, 3) = 1, we get
   ε ≡ 0 (mod 3), but ε ∈ {±1} — contradiction.

3. EMPIRICAL: Conjecture (Deciding face) verified on 142,812 / 142,812
   chord-apex+Kempe colourings of reduced duals up to |V(G)| ≤ 20 --
   matching the full coverage of check_constancy_obstruction.py.
   Face-length distribution:
     |f| = 4:  13,074
     |f| = 5: 102,498 (most common)
     |f| = 7:  18,570
     |f| = 8:   7,752
     |f| = 10:    846
     |f| = 11:     72
   (All ≢ 0 mod 3.)

New scripts:
  - check_kb_kc_coverage.py: |V(K_b) ∪ V(K_c)| / |V(Ĝ')| distribution.
    73.87% of colourings have V(K_b) ∪ V(K_c) = V (full coverage); the
    remaining 26% have coverage ≥ 70%, mostly ≥ 90%.
  - check_deciding_face.py: existence of deciding face across all
    colourings; 100.00% / 142,812.

Why this is the right reduction:
  - It uses ALL THREE pieces of chord-apex+Kempe structure: Lemma 5.3
    (constancy from no-witness), forced colour-equality at merged/spike,
    and forced Kempe-cycle containment of merged + spike + side edges
    (the latter two enter via V(K_b) ∪ V(K_c) covering specific
    structural vertices).
  - It uses Heawood's face-sum identity, which is the classical 3-fold
    parity constraint on cubic plane 3-edge-colourings.
  - The C28 counterexample to Conjecture 5.5 is not affected: it's not
    a chord-apex+Kempe colouring of a reduced dual, so the deciding-face
    structure doesn't apply.

Remaining work: prove the deciding-face conjecture structurally (likely
via the specific F_01 / F_12 "flank face" of the reduced dual, whose
length n_0 - 1 from the adjacent G'-face of length n_0 ≥ 5 is ≢ 0 mod 3
exactly when n_0 ≢ 1 mod 3, plus boundary-in-V(K_b) ∪ V(K_c) which
follows from Lemma 5.X kempe-spike + colour analysis at A_i).

Paper grows from 17 to 18 pages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 04:12:09 -04:00

133 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Check what fraction of V(Ĝ'_{v,i}) is covered by V(K_b) V(K_c)
across all chord-apex+Kempe colourings of all reduced duals up to
|V(G)| ≤ 18.
This is the key prerequisite for the Heawood-face-sum proof attempt:
if V(K_b) V(K_c) = V(reduced dual) in many cases, then under
constancy on V(K_b) V(K_c) every face of the reduced dual must
satisfy ε * |f| ≡ 0 (mod 3); since ε = ±1, this forces |f| ≡ 0 mod 3
for every face -- and any reduced dual with a face of length 4, 5, or
7 (etc., not multiple of 3) would yield an immediate contradiction.
Run with: sage experiments/check_kb_kc_coverage.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, heawood_numbers, vertices_of_kempe,
)
def test_one(D):
D.is_planar(set_embedding=True)
n_col = 0
coverage_dist = {} # |V(K_b)V(K_c)| / |V| as ratio (rounded) -> count
full_coverage = 0
face_lengths_at_full = {} # face length distribution when full coverage
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)]
n_v = H.order()
face_lens = sorted([len(f) for f in H.faces()])
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)
cov = len(V_b | V_c)
cov_ratio = cov / n_v
bucket = round(cov_ratio * 20) / 20 # bucket in 5% increments
coverage_dist[bucket] = coverage_dist.get(bucket, 0) + 1
if cov == n_v:
full_coverage += 1
key = tuple(face_lens)
face_lengths_at_full[key] = (
face_lengths_at_full.get(key, 0) + 1)
return n_col, coverage_dist, full_coverage, face_lengths_at_full
def main(max_n=18, time_budget_per_n=300):
print(f"V(K_b) V(K_c) coverage / V(Ĝ'_{{v,i}}) "
f"on chord-apex+Kempe colourings, n_G ∈ [12, {max_n}]\n")
grand_col = 0
grand_dist = {}
grand_full = 0
grand_full_faces = {}
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_full_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, dist, full, full_faces = test_one(D)
n_col_n += ni
n_full_n += full
for k, v in dist.items():
grand_dist[k] = grand_dist.get(k, 0) + v
for k, v in full_faces.items():
grand_full_faces[k] = grand_full_faces.get(k, 0) + v
elapsed = time.time() - start
print(f"n={n}: {n_col_n} col., {n_full_n} full-coverage "
f"({100 * n_full_n / max(n_col_n, 1):.1f}%) [{elapsed:.0f}s]")
sys.stdout.flush()
grand_col += n_col_n
grand_full += n_full_n
print()
print("=" * 70)
print(f"Grand totals: {grand_col} chord-apex+Kempe colourings")
print(f" full coverage (V(K_b)V(K_c) = V): {grand_full} "
f"({100 * grand_full / max(grand_col, 1):.2f}%)")
print()
print(" Coverage ratio distribution "
"(|V(K_b)V(K_c)| / |V(Ĝ')|):")
for r in sorted(grand_dist):
c = grand_dist[r]
pct = 100 * c / max(grand_col, 1)
bar = '#' * max(1, int(pct))
print(f" {r:5.2f}: {c:>7} ({pct:5.2f}%) {bar[:50]}")
if grand_full > 0:
print()
print(" Face-length distributions among full-coverage cases:")
for key in sorted(grand_full_faces, key=lambda k: -grand_full_faces[k]):
print(f" {key}: {grand_full_faces[key]}")
if __name__ == '__main__':
main()