diff --git a/papers/even_level_graph_generators/experiments/bridge_derived_test.py b/papers/even_level_graph_generators/experiments/bridge_derived_test.py new file mode 100644 index 0000000..2308c94 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/bridge_derived_test.py @@ -0,0 +1,145 @@ +"""Test whether the 4 open Holton-McKay duals are bridge-derived level +graphs: reachable from an Even Level Graph by bridge switches (E/O +switches whose new same-parity edge is a bridge in its parity subgraph). + +Because bridge switches keep parity subgraphs bipartite, the reachable +set is much smaller than the full E/O orbit. We search BACKWARD from the +dual: a bridge-switch predecessor of G is G' such that G' -> G is a +bridge switch. We look for an ELG (matching the orbit labelling) in the +backward bridge-orbit. +""" +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 +import time +from load_holton_mckay import parse_planar_code +from tutte_dual_treecolor import dual_triangulation +from test_conjecture import bfs_levels, is_even_level_graph + + +def sig(G): + return frozenset(frozenset(e) for e in G.edges()) + + +def parity_subgraph(G, labels, parity): + nodes = [v for v in G.nodes() if labels[v] % 2 == parity] + return G.subgraph(nodes) + + +def edge_is_bridge_in_parity(H, labels, a, b): + """Given triangulation H that CONTAINS edge ab, with a,b same parity: + is ab a bridge of its parity subgraph (i.e., not on any cycle)? + For cross-parity ab, returns True (enters no parity subgraph).""" + if labels[a] % 2 != labels[b] % 2: + return True + sub = parity_subgraph(H, labels, labels[a] % 2).copy() + sub.remove_edge(a, b) + return not nx.has_path(sub, a, b) + + +def forward_bridge_neighbors(G, labels): + """Forward bridge switches from G.""" + ok, emb = nx.check_planarity(G) + if not ok: + return + for u, v in list(G.edges()): + if labels[u] % 2 != labels[v] % 2: + continue # only flip same-parity edges + f1 = emb.traverse_face(u, v) + f2 = emb.traverse_face(v, u) + if len(f1) != 3 or len(f2) != 3: + continue + w = next(a for a in f1 if a != u and a != v) + x = next(b for b in f2 if b != u and b != v) + if w == x or G.has_edge(w, x): + continue + Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x) + # bridge condition checked on the post-switch state Gp: + if not edge_is_bridge_in_parity(Gp, labels, w, x): + continue + yield Gp + + +def backward_bridge_neighbors(G, labels): + """States G' with G' -> G a bridge switch. G' = G - uv + wx where uv + is an edge of G, wx its diagonal, wx same-parity (it was the flipped + edge in G'), and in G' the edge uv (the *new* edge of the switch + G'->G) must be a bridge in G'-minus-uv's parity subgraph... + + We must reconstruct: G' has wx (same-parity), flipping wx gives uv. + For G'->G to be a *bridge* switch, the NEW edge uv (added to G when + going G'->G) must be a bridge in G's parity subgraph if uv is + same-parity. So check the bridge condition on uv in (G without uv).""" + ok, emb = nx.check_planarity(G) + if not ok: + return + for u, v in list(G.edges()): + f1 = emb.traverse_face(u, v) + f2 = emb.traverse_face(v, u) + if len(f1) != 3 or len(f2) != 3: + continue + w = next(a for a in f1 if a != u and a != v) + x = next(b for b in f2 if b != u and b != v) + if w == x or G.has_edge(w, x): + continue + if labels[w] % 2 != labels[x] % 2: + continue # wx must be same-parity to be flippable in G' + # Switch G' -> G adds edge uv to the post-state G. Bridge switch + # requires uv to be a bridge of its parity subgraph in G: + if not edge_is_bridge_in_parity(G, labels, u, v): + continue + Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x) + yield Gp + + +def search_bridge_derived(G, labels, max_states=2_000_000, time_limit=600): + """Backward bridge-orbit BFS; return an ELG (matching labels) if found.""" + t0 = time.time() + seen = {sig(G)} + frontier = [G] + rounds = 0 + while frontier and len(seen) < max_states: + if time.time() - t0 > time_limit: + return None, len(seen), rounds, 'timeout' + new = [] + for H in frontier: + for s in H.nodes(): + ok, lvls = is_even_level_graph(H, frozenset({s})) + if not ok: + continue + same = all(lvls[u] % 2 == labels[u] % 2 for u in H.nodes()) + opp = all(lvls[u] % 2 != labels[u] % 2 for u in H.nodes()) + if same or opp: + return H, len(seen), rounds, 'found' + for Hp in backward_bridge_neighbors(H, labels): + sg = sig(Hp) + if sg not in seen: + seen.add(sg); new.append(Hp) + frontier = new + rounds += 1 + return None, len(seen), rounds, ('exhausted' if not frontier else 'capped') + + +def main(): + graphs = parse_planar_code('experiments/nonham38m4.pc') + for i in [0, 3, 4, 5]: + G, _ = dual_triangulation(graphs[i][0]) + print(f'=== dual {i} ===') + found = False + for src in list(G.nodes()): + labels = bfs_levels(G, frozenset({src})) + result, n_states, rnds, status = search_bridge_derived( + G, labels, max_states=500000, time_limit=120) + tag = 'FOUND ELG' if result is not None else 'none' + print(f' src={src}: {tag} ({status}, {n_states} states, {rnds} rounds)') + if result is not None: + found = True + break + print(f' dual {i}: bridge-derived = {found}') + + +if __name__ == '__main__': + main() diff --git a/papers/even_level_graph_generators/experiments/directed_orbit_check.py b/papers/even_level_graph_generators/experiments/directed_orbit_check.py new file mode 100644 index 0000000..f87e560 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/directed_orbit_check.py @@ -0,0 +1,125 @@ +"""Verify the directedness of the E/O-switch relation, and its effect on +'derived'. Compute, for T*_9 (n=9, known NOT derived) with a fixed +labelling: + - forward orbit: states reachable FROM T*_9 (T*_9 ->* H) + - backward orbit: states that reach T*_9 (H ->* T*_9) +and check each for an Even Level Graph. + +'Derived' (directed) = some ELG reaches G = ELG in backward orbit. +""" +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 bfs_levels, is_even_level_graph + + +def sig(G): + return frozenset(frozenset(e) for e in G.edges()) + + +def forward_neighbors(G, labels): + ok, emb = nx.check_planarity(G) + if not ok: + return + for u, v in list(G.edges()): + if labels[u] % 2 != labels[v] % 2: + continue + f1 = emb.traverse_face(u, v) + f2 = emb.traverse_face(v, u) + if len(f1) != 3 or len(f2) != 3: + continue + w = next(x for x in f1 if x != u and x != v) + x = next(y for y in f2 if y != u and y != v) + if w == x or G.has_edge(w, x): + continue + Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x) + yield Gp + + +def backward_neighbors(G, labels): + """States G' with G' ->forward G. G' = G - f + diag_G(f) for each edge + f of G whose diagonal is same-parity (so flipping it in G' is valid).""" + ok, emb = nx.check_planarity(G) + if not ok: + return + for u, v in list(G.edges()): + f1 = emb.traverse_face(u, v) + f2 = emb.traverse_face(v, u) + if len(f1) != 3 or len(f2) != 3: + continue + w = next(x for x in f1 if x != u and x != v) + x = next(y for y in f2 if y != u and y != v) + if w == x or G.has_edge(w, x): + continue + # diagonal of edge uv is wx; predecessor un-flips: remove uv? No. + # Predecessor G' has edge wx (same-parity), flipping wx gives uv... + # We want G = G' - (wx) + (uv). So G' = G - uv + wx, and the flipped + # edge in G' is wx which must be same-parity. + if labels[w] % 2 != labels[x] % 2: + continue + Gp = G.copy(); Gp.remove_edge(u, v); Gp.add_edge(w, x) + yield Gp + + +def orbit(G_start, labels, neighbor_fn, max_states=300000): + seen = {sig(G_start): G_start} + frontier = [G_start] + while frontier and len(seen) < max_states: + new = [] + for H in frontier: + for Hp in neighbor_fn(H, labels): + s = sig(Hp) + if s not in seen: + seen[s] = Hp + new.append(Hp) + frontier = new + return list(seen.values()) + + +def has_elg(orbit_states, labels): + """Find an Even Level Graph in the orbit whose BFS-parity (from some + source) MATCHES the orbit labelling (up to global parity swap). + Only such a graph is a valid 'derived from this ELG' witness.""" + for H in orbit_states: + for s in H.nodes(): + ok, lvls = is_even_level_graph(H, frozenset({s})) + if not ok: + continue + same = all(lvls[u] % 2 == labels[u] % 2 for u in H.nodes()) + opp = all(lvls[u] % 2 != labels[u] % 2 for u in H.nodes()) + if same or opp: + return True, H, s + return False, None, None + + +def main(): + tris = enumerate_all_triangulations(9) + Tstar = next(G for G in tris + if sorted([G.degree(v) for v in G.nodes()], reverse=True) + == [5, 5, 5, 5, 5, 5, 4, 4, 4]) + + src = 0 + labels = bfs_levels(Tstar, frozenset({src})) + print(f'T*_9, labelling = BFS parity from source {src}') + + fwd = orbit(Tstar, labels, forward_neighbors) + f_elg, _, _ = has_elg(fwd, labels) + print(f' FORWARD orbit (T*_9 ->* H): size {len(fwd)}, contains ELG: {f_elg}') + + bwd = orbit(Tstar, labels, backward_neighbors) + b_elg, He, se = has_elg(bwd, labels) + print(f' BACKWARD orbit (H ->* T*_9): size {len(bwd)}, contains ELG: {b_elg}') + if b_elg: + print(f' -> ELG predecessor exists (src {se}); T*_9 WOULD be derived') + else: + print(f' -> no ELG reaches T*_9; T*_9 is NOT derived (directed)') + + print(f'\nConclusion: directedness matters = {f_elg != b_elg}') + + +if __name__ == '__main__': + main() diff --git a/papers/even_level_graph_generators/experiments/exhaustive_bridge.py b/papers/even_level_graph_generators/experiments/exhaustive_bridge.py new file mode 100644 index 0000000..d235907 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/exhaustive_bridge.py @@ -0,0 +1,143 @@ +"""Exhaustive bridge-derived test for the 4 open Holton-McKay duals. + +Step A (gauge): for each dual, count the valid parity partitions +(bipartitions L with both parity subgraphs bipartite) and measure a few +bridge-orbit sizes run to FULL exhaustion (no timeout). + +Step B (decide): for each dual, for every valid parity partition L, +exhaust the backward bridge-orbit and look for an ELG with BFS-parity L. +YES if found for any L; NO (conclusive) if none found for all L. +""" +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 +import time +from load_holton_mckay import parse_planar_code +from tutte_dual_treecolor import dual_triangulation +from test_conjecture import is_even_level_graph +from bridge_derived_test import ( + sig, parity_subgraph, edge_is_bridge_in_parity, + backward_bridge_neighbors, +) + + +def valid_parity_partitions(G): + """Yield labels-dicts for every bipartition (fix node 0 in even class) + such that both induced parity subgraphs are bipartite. Labels are 0 + (even) / 1 (odd).""" + nodes = sorted(G.nodes()) + n = len(nodes) + assert nodes[0] == 0 + for mask in range(2 ** (n - 1)): + labels = {0: 0} + for i in range(n - 1): + labels[nodes[i + 1]] = (mask >> i) & 1 + even = [v for v in nodes if labels[v] == 0] + odd = [v for v in nodes if labels[v] == 1] + if not odd: + continue + if nx.is_bipartite(G.subgraph(even)) and nx.is_bipartite(G.subgraph(odd)): + yield labels + + +def is_elg_with_parity(H, labels): + """Is H an Even Level Graph for some source whose BFS-parity matches + `labels` (up to global swap)? Only even-class vertices can be the + source (level 0 is even).""" + even = [v for v in H.nodes() if labels[v] == 0] + odd_set = set(v for v in H.nodes() if labels[v] == 1) + for s in even: + # quick reject: all neighbours of s must be odd-class (level 1) + if any(nb not in odd_set for nb in H.neighbors(s)): + # could still match under global swap; handle swap separately + pass + ok, lvls = is_even_level_graph(H, frozenset({s})) + if not ok: + continue + if all(lvls[u] % 2 == labels[u] for u in H.nodes()): + return True + # global swap: source in odd class, BFS-parity is complement of labels + odd = [v for v in H.nodes() if labels[v] == 1] + for s in odd: + ok, lvls = is_even_level_graph(H, frozenset({s})) + if not ok: + continue + if all(lvls[u] % 2 != labels[u] for u in H.nodes()): + return True + return False + + +def exhaust_bridge_orbit_for_elg(G, labels, cap=3_000_000): + """Exhaust backward bridge-orbit (no ELG check during BFS), then scan + for an ELG witness. Returns ('found',H) / ('exhausted',size) / + ('capped',size).""" + seen = {sig(G): G} + frontier = [G] + while frontier and len(seen) < cap: + new = [] + for H in frontier: + for Hp in backward_bridge_neighbors(H, labels): + sg = sig(Hp) + if sg not in seen: + seen[sg] = Hp + new.append(Hp) + frontier = new + status = 'capped' if frontier else 'exhausted' + if status == 'exhausted': + for H in seen.values(): + if is_elg_with_parity(H, labels): + return 'found', H + return status, len(seen) + + +def decide_dual(i, cap=3_000_000, log=print): + graphs = parse_planar_code('experiments/nonham38m4.pc') + G, _ = dual_triangulation(graphs[i][0]) + parts = list(valid_parity_partitions(G)) + log(f'dual {i}: {len(parts)} valid parity partitions') + any_capped = False + max_orbit = 0 + t0 = time.time() + for j, labels in enumerate(parts): + st, info = exhaust_bridge_orbit_for_elg(G, labels, cap=cap) + if st == 'found': + log(f' partition {j}: FOUND ELG -> dual {i} IS bridge-derived ' + f'({time.time()-t0:.0f}s)') + return 'bridge-derived' + if st == 'capped': + any_capped = True + log(f' partition {j}: orbit exceeded cap ({info}); inconclusive') + else: + max_orbit = max(max_orbit, info) + if (j + 1) % 25 == 0: + log(f' ...{j+1}/{len(parts)} partitions, max orbit {max_orbit}, ' + f'{time.time()-t0:.0f}s') + if any_capped: + log(f' dual {i}: no witness, but some orbits hit cap -> INCONCLUSIVE ' + f'({time.time()-t0:.0f}s)') + return 'inconclusive' + log(f' dual {i}: NOT bridge-derived (all {len(parts)} orbits exhausted, ' + f'max orbit {max_orbit}, {time.time()-t0:.0f}s)') + return 'not-bridge-derived' + + +def gauge(dual_indices): + graphs = parse_planar_code('experiments/nonham38m4.pc') + for i in dual_indices: + G, _ = dual_triangulation(graphs[i][0]) + t0 = time.time() + parts = list(valid_parity_partitions(G)) + print(f'dual {i}: {len(parts)} valid parity partitions ' + f'(enumerated in {time.time()-t0:.1f}s)') + + +if __name__ == '__main__': + import sys as _s + if len(_s.argv) > 1 and _s.argv[1] == 'gauge': + gauge([0, 3, 4, 5]) + elif len(_s.argv) > 1: + for idx in _s.argv[1:]: + decide_dual(int(idx)) diff --git a/papers/even_level_graph_generators/experiments/orbit_invariant_search.py b/papers/even_level_graph_generators/experiments/orbit_invariant_search.py new file mode 100644 index 0000000..6f7bb60 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/orbit_invariant_search.py @@ -0,0 +1,128 @@ +"""Hunt for a structural invariant of E/O-switch orbits that separates +'derived from an ELG' from 'not derived'. + +Strategy: at n=9, T*_9 is NOT a derived level graph (it is intertwining- +only). Compare its E/O orbit (under a fixed labelling) against the orbit +of a graph that IS derived. Look for an invariant constant on orbits +that differs between the two. +""" +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, bfs_levels, is_even_level_graph + + +def eo_switch_neighbors(G, labels): + """Yield all triangulations reachable from G by one E/O switch + (flip a same-parity edge). Directed: this is the FORWARD relation.""" + ok, emb = nx.check_planarity(G) + if not ok: + return + for u, v in list(G.edges()): + if labels[u] % 2 != labels[v] % 2: + continue + f1 = emb.traverse_face(u, v) + f2 = emb.traverse_face(v, u) + if len(f1) != 3 or len(f2) != 3: + continue + w = next(x for x in f1 if x != u and x != v) + x = next(y for y in f2 if y != u and y != v) + if w == x or G.has_edge(w, x): + continue + Gp = G.copy() + Gp.remove_edge(u, v) + Gp.add_edge(w, x) + yield Gp + + +def undirected_orbit(G_start, labels, max_states=200000): + """Connected component of G_start in the UNDIRECTED switch graph + (treat each switch as bidirectional by also exploring forward from + every reached state -- since a forward switch's reverse is itself a + forward switch from the target when the new edge is same-parity, and + we explore all forward edges from every node, the BFS closure equals + the weakly-connected component).""" + sig0 = frozenset(frozenset(e) for e in G_start.edges()) + seen = {sig0: G_start} + frontier = [G_start] + while frontier and len(seen) < max_states: + new = [] + for H in frontier: + for Hp in eo_switch_neighbors(H, labels): + sig = frozenset(frozenset(e) for e in Hp.edges()) + if sig not in seen: + seen[sig] = Hp + new.append(Hp) + frontier = new + return list(seen.values()) + + +def parity_subgraph_edge_counts(G, labels): + even = [v for v in G.nodes() if labels[v] % 2 == 0] + odd = [v for v in G.nodes() if labels[v] % 2 == 1] + e_even = G.subgraph(even).number_of_edges() + e_odd = G.subgraph(odd).number_of_edges() + cross = G.number_of_edges() - e_even - e_odd + return e_even, e_odd, cross + + +def orbit_report(G, labels, name): + orbit = undirected_orbit(G, labels) + has_elg = any( + any(is_even_level_graph(H, frozenset({s}))[0] for s in H.nodes()) + for H in orbit + ) + # Invariant candidates over the orbit + ec = set() + for H in orbit: + ec.add(parity_subgraph_edge_counts(H, labels)) + print(f'{name}: orbit size {len(orbit)}, contains ELG: {has_elg}') + print(f' (e_even, e_odd, cross) values in orbit: {sorted(ec)}') + return orbit, has_elg + + +def main(): + tris = enumerate_all_triangulations(9) + # T*_9 is the iso class that is NOT derived (intertwining-only). + # We earlier identified it as iso index 49 via canonical_sig matching + # the degree sequence (5,5,5,5,5,5,4,4,4). + Tstar = None + for G in tris: + if sorted([G.degree(v) for v in G.nodes()], reverse=True) == \ + [5, 5, 5, 5, 5, 4, 4, 4, 4]: + pass + for G in tris: + ds = sorted([G.degree(v) for v in G.nodes()], reverse=True) + if ds == [5, 5, 5, 5, 5, 5, 4, 4, 4]: + Tstar = G + break + print(f'T*_9 degree seq: ' + f'{sorted([Tstar.degree(v) for v in Tstar.nodes()], reverse=True)}') + + # Pick a labelling of T*_9: a valid parity partition (bipartite parity + # subgraphs). Use one we know: V_E={0,1,3,6}? But labels here come from + # the enumerate ordering. Instead, search labellings = BFS parities. + print('\n--- T*_9 (NOT derived) orbits under BFS-source labellings ---') + for s in list(Tstar.nodes()): + labels = bfs_levels(Tstar, frozenset({s})) + orbit, has_elg = orbit_report(Tstar, labels, f'T*_9 src={s}') + if has_elg: + print(' ^ unexpectedly found ELG') + + # A derived graph: pick one that is an ELG itself. + print('\n--- A derived graph (an ELG) for comparison ---') + for G in tris: + elg_src = next((s for s in G.nodes() + if is_even_level_graph(G, frozenset({s}))[0]), None) + if elg_src is not None: + labels = bfs_levels(G, frozenset({elg_src})) + orbit_report(G, labels, f'derived(ELG) src={elg_src}') + break + + +if __name__ == '__main__': + main() diff --git a/papers/even_level_graph_generators/paper.aux b/papers/even_level_graph_generators/paper.aux index 0a55145..8890fd0 100644 --- a/papers/even_level_graph_generators/paper.aux +++ b/papers/even_level_graph_generators/paper.aux @@ -34,18 +34,20 @@ \newlabel{sec:even-level-graphs}{{4}{3}{Even Level Graphs}{section.4}{}} \newlabel{def:even-level-graph}{{4.1}{3}{Even Level Graph}{theorem.4.1}{}} \newlabel{thm:even-level-4colorable}{{4.2}{3}{}{theorem.4.2}{}} -\citation{holton-mckay} \newlabel{def:derived-level-graph}{{4.3}{4}{Derived level graph}{theorem.4.3}{}} -\newlabel{def:intertwining-tree}{{4.4}{4}{Intertwining tree}{theorem.4.4}{}} -\newlabel{thm:intertwining-iff-hamiltonian-dual}{{4.5}{4}{}{theorem.4.5}{}} -\newlabel{conj:every-triangulation-derived}{{4.6}{4}{}{theorem.4.6}{}} -\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical status}}{4}{section*.1}\protected@file@percent } +\newlabel{def:bridge-switch}{{4.4}{4}{Bridge switch}{theorem.4.4}{}} +\newlabel{def:bridge-derived-level-graph}{{4.5}{4}{Bridge-derived level graph}{theorem.4.5}{}} +\newlabel{def:intertwining-tree}{{4.6}{4}{Intertwining tree}{theorem.4.6}{}} +\newlabel{thm:intertwining-iff-hamiltonian-dual}{{4.7}{4}{}{theorem.4.7}{}} +\citation{holton-mckay} \bibcite{holton-mckay}{1} +\newlabel{conj:every-triangulation-derived}{{4.8}{5}{}{theorem.4.8}{}} +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical status}}{5}{section*.1}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{The boundary case $n = 21$}}{5}{section*.2}\protected@file@percent } \newlabel{tocindent-1}{0pt} \newlabel{tocindent0}{14.69437pt} \newlabel{tocindent1}{17.77782pt} \newlabel{tocindent2}{0pt} \newlabel{tocindent3}{0pt} -\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{The boundary case $n = 21$}}{5}{section*.2}\protected@file@percent } -\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{5}{section*.3}\protected@file@percent } -\gdef \@abspage@last{5} +\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{6}{section*.3}\protected@file@percent } +\gdef \@abspage@last{6} diff --git a/papers/even_level_graph_generators/paper.log b/papers/even_level_graph_generators/paper.log index 7c4b5c9..1bb6ff3 100644 --- a/papers/even_level_graph_generators/paper.log +++ b/papers/even_level_graph_generators/paper.log @@ -1,4 +1,4 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 20:44 +This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 23:45 entering extended mode restricted \write18 enabled. %&-line parsing enabled. @@ -387,24 +387,24 @@ LaTeX Warning: `h' float specifier changed to `ht'. ng>] [4] Package hyperref Warning: Token not allowed in a PDF string (Unicode): -(hyperref) removing `math shift' on input line 324. +(hyperref) removing `math shift' on input line 355. Package hyperref Warning: Token not allowed in a PDF string (Unicode): -(hyperref) removing `math shift' on input line 324. +(hyperref) removing `math shift' on input line 355. -[5] (./paper.aux) +[5] [6] (./paper.aux) Package rerunfilecheck Info: File `paper.out' has not changed. (rerunfilecheck) Checksum: AECCB746CF11915BCB68F1E7FF8075A7;1047. ) Here is how much of TeX's memory you used: - 9737 strings out of 478268 - 150746 string characters out of 5846347 - 453840 words of memory out of 5000000 - 27645 multiletter control sequences out of 15000+600000 + 9742 strings out of 478268 + 150825 string characters out of 5846347 + 454933 words of memory out of 5000000 + 27647 multiletter control sequences out of 15000+600000 475666 words of font info for 53 fonts, out of 8000000 for 9000 1302 hyphenation exceptions out of 8191 - 69i,8n,76p,781b,427s stack positions out of 10000i,1000n,20000p,200000b,200000s + 69i,8n,76p,781b,504s stack positions out of 10000i,1000n,20000p,200000b,200000s @@ -418,10 +418,10 @@ texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb> -Output written on paper.pdf (5 pages, 545810 bytes). +Output written on paper.pdf (6 pages, 548304 bytes). PDF statistics: - 162 PDF objects out of 1000 (max. 8388607) - 119 compressed objects within 2 object streams - 30 named destinations out of 1000 (max. 500000) + 171 PDF objects out of 1000 (max. 8388607) + 127 compressed objects within 2 object streams + 33 named destinations out of 1000 (max. 500000) 77 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/papers/even_level_graph_generators/paper.pdf b/papers/even_level_graph_generators/paper.pdf index 7db2042..180fe72 100644 Binary files a/papers/even_level_graph_generators/paper.pdf and b/papers/even_level_graph_generators/paper.pdf differ diff --git a/papers/even_level_graph_generators/paper.tex b/papers/even_level_graph_generators/paper.tex index fce6242..21ab465 100644 --- a/papers/even_level_graph_generators/paper.tex +++ b/papers/even_level_graph_generators/paper.tex @@ -250,6 +250,37 @@ A derived level graph $G'$ is \emph{valid} if both $E_{G,S}(G')$ and $O_{G,S}(G')$ contain only even cycles. \end{definition} +\begin{definition}[Bridge switch] +\label{def:bridge-switch} +Let $G'$ be a triangulation reached from an Even Level Graph $G$, with +parity classes inherited from $G$ as in +Definition~\ref{def:derived-level-graph}. An edge switch on an edge +$e \in E \cup O$ of $G'$, replacing $uvw, uvx$ by the edge $wx$, is a +\emph{bridge switch} if either +\begin{itemize} +\item the new edge $wx$ is a cross-parity edge (one endpoint even, the +other odd), so $wx$ enters neither parity subgraph; or +\item $wx$ is a same-parity edge and is a \emph{bridge} in the parity +subgraph it joins -- that is, $w$ and $x$ lie in different connected +components of that parity subgraph, so adding $wx$ creates no new cycle. +\end{itemize} +\end{definition} + +\begin{definition}[Bridge-derived level graph] +\label{def:bridge-derived-level-graph} +A \emph{bridge-derived level graph} of an Even Level Graph $G$ is a +triangulation obtained from $G$ by a sequence of bridge switches +(Definition~\ref{def:bridge-switch}). +\end{definition} + +Because a bridge switch never closes a cycle in a parity subgraph, it +never introduces an odd cycle there. As an Even Level Graph has +bipartite parity subgraphs (every level cycle is even), every +bridge-derived level graph has bipartite parity subgraphs as well, and +so is automatically a valid derived level graph. Equivalently, the +first Betti number of each parity subgraph is non-increasing along any +sequence of bridge switches. + \begin{definition}[Intertwining tree] \label{def:intertwining-tree} A maximal planar graph $G$ is an \emph{intertwining tree} if its