face_mono: extend Conjecture 5.26 to n_G ≤ 22
Adds experiments/test_conj_5_26_n_21_22.py, a clause-4 checker that re-uses find_all_36_witnesses + check_clause_4 from check_conj_final_scaled.py and runs them on n = 21, 22 with incremental JSONL output and a 10-minute PROGRESS heartbeat. Results (139 min wall, single thread): n=21: 192 tri, 392,370 colourings w/ clause-1–3 witness, all pass n=22: 651 tri, 1,786,314 colourings w/ clause-1–3 witness, all pass total at n ≤ 22: 2,321,496 / 2,321,496 (combined with the existing 142,812 at n ≤ 20 from check_conj_final_scaled.py) Paper edits: - Abstract: "|V(G)| ≤ 20 (142,812)" → "|V(G)| ≤ 22 (2,321,496)" for the strengthening; clauses-1–3 count unchanged at 535,182 / n ≤ 21. - Intro paragraph: matching update. - Remark rem:conj-3-8-empirical table: added n=21 and n=22 rows; new total ($n \le 22$) = 959 triangulations, 2,321,496 colourings. - Updated script reference in that remark to point at check_conj_final_scaled.py + test_conj_5_26_n_21_22.py. COMMENTARY.md summary table: Conjecture 5.26 row bumped to 2,321,496 / 2,321,496 (n ≤ 22). Also commits the test_*_results.jsonl artifacts (with per-tri records + n-summaries + grand summary) for reproducibility. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
"""Test Conjecture 5.26 (the strengthening, clauses 1–4) on
|
||||
chord-apex+Kempe colourings of reduced duals for n in [21, 22].
|
||||
|
||||
For each (G, v, i, φ) chord-apex+Kempe colouring, enumerate every
|
||||
witness (F, e_1, e_2) satisfying clauses 1–3, then check whether
|
||||
at least one such witness also satisfies clause 4. The conjecture
|
||||
states: SOME witness for each colouring passes clause 4.
|
||||
|
||||
A "passing colouring" = ∃ witness with clauses 1–4 all satisfied.
|
||||
A "failing colouring" = ALL its witnesses fail clause 4.
|
||||
|
||||
The known result for n ≤ 20 is 142,812 / 142,812 (pass) via the
|
||||
existing check_conj_final_scaled.py. This script extends to
|
||||
n = 21 and n = 22, with incremental JSONL output and a
|
||||
10-minute PROGRESS heartbeat.
|
||||
|
||||
Run with: sage experiments/test_conj_5_26_n_21_22.py [n_min] [n_max]
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
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,
|
||||
find_all_36_witnesses,
|
||||
check_clause_4,
|
||||
)
|
||||
from check_heawood_on_kempe import dual_of
|
||||
|
||||
|
||||
RESULTS_FILE = Path(HERE) / '..' / 'test_conj_5_26_n_21_22_results.jsonl'
|
||||
|
||||
|
||||
def test_one(D):
|
||||
"""For dual D, iterate reductions + chord-apex+Kempe colourings.
|
||||
Returns counters and any failing examples."""
|
||||
D.is_planar(set_embedding=True)
|
||||
n_col_with_witness = 0
|
||||
n_pass = 0
|
||||
n_fail = 0
|
||||
failures = [] # only failing colourings get recorded
|
||||
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)]
|
||||
for col in cand:
|
||||
witnesses = find_all_36_witnesses(H, edges, list(col), named)
|
||||
if not witnesses:
|
||||
continue
|
||||
n_col_with_witness += 1
|
||||
ok = False
|
||||
for w in witnesses:
|
||||
try:
|
||||
if check_clause_4(H, edges, list(col), named, w):
|
||||
ok = True
|
||||
break
|
||||
except Exception:
|
||||
# Defensive: treat exception as "this witness fails"
|
||||
# and keep trying others.
|
||||
pass
|
||||
if ok:
|
||||
n_pass += 1
|
||||
else:
|
||||
n_fail += 1
|
||||
failures.append({
|
||||
'i_red': i_red,
|
||||
'n_witnesses': len(witnesses),
|
||||
})
|
||||
return n_col_with_witness, n_pass, n_fail, failures
|
||||
|
||||
|
||||
def write_result(record):
|
||||
with open(RESULTS_FILE, 'a') as f:
|
||||
f.write(json.dumps(record) + '\n')
|
||||
f.flush()
|
||||
|
||||
|
||||
def main(n_min=21, n_max=22, progress_every_seconds=600):
|
||||
started_at = time.time()
|
||||
print(f"Testing Conjecture 5.26 (clauses 1–4) on n in [{n_min}, {n_max}]")
|
||||
print(f"Writing incremental results to {RESULTS_FILE}")
|
||||
print(f"PROGRESS lines every {progress_every_seconds}s")
|
||||
sys.stdout.flush()
|
||||
if RESULTS_FILE.exists():
|
||||
bak = RESULTS_FILE.with_suffix(RESULTS_FILE.suffix + '.bak')
|
||||
RESULTS_FILE.rename(bak)
|
||||
print(f"Previous results moved to {bak}")
|
||||
RESULTS_FILE.touch()
|
||||
|
||||
grand_col = 0
|
||||
grand_pass = 0
|
||||
grand_fail = 0
|
||||
last_progress = time.time()
|
||||
|
||||
for n in range(n_min, n_max + 1):
|
||||
n_start = time.time()
|
||||
print(f"\n=== n_G = {n} ===")
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
triangulations_iter = graphs.triangulations(n, minimum_degree=5)
|
||||
except Exception as ex:
|
||||
print(f"n={n}: cannot enumerate ({ex})")
|
||||
continue
|
||||
n_col_n = 0
|
||||
n_pass_n = 0
|
||||
n_fail_n = 0
|
||||
tri_idx = 0
|
||||
for G in triangulations_iter:
|
||||
G.is_planar(set_embedding=True)
|
||||
D = dual_of(G)
|
||||
tri_start = time.time()
|
||||
nc, np_, nf, fails = test_one(D)
|
||||
tri_elapsed = time.time() - tri_start
|
||||
n_col_n += nc
|
||||
n_pass_n += np_
|
||||
n_fail_n += nf
|
||||
record = {
|
||||
'n_G': n, 'tri_idx': tri_idx,
|
||||
'n_col_with_witness': nc,
|
||||
'n_pass': np_,
|
||||
'n_fail': nf,
|
||||
'tri_elapsed_s': round(tri_elapsed, 2),
|
||||
'wall_elapsed_s': round(time.time() - started_at, 2),
|
||||
'failures': fails,
|
||||
}
|
||||
write_result(record)
|
||||
now = time.time()
|
||||
if now - last_progress >= progress_every_seconds:
|
||||
wall_min = (now - started_at) / 60
|
||||
print(f"PROGRESS: wall {wall_min:.1f} min; "
|
||||
f"n={n} tri {tri_idx+1} done ({tri_elapsed:.1f}s); "
|
||||
f"running totals @n={n}: col={n_col_n}, "
|
||||
f"pass={n_pass_n}, fail={n_fail_n}")
|
||||
sys.stdout.flush()
|
||||
last_progress = now
|
||||
tri_idx += 1
|
||||
|
||||
n_elapsed = time.time() - n_start
|
||||
summary = {
|
||||
'type': 'n_summary',
|
||||
'n_G': n, 'n_tri': tri_idx,
|
||||
'n_col_with_witness': n_col_n,
|
||||
'n_pass': n_pass_n,
|
||||
'n_fail': n_fail_n,
|
||||
'n_elapsed_s': round(n_elapsed, 2),
|
||||
'wall_elapsed_s': round(time.time() - started_at, 2),
|
||||
}
|
||||
write_result(summary)
|
||||
print(f"\nn={n}: {tri_idx} triangulations, {n_col_n} colourings "
|
||||
f"with witnesses [{n_elapsed:.0f}s]")
|
||||
print(f" pass clause 4: {n_pass_n}")
|
||||
print(f" fail clause 4: {n_fail_n}")
|
||||
sys.stdout.flush()
|
||||
|
||||
grand_col += n_col_n
|
||||
grand_pass += n_pass_n
|
||||
grand_fail += n_fail_n
|
||||
|
||||
total_elapsed = time.time() - started_at
|
||||
final = {
|
||||
'type': 'grand_summary',
|
||||
'n_min': n_min, 'n_max': n_max,
|
||||
'grand_col_with_witness': grand_col,
|
||||
'grand_pass': grand_pass,
|
||||
'grand_fail': grand_fail,
|
||||
'wall_elapsed_s': round(total_elapsed, 2),
|
||||
}
|
||||
write_result(final)
|
||||
print("\n" + "=" * 70)
|
||||
print(f"DONE: n in [{n_min}, {n_max}], {grand_col} colourings with "
|
||||
f"clause-1-3 witness in {total_elapsed/60:.1f} min")
|
||||
print(f" pass: {grand_pass}")
|
||||
print(f" fail: {grand_fail}")
|
||||
if grand_fail == 0 and grand_col > 0:
|
||||
print(f"✓ Conjecture 5.26 EMPIRICALLY EXTENDED to n_G ≤ {n_max}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
n_min = int(sys.argv[1]) if len(sys.argv) > 1 else 21
|
||||
n_max = int(sys.argv[2]) if len(sys.argv) > 2 else 22
|
||||
main(n_min=n_min, n_max=n_max)
|
||||
Reference in New Issue
Block a user