33b51b675b
Empirical refinement of Lemma 5.3: h_phi is non-constant on V(K_b)
alone (not just on the union) and likewise on V(K_c) alone, in every
one of 142,812 chord-apex+Kempe colourings tested (n in [12, 20]).
This is strictly stronger than what we previously reported.
The proof of Lemma 5.3 already constructs the (F, e_1, e_2) witness
from any consecutive same-Heawood failure on either Kempe cycle
through merged -- never needing the other cycle. Pull that out into
a separate Corollary 5.4 ("Per-cycle form"), which makes the
empirical-to-conjecture path more direct.
Update Remark 5.5 to:
- Cite Corollary 5.4 instead of the contrapositive of Lemma 5.3.
- Replace "non-constant on V(K_b) U V(K_c)" with the per-cycle form.
- Extend the empirical table with separate columns for K_b and K_c
non-constancy.
Also commit experiments/check_constancy_obstruction.py, the script
that produced these refined empirical findings. It additionally
records that no single named vertex (v_n, A_i, ..., A_{i+4}) is
structurally majority or minority -- the minority rates cluster in
31-39%, ruling out a single-vertex-mismatch identity.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
225 lines
9.0 KiB
Python
225 lines
9.0 KiB
Python
"""For each chord-apex+Kempe colouring, dissect WHERE the constancy
|
|
hypothesis fails:
|
|
|
|
(1) Is h_phi constant on V(K_b) alone? On V(K_c) alone? On the
|
|
intersection V(K_b) cap V(K_c)?
|
|
(2) Per colouring, distribution of (#+1, #-1) shared vertices.
|
|
(3) For each colouring, identify the "minority Heawood" shared
|
|
vertices (= those whose h differs from the more frequent value
|
|
on V(K_b) cup V(K_c)). How many are there? Are they
|
|
concentrated at specific structural positions (v_n, A_{i+1},
|
|
A_{i+3}, A_{i+4})?
|
|
(4) Is h(v_n) always equal to / always different from the majority?
|
|
|
|
Under the constancy hypothesis, all of these distributions would be
|
|
trivial (everything same Heawood, 0 minority vertices). Their
|
|
non-trivial empirical structure exposes where Path 4's "trap"
|
|
mechanism would have to apply.
|
|
|
|
Run with: sage experiments/check_constancy_obstruction.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,
|
|
trace_kempe_cycle,
|
|
edge_idx,
|
|
kempe_cycle_set,
|
|
)
|
|
from check_heawood_on_kempe import (
|
|
dual_of, heawood_numbers, vertices_of_kempe,
|
|
)
|
|
|
|
|
|
def named_vertices(named, v_n=9999):
|
|
"""Recover A_i, A_{i+1}, ..., A_{i+4}."""
|
|
def other(fs, v):
|
|
return next(iter(fs - {v}))
|
|
A_i = other(named['side_0'], v_n)
|
|
A_i1 = other(named['spike'], v_n)
|
|
A_i2 = other(named['side_1'], v_n)
|
|
A_i3, A_i4 = sorted(named['merged'])
|
|
return A_i, A_i1, A_i2, A_i3, A_i4
|
|
|
|
|
|
def test_one(D):
|
|
D.is_planar(set_embedding=True)
|
|
n_col = 0
|
|
rec = {
|
|
'const_kb': 0, 'const_kc': 0, 'const_cap': 0,
|
|
# (#+1, #-1) on V(K_b) cup V(K_c) histogram
|
|
'plus_minus_dist': {},
|
|
# # minority Heawood vertices on V(K_b) cup V(K_c)
|
|
'minority_count': {},
|
|
# Is v_n minority? majority?
|
|
'v_n_pos': {'minority': 0, 'majority': 0, 'tie': 0},
|
|
# Position-tallies of minority for each "named" vertex
|
|
'minority_at': {'v_n': 0, 'A_i': 0, 'A_i1': 0, 'A_i2': 0,
|
|
'A_i3': 0, 'A_i4': 0},
|
|
# Is h(v_n) == h(A_{i+3}) == h(A_{i+4}) always?
|
|
'v_n_eq_merged_endpts': 0,
|
|
# Is h constant on the 6 named vertices {v_n, A_0..A_4}?
|
|
'const_on_named6': 0,
|
|
}
|
|
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
|
|
A_i, A_i1, A_i2, A_i3, A_i4 = named_vertices(named, v_n)
|
|
for col in cand:
|
|
n_col += 1
|
|
try:
|
|
h = heawood_numbers(H, edges, col)
|
|
except RuntimeError:
|
|
continue
|
|
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
|
|
V_cap = V_b & V_c
|
|
h_b_vals = {h[v] for v in V_b}
|
|
h_c_vals = {h[v] for v in V_c}
|
|
h_cap_vals = {h[v] for v in V_cap}
|
|
if len(h_b_vals) == 1: rec['const_kb'] += 1
|
|
if len(h_c_vals) == 1: rec['const_kc'] += 1
|
|
if len(h_cap_vals) == 1: rec['const_cap'] += 1
|
|
plus = sum(1 for v in V_union if h[v] == 1)
|
|
minus = sum(1 for v in V_union if h[v] == -1)
|
|
rec['plus_minus_dist'][(plus, minus)] = \
|
|
rec['plus_minus_dist'].get((plus, minus), 0) + 1
|
|
# Majority sign on union
|
|
if plus > minus:
|
|
maj = 1
|
|
elif minus > plus:
|
|
maj = -1
|
|
else:
|
|
maj = 0
|
|
minority = sum(1 for v in V_union if h[v] != maj and maj != 0)
|
|
if maj == 0:
|
|
minority = min(plus, minus)
|
|
rec['minority_count'][minority] = \
|
|
rec['minority_count'].get(minority, 0) + 1
|
|
# v_n's position
|
|
hv = h.get(v_n)
|
|
if hv is not None and maj != 0:
|
|
if hv == maj:
|
|
rec['v_n_pos']['majority'] += 1
|
|
else:
|
|
rec['v_n_pos']['minority'] += 1
|
|
else:
|
|
rec['v_n_pos']['tie'] += 1
|
|
# Named vertices: minority count
|
|
for name, vv in [('v_n', v_n), ('A_i', A_i),
|
|
('A_i1', A_i1), ('A_i2', A_i2),
|
|
('A_i3', A_i3), ('A_i4', A_i4)]:
|
|
if vv in V_union and maj != 0 and h[vv] != maj:
|
|
rec['minority_at'][name] += 1
|
|
# h(v_n) == h(A_{i+3}) == h(A_{i+4}) ?
|
|
if h.get(v_n) == h.get(A_i3) == h.get(A_i4):
|
|
rec['v_n_eq_merged_endpts'] += 1
|
|
# h constant on the 6 named vertices?
|
|
named6 = [v_n, A_i, A_i1, A_i2, A_i3, A_i4]
|
|
named6_vals = {h[vv] for vv in named6 if vv in h}
|
|
if len(named6_vals) == 1:
|
|
rec['const_on_named6'] += 1
|
|
return n_col, rec
|
|
|
|
|
|
def merge_into(g, r):
|
|
for k in ('const_kb', 'const_kc', 'const_cap',
|
|
'v_n_eq_merged_endpts', 'const_on_named6'):
|
|
g[k] += r[k]
|
|
for k in ('plus_minus_dist', 'minority_count'):
|
|
for kk, vv in r[k].items():
|
|
g[k][kk] = g[k].get(kk, 0) + vv
|
|
for sub in ('v_n_pos', 'minority_at'):
|
|
for kk, vv in r[sub].items():
|
|
g[sub][kk] = g[sub].get(kk, 0) + vv
|
|
|
|
|
|
def main(max_n=18, time_budget_per_n=1800):
|
|
print(f"Constancy obstruction analysis, n in [12, {max_n}]\n")
|
|
grand = {
|
|
'const_kb': 0, 'const_kc': 0, 'const_cap': 0,
|
|
'plus_minus_dist': {}, 'minority_count': {},
|
|
'v_n_pos': {'minority': 0, 'majority': 0, 'tie': 0},
|
|
'minority_at': {'v_n': 0, 'A_i': 0, 'A_i1': 0, 'A_i2': 0,
|
|
'A_i3': 0, 'A_i4': 0},
|
|
'v_n_eq_merged_endpts': 0,
|
|
'const_on_named6': 0,
|
|
}
|
|
grand_col = 0
|
|
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
|
|
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, ri = test_one(D)
|
|
n_col_n += ni
|
|
merge_into(grand, ri)
|
|
elapsed = time.time() - start
|
|
print(f"n={n}: {n_col_n} col., [{elapsed:.0f}s]")
|
|
sys.stdout.flush()
|
|
grand_col += n_col_n
|
|
|
|
print()
|
|
print("=" * 78)
|
|
print(f"Grand totals (n in [12, {max_n}], {grand_col} colourings)")
|
|
print(f"\n h constant on V(K_b): {grand['const_kb']}/{grand_col}")
|
|
print(f" h constant on V(K_c): {grand['const_kc']}/{grand_col}")
|
|
print(f" h constant on V(K_b) cap V(K_c): {grand['const_cap']}/{grand_col}")
|
|
print(f" h constant on {{v_n, A_0..A_4}}: "
|
|
f"{grand['const_on_named6']}/{grand_col}")
|
|
print(f" h(v_n) == h(A_{{i+3}}) == h(A_{{i+4}}): "
|
|
f"{grand['v_n_eq_merged_endpts']}/{grand_col}")
|
|
print(f"\n Minority count (= #vertices on V(K_b) cup V(K_c) with "
|
|
f"non-majority h) distribution:")
|
|
for k, v in sorted(grand['minority_count'].items()):
|
|
pct = 100 * v / max(1, grand_col)
|
|
print(f" {k:>3}: {v} ({pct:.2f}%)")
|
|
print(f"\n v_n status on V(K_b) cup V(K_c):")
|
|
print(f" in majority: {grand['v_n_pos']['majority']} "
|
|
f"({100*grand['v_n_pos']['majority']/max(1,grand_col):.2f}%)")
|
|
print(f" in minority: {grand['v_n_pos']['minority']} "
|
|
f"({100*grand['v_n_pos']['minority']/max(1,grand_col):.2f}%)")
|
|
print(f" tie: {grand['v_n_pos']['tie']} "
|
|
f"({100*grand['v_n_pos']['tie']/max(1,grand_col):.2f}%)")
|
|
print(f"\n How often each named vertex is in the minority:")
|
|
for name, c in grand['minority_at'].items():
|
|
print(f" {name:>5}: {c}/{grand_col} "
|
|
f"({100*c/max(1,grand_col):.2f}%)")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|