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>
167 lines
6.8 KiB
Python
167 lines
6.8 KiB
Python
"""For the 1,314 bad chord-apex+Kempe colourings, check whether
|
|
S-vertices form connected subgraphs (= are adjacent to each other),
|
|
which would explain why their pentagon-coverage is below 3|S|.
|
|
|
|
For each bad colouring:
|
|
- Compute |S| and the induced subgraph H[S].
|
|
- # connected components of H[S].
|
|
- Edges in H[S] (= S-vertex pairs sharing an edge).
|
|
- For each pair of adjacent S-vertices: how many G'-pentagons they
|
|
share (= "overlap" that reduces pigeonhole bound).
|
|
|
|
Run with: sage experiments/check_S_adjacency.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 test_one(D):
|
|
D.is_planar(set_embedding=True)
|
|
bad_count = 0
|
|
S_components_dist = {} # (|S|, # connected components in H[S]) -> count
|
|
S_edges_dist = {} # (|S|, # edges in H[S]) -> count
|
|
pentagon_overlap_dist = {} # (|S|, max overlap) -> 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)]
|
|
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
|
|
bad_count += 1
|
|
S_size = len(S)
|
|
# H[S] = induced subgraph
|
|
HS = H.subgraph(S)
|
|
comps = HS.connected_components()
|
|
key = (S_size, len(comps))
|
|
S_components_dist[key] = S_components_dist.get(key, 0) + 1
|
|
# Edges in H[S]
|
|
n_edges = HS.size()
|
|
key2 = (S_size, n_edges)
|
|
S_edges_dist[key2] = S_edges_dist.get(key2, 0) + 1
|
|
# Pentagon overlap: for each pair of adjacent S-vertices,
|
|
# count their shared G'-pentagons.
|
|
max_overlap = 0
|
|
for u in S:
|
|
for v_other in H.neighbors(u):
|
|
if v_other not in S or v_other <= u:
|
|
continue
|
|
# Find G'-pentagons containing both u and v_other
|
|
n_shared = 0
|
|
for f in H.faces():
|
|
if len(f) != 5: continue
|
|
verts = {a for a, b in f} | {b for a, b in f}
|
|
if u in verts and v_other in verts:
|
|
n_shared += 1
|
|
max_overlap = max(max_overlap, n_shared)
|
|
key3 = (S_size, max_overlap)
|
|
pentagon_overlap_dist[key3] = (
|
|
pentagon_overlap_dist.get(key3, 0) + 1)
|
|
return (bad_count, S_components_dist, S_edges_dist, pentagon_overlap_dist)
|
|
|
|
|
|
def main(max_n=20, time_budget_per_n=1800):
|
|
print("Connectivity structure of S-vertices in bad colourings.\n")
|
|
grand_bad = 0
|
|
grand_comps = {}
|
|
grand_edges = {}
|
|
grand_overlap = {}
|
|
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_bad_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}")
|
|
break
|
|
G.is_planar(set_embedding=True)
|
|
D = dual_of(G)
|
|
nb, sc, se, po = test_one(D)
|
|
n_bad_n += nb
|
|
for k, v in sc.items(): grand_comps[k] = grand_comps.get(k, 0) + v
|
|
for k, v in se.items(): grand_edges[k] = grand_edges.get(k, 0) + v
|
|
for k, v in po.items(): grand_overlap[k] = grand_overlap.get(k, 0) + v
|
|
elapsed = time.time() - start
|
|
print(f"n={n}: {n_bad_n} bad colourings [{elapsed:.0f}s]")
|
|
sys.stdout.flush()
|
|
grand_bad += n_bad_n
|
|
print()
|
|
print("=" * 70)
|
|
print(f"Total bad colourings: {grand_bad}")
|
|
print("\n(|S|, # connected components in H[S]) distribution:")
|
|
for k in sorted(grand_comps):
|
|
c = grand_comps[k]
|
|
print(f" |S|={k[0]}, #comps={k[1]}: {c}")
|
|
print("\n(|S|, # edges in H[S]) distribution:")
|
|
for k in sorted(grand_edges):
|
|
c = grand_edges[k]
|
|
print(f" |S|={k[0]}, #edges={k[1]}: {c}")
|
|
print("\n(|S|, max # shared pentagons across adjacent S-pairs):")
|
|
for k in sorted(grand_overlap):
|
|
c = grand_overlap[k]
|
|
print(f" |S|={k[0]}, max_overlap={k[1]}: {c}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|