face_monochromatic_pairs: per-cycle refinement + Corollary 5.4
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>
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user