From 1a71658349f5f0c54a7c00afab200257e59cd262 Mon Sep 17 00:00:00 2001 From: didericis Date: Fri, 22 May 2026 10:03:04 -0400 Subject: [PATCH] Small-n bridge-derivability probe: classification + invariant search Findings at n=9 (50 triangulations, orbits fully exhaustible): - 36 bridge-derived, 14 NOT bridge-derived. So bridge-derived is a PROPER subclass of derived (49 derived at n=9). All 14 non-bridge graphs are intertwining trees -- as are all 50, necessarily: intertwining tree <=> dual Hamiltonian, and the smallest non-Hamiltonian 3-connected cubic planar graph has 38 vertices, i.e. dual on 2n-4=38 => n=21. Hence every triangulation with n<=20 is an intertwining tree, and the disjunction "bridge-derived OR intertwining" is trivially true below n=21. The 4 Holton-McKay duals are the first non-intertwining triangulations. - Static parity-subgraph invariants (Betti numbers, component counts, cross-edge count, existence of an all-forest partition) do NOT separate bridge-derived from non-bridge-derived -- both classes realize beta=0 partitions and identical ranges. Bridge-derivability is dynamical, not a simple static invariant; no easy obstruction. - Side lemma: every valid parity partition of an n-vertex triangulation has exactly 2n-4 cross edges (intra-edges = n-2). Holds for all n=9 graphs. Co-Authored-By: Claude Opus 4.7 --- .../colored_edge_flip_class_survey.py | 0 .../colored_pentagon_contractions.py | 0 .../experiments/invariant_dump.py | 71 +++++++++++++++++ .../experiments/small_n_probe.py | 76 +++++++++++++++++++ .../experiments/plane_depth_sequencing.py | 0 .../plane_depth_sequencing_figure.py | 0 .../quad_sequence_coloring_check.py | 0 .../experiments/plane_diamond_coloring.py | 0 8 files changed, 147 insertions(+) rename colored_edge_flip_class_survey.py => papers/colored_edge_flip_classes/experiments/colored_edge_flip_class_survey.py (100%) rename colored_pentagon_contractions.py => papers/colored_pentagon_contractions/experiments/colored_pentagon_contractions.py (100%) create mode 100644 papers/even_level_graph_generators/experiments/invariant_dump.py create mode 100644 papers/even_level_graph_generators/experiments/small_n_probe.py rename plane_depth_sequencing.py => papers/plane_depth_sequencing/experiments/plane_depth_sequencing.py (100%) rename plane_depth_sequencing_figure.py => papers/plane_depth_sequencing/experiments/plane_depth_sequencing_figure.py (100%) rename quad_sequence_coloring_check.py => papers/plane_depth_sequencing/experiments/quad_sequence_coloring_check.py (100%) rename plane_diamond_coloring.py => papers/plane_diamond_coloring/experiments/plane_diamond_coloring.py (100%) diff --git a/colored_edge_flip_class_survey.py b/papers/colored_edge_flip_classes/experiments/colored_edge_flip_class_survey.py similarity index 100% rename from colored_edge_flip_class_survey.py rename to papers/colored_edge_flip_classes/experiments/colored_edge_flip_class_survey.py diff --git a/colored_pentagon_contractions.py b/papers/colored_pentagon_contractions/experiments/colored_pentagon_contractions.py similarity index 100% rename from colored_pentagon_contractions.py rename to papers/colored_pentagon_contractions/experiments/colored_pentagon_contractions.py diff --git a/papers/even_level_graph_generators/experiments/invariant_dump.py b/papers/even_level_graph_generators/experiments/invariant_dump.py new file mode 100644 index 0000000..a5d5d2a --- /dev/null +++ b/papers/even_level_graph_generators/experiments/invariant_dump.py @@ -0,0 +1,71 @@ +"""Dump candidate invariants for bridge-derived vs non-bridge-derived +triangulations at small n, to spot a separating (necessary) condition.""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import networkx as nx +from triangulation_gen import enumerate_all_triangulations +from exhaustive_bridge import valid_parity_partitions +from small_n_probe import is_bridge_derived + + +def per_partition_stats(G): + """For each valid partition return dict of invariants.""" + rows = [] + for labels in valid_parity_partitions(G): + ev = [v for v in G.nodes() if labels[v] == 0] + od = [v for v in G.nodes() if labels[v] == 1] + SE, SO = G.subgraph(ev), G.subgraph(od) + ce = nx.number_connected_components(SE) + co = nx.number_connected_components(SO) + be = SE.number_of_edges() - len(ev) + ce + bo = SO.number_of_edges() - len(od) + co + rows.append(dict(be=be, bo=bo, ee=SE.number_of_edges(), + eo=SO.number_of_edges(), ce=ce, co=co, + ne=len(ev), no=len(od))) + return rows + + +def summarize(G): + rows = per_partition_stats(G) + tb = [r['be'] + r['bo'] for r in rows] + cross = [G.number_of_edges() - r['ee'] - r['eo'] for r in rows] + # does some partition make BOTH parity subgraphs forests (be=bo=0)? + forests = any(r['be'] == 0 and r['bo'] == 0 for r in rows) + return dict(min_tb=min(tb), max_tb=max(tb), nparts=len(rows), + min_cross=min(cross), max_cross=max(cross), + both_forest=forests) + + +def main(n): + tris = enumerate_all_triangulations(n) + print(f'n={n}: {len(tris)} triangulations', flush=True) + bd_stats, nb_stats = [], [] + for G in tris: + s = summarize(G) + if is_bridge_derived(G): + bd_stats.append(s) + else: + nb_stats.append(s) + + def col(stats, key): + return sorted(set(s[key] for s in stats)) + + for key in ['min_tb', 'max_tb', 'min_cross', 'max_cross']: + print(f' {key:10s} bridge={col(bd_stats,key)} ' + f'NONbridge={col(nb_stats,key)}', flush=True) + print(f' both_forest exists? bridge={sorted(set(s["both_forest"] for s in bd_stats))}' + f' NONbridge={sorted(set(s["both_forest"] for s in nb_stats))}', flush=True) + # separator check: any min_tb value unique to one class? + bd_min = set(s['min_tb'] for s in bd_stats) + nb_min = set(s['min_tb'] for s in nb_stats) + print(f' min_tb only-in-bridge: {bd_min - nb_min}; ' + f'only-in-NONbridge: {nb_min - bd_min}', flush=True) + print(f' both_forest: bridge {sum(s["both_forest"] for s in bd_stats)}/{len(bd_stats)}, ' + f'NONbridge {sum(s["both_forest"] for s in nb_stats)}/{len(nb_stats)}', flush=True) + + +if __name__ == '__main__': + main(int(sys.argv[1]) if len(sys.argv) > 1 else 9) diff --git a/papers/even_level_graph_generators/experiments/small_n_probe.py b/papers/even_level_graph_generators/experiments/small_n_probe.py new file mode 100644 index 0000000..51a9e2c --- /dev/null +++ b/papers/even_level_graph_generators/experiments/small_n_probe.py @@ -0,0 +1,76 @@ +"""At small n (orbits fully exhaustible), classify every triangulation as +bridge-derived / derived / intertwining-tree, and tabulate candidate +invariants on the parity subgraphs to look for one that separates +bridge-derived from the rest. +""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import networkx as nx +from triangulation_gen import enumerate_all_triangulations +from test_conjecture import canonical_sig, is_even_level_graph +from exhaustive_bridge import valid_parity_partitions +from fast_bridge import EdgeCode +from fast_decide import expand_and_check +from test_disjunction import is_intertwining_tree + + +def is_bridge_derived(G, cap=2_000_000): + """Exhaustive: some valid parity partition L has an ELG (parity L) in + the backward bridge-orbit of G. Feasible only at small n.""" + n = G.number_of_nodes() + code = EdgeCode(G.nodes()) + code.state0 = code.state_of(G) + for labels in valid_parity_partitions(G): + seen = {code.state0} + frontier = [code.state0] + while frontier and len(seen) < cap: + new = [] + for st in frontier: + wit, neigh = expand_and_check(st, code, labels, n) + if wit: + return True + for ns in neigh: + if ns not in seen: + seen.add(ns) + new.append(ns) + frontier = new + return False + + +def parity_invariants(G): + """Over all valid parity partitions, collect (betti_even, betti_odd, + e_even, e_odd, comps_even, comps_odd) tuples; return the set.""" + out = set() + for labels in valid_parity_partitions(G): + ev = [v for v in G.nodes() if labels[v] == 0] + od = [v for v in G.nodes() if labels[v] == 1] + SE, SO = G.subgraph(ev), G.subgraph(od) + be = SE.number_of_edges() - len(ev) + nx.number_connected_components(SE) + bo = SO.number_of_edges() - len(od) + nx.number_connected_components(SO) + out.add((be, bo, SE.number_of_edges(), SO.number_of_edges(), + nx.number_connected_components(SE), + nx.number_connected_components(SO))) + return out + + +def main(n): + tris = enumerate_all_triangulations(n) + print(f'n={n}: {len(tris)} triangulations', flush=True) + classes = {'bridge': [], 'not_bridge': []} + for idx, G in enumerate(tris): + bd = is_bridge_derived(G) + it = is_intertwining_tree(G) + deg = tuple(sorted((d for _, d in G.degree()), reverse=True)) + classes['bridge' if bd else 'not_bridge'].append((idx, it, deg)) + print(f' bridge-derived: {len(classes["bridge"])}', flush=True) + print(f' NOT bridge-derived: {len(classes["not_bridge"])}', flush=True) + print(' --- NOT bridge-derived (idx, intertwining?, degseq) ---', flush=True) + for idx, it, deg in classes['not_bridge']: + print(f' idx={idx} intertwining={it} deg={deg}', flush=True) + + +if __name__ == '__main__': + main(int(sys.argv[1]) if len(sys.argv) > 1 else 9) diff --git a/plane_depth_sequencing.py b/papers/plane_depth_sequencing/experiments/plane_depth_sequencing.py similarity index 100% rename from plane_depth_sequencing.py rename to papers/plane_depth_sequencing/experiments/plane_depth_sequencing.py diff --git a/plane_depth_sequencing_figure.py b/papers/plane_depth_sequencing/experiments/plane_depth_sequencing_figure.py similarity index 100% rename from plane_depth_sequencing_figure.py rename to papers/plane_depth_sequencing/experiments/plane_depth_sequencing_figure.py diff --git a/quad_sequence_coloring_check.py b/papers/plane_depth_sequencing/experiments/quad_sequence_coloring_check.py similarity index 100% rename from quad_sequence_coloring_check.py rename to papers/plane_depth_sequencing/experiments/quad_sequence_coloring_check.py diff --git a/plane_diamond_coloring.py b/papers/plane_diamond_coloring/experiments/plane_diamond_coloring.py similarity index 100% rename from plane_diamond_coloring.py rename to papers/plane_diamond_coloring/experiments/plane_diamond_coloring.py