diff --git a/papers/even_level_graph_generators/experiments/plot_counterexample.py b/papers/even_level_graph_generators/experiments/plot_counterexample.py new file mode 100644 index 0000000..59a8079 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/plot_counterexample.py @@ -0,0 +1,38 @@ +"""Plot iso[49] at n=9, the counterexample to Conjecture 4.4.""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +import networkx as nx +import matplotlib.pyplot as plt +from triangulation_gen import enumerate_all_triangulations + +OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +tris = enumerate_all_triangulations(9) +G = tris[49] +print(f'iso[49] degree sequence: {sorted([G.degree(v) for v in G.nodes()], reverse=True)}') +print(f'iso[49] edges: {sorted(G.edges())}') + +# Use planar layout +_, emb = nx.check_planarity(G) +pos = nx.combinatorial_embedding_to_pos(emb) + +fig, ax = plt.subplots(figsize=(8, 7)) +nx.draw_networkx_edges(G, pos, ax=ax, edge_color='#333', width=1.5) +# Color vertices by degree +degree_color = {4: '#3b82f6', 5: '#dc2626'} +node_colors = [degree_color[G.degree(v)] for v in G.nodes()] +nx.draw_networkx_nodes(G, pos, ax=ax, node_color=node_colors, + node_size=600, edgecolors='black', linewidths=1.2) +nx.draw_networkx_labels(G, pos, ax=ax, font_color='white', + font_size=11, font_weight='bold') +ax.set_aspect('equal'); ax.axis('off') +ax.set_title('iso[49] at $n=9$: degree sequence (5,5,5,5,5,5,4,4,4).\n' + 'NOT a valid derived level graph of any Even Level Graph.\n' + 'Blue = degree 4, Red = degree 5.', fontsize=11) +fig.tight_layout() +out = os.path.join(OUT_DIR, 'fig_n9_counterexample.png') +fig.savefig(out, dpi=180, bbox_inches='tight') +plt.close(fig) +print(f'wrote {out}') diff --git a/papers/even_level_graph_generators/experiments/plot_valid_partition.py b/papers/even_level_graph_generators/experiments/plot_valid_partition.py new file mode 100644 index 0000000..83089f5 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/plot_valid_partition.py @@ -0,0 +1,92 @@ +"""Show a valid parity partition of iso[49] at n=9, with the induced +4-coloring.""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +import networkx as nx +import matplotlib.pyplot as plt +from triangulation_gen import enumerate_all_triangulations + +OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +tris = enumerate_all_triangulations(9) +G = tris[49] + +V_E = (0, 1, 3, 6) +V_O = (2, 4, 5, 7, 8) + +# 2-color each induced subgraph +def bipart_coloring(subg): + # BFS-based 2-coloring; returns dict v -> 0/1 + cmap = {} + for start in subg.nodes(): + if start in cmap: continue + cmap[start] = 0 + frontier = [start] + while frontier: + new = [] + for u in frontier: + for w in subg.neighbors(u): + if w not in cmap: + cmap[w] = 1 - cmap[u] + new.append(w) + frontier = new + return cmap + +cE = bipart_coloring(G.subgraph(V_E)) +cO = bipart_coloring(G.subgraph(V_O)) +# 4-color: even gets red/blue, odd gets yellow/green +COLOR_RED, COLOR_BLUE = '#dc2626', '#3b82f6' +COLOR_YELLOW, COLOR_GREEN = '#eab308', '#16a34a' +four_color = {} +for v in V_E: + four_color[v] = COLOR_RED if cE[v] == 0 else COLOR_BLUE +for v in V_O: + four_color[v] = COLOR_YELLOW if cO[v] == 0 else COLOR_GREEN + +# Verify proper 4-coloring +for u, v in G.edges(): + assert four_color[u] != four_color[v], \ + f'edge ({u},{v}) violates 4-coloring: {four_color[u]} vs {four_color[v]}' +print('4-coloring is proper.') + +_, emb = nx.check_planarity(G) +pos = nx.combinatorial_embedding_to_pos(emb) + +fig, ax = plt.subplots(figsize=(9, 8)) +nx.draw_networkx_edges(G, pos, ax=ax, edge_color='#333', width=1.5) +node_colors = [four_color[v] for v in G.nodes()] +nx.draw_networkx_nodes(G, pos, ax=ax, node_color=node_colors, + node_size=700, edgecolors='black', linewidths=1.3) +nx.draw_networkx_labels(G, pos, ax=ax, font_color='white', + font_size=12, font_weight='bold') +ax.set_aspect('equal'); ax.axis('off') + +# Legend +import matplotlib.patches as mpatches +legend_handles = [ + mpatches.Patch(color=COLOR_RED, label=r'even-parity vertex, bipartition class 0'), + mpatches.Patch(color=COLOR_BLUE, label=r'even-parity vertex, bipartition class 1'), + mpatches.Patch(color=COLOR_YELLOW, label=r'odd-parity vertex, bipartition class 0'), + mpatches.Patch(color=COLOR_GREEN, label=r'odd-parity vertex, bipartition class 1'), +] +ax.legend(handles=legend_handles, loc='lower center', + bbox_to_anchor=(0.5, -0.08), ncol=2, fontsize=9, frameon=False) + +ax.set_title(f'iso[49] with a valid parity partition\n' + f'$V_E = \\{{0, 1, 3, 6\\}}$, ' + f'$V_O = \\{{2, 4, 5, 7, 8\\}}$\n' + f'(both induced subgraphs bipartite; 4-coloring derived)', + fontsize=11) +fig.tight_layout() +out = os.path.join(OUT_DIR, 'fig_n9_valid_partition.png') +fig.savefig(out, dpi=180, bbox_inches='tight') +plt.close(fig) +print(f'wrote {out}') + +# Also report the induced subgraphs +GE = G.subgraph(V_E) +GO = G.subgraph(V_O) +print(f'G[V_E] = {sorted(GE.edges())} bipartite={nx.is_bipartite(GE)}') +print(f'G[V_O] = {sorted(GO.edges())} bipartite={nx.is_bipartite(GO)}') diff --git a/papers/even_level_graph_generators/experiments/test_conjecture.py b/papers/even_level_graph_generators/experiments/test_conjecture.py new file mode 100644 index 0000000..af93ba3 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/test_conjecture.py @@ -0,0 +1,172 @@ +"""Empirical test of the derived-level-graph conjecture for n=6..8. + +For each iso class of maximal planar graphs G': + Search for an Even Level Graph G (some iso class, some level source) + such that G' is in the iso-class orbit of G under E/O-edge switches. +""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +import time +import itertools +import networkx as nx +from triangulation_gen import enumerate_all_triangulations + + +def canonical_sig(G): + """Iso-class signature: WL hash via Weisfeiler-Lehman or just sorted + edge tuples up to vertex relabelling. We use nx.weisfeiler_lehman_graph_hash.""" + return nx.weisfeiler_lehman_graph_hash(G) + + +def labelled_sig(G, labels): + """Signature that respects both graph structure and a vertex labelling + (even/odd). Two graphs match iff there's an iso preserving labels.""" + H = G.copy() + for v in H.nodes(): + H.nodes[v]['label'] = labels[v] + return nx.weisfeiler_lehman_graph_hash(H, node_attr='label') + + +def bfs_levels(G, source_set): + """BFS from a set of source vertices in G. Returns dict v -> level.""" + levels = {v: float('inf') for v in G.nodes()} + frontier = list(source_set) + for v in frontier: + levels[v] = 0 + d = 0 + while frontier: + next_frontier = [] + for u in frontier: + for w in G.neighbors(u): + if levels[w] > d + 1: + levels[w] = d + 1 + next_frontier.append(w) + frontier = next_frontier + d += 1 + return levels + + +def get_level_sources(G): + """Yield each vertex as a possible level source (singleton set).""" + for v in G.nodes(): + yield frozenset({v}) + + +def is_even_level_graph(G, source): + """Check: every level cycle of G (BFS from source) has even length.""" + levels = bfs_levels(G, source) + if any(l == float('inf') for l in levels.values()): + return False, None + max_l = max(levels.values()) + for k in range(max_l + 1): + L_k_nodes = [v for v in G.nodes() if levels[v] == k] + L_k = G.subgraph(L_k_nodes) + if L_k.number_of_edges() == 0: + continue + # Check bipartiteness (equivalent to no odd cycles) + if not nx.is_bipartite(L_k): + return False, None + return True, levels + + +def is_valid_parity_partition(G, labels): + """Both induced subgraphs G[V_E] and G[V_O] are bipartite.""" + V_E = [v for v in G.nodes() if labels[v] % 2 == 0] + V_O = [v for v in G.nodes() if labels[v] % 2 == 1] + return nx.is_bipartite(G.subgraph(V_E)) and nx.is_bipartite(G.subgraph(V_O)) + + +def E_O_switches(G, labels): + """Yield all triangulations reachable from G by one E/O-edge switch. + An E/O-edge has both endpoints of the same parity in `labels`.""" + ip, emb = nx.check_planarity(G) + if not ip: + return + yielded = set() + 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) + sig = frozenset(frozenset(e) for e in Gp.edges()) + if sig in yielded: + continue + yielded.add(sig) + yield Gp + + +def bfs_orbit(G_start, labels, max_states=200000): + """BFS in E/O-switch graph starting from G_start (with given labels). + Returns the set of iso classes (unlabelled) reachable.""" + seen_labelled = {frozenset(frozenset(e) for e in G_start.edges())} + iso_classes_reached = {canonical_sig(G_start)} + frontier = [G_start] + rounds = 0 + while frontier and len(seen_labelled) < max_states: + new = [] + for G in frontier: + for Gp in E_O_switches(G, labels): + sig = frozenset(frozenset(e) for e in Gp.edges()) + if sig in seen_labelled: + continue + seen_labelled.add(sig) + iso_classes_reached.add(canonical_sig(Gp)) + new.append(Gp) + frontier = new + rounds += 1 + return iso_classes_reached, len(seen_labelled), rounds + + +def test_for_n(n): + print(f'\n=== n = {n} ===') + t0 = time.time() + tris = enumerate_all_triangulations(n) + print(f' {len(tris)} iso classes of triangulations') + iso_class_sigs = {canonical_sig(T) for T in tris} + + # Set of iso classes that ARE derived level graphs (of some ELG) + derived_iso_classes = set() + + for i, G in enumerate(tris): + if len(derived_iso_classes) == len(iso_class_sigs): + break # early exit: everything is already covered + for source in get_level_sources(G): + is_elg, levels = is_even_level_graph(G, source) + if not is_elg: + continue + reached, n_lbld, rnds = bfs_orbit(G, levels) + new_count = len(reached - derived_iso_classes) + derived_iso_classes.update(reached) + if new_count > 0: + print(f' iso[{i}] source={sorted(source)}: ELG, ' + f'orbit adds {new_count} new iso classes ' + f'(orbit size {len(reached)}, ' + f'{n_lbld} labelled, {rnds} rounds, ' + f'total {len(derived_iso_classes)}/' + f'{len(iso_class_sigs)})') + + missing = iso_class_sigs - derived_iso_classes + print(f' TOTAL: {len(derived_iso_classes)} / {len(iso_class_sigs)} ' + f'iso classes are derived level graphs') + print(f' missing: {len(missing)}') + print(f' elapsed: {time.time() - t0:.1f}s') + return len(missing) == 0 + + +if __name__ == '__main__': + import sys + ns = [int(x) for x in sys.argv[1:]] if len(sys.argv) > 1 else [6, 7, 8] + for n in ns: + ok = test_for_n(n) + print(f' conjecture holds for n={n}: {ok}') diff --git a/papers/even_level_graph_generators/experiments/test_disjunction.py b/papers/even_level_graph_generators/experiments/test_disjunction.py new file mode 100644 index 0000000..0606beb --- /dev/null +++ b/papers/even_level_graph_generators/experiments/test_disjunction.py @@ -0,0 +1,120 @@ +"""Test the disjunction: every maximal planar graph is a valid derived +level graph, an intertwining tree, or both. Iterates n=6..12, stops if +a counterexample is found.""" +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 time +import itertools +import networkx as nx +from triangulation_gen import enumerate_all_triangulations +from test_conjecture import ( + canonical_sig, bfs_levels, get_level_sources, + is_even_level_graph, bfs_orbit +) + + +def is_intertwining_tree(G): + """Search for a 2-partition (A, B) such that G[A] and G[B] are trees.""" + nodes = list(G.nodes()) + n = len(nodes) + # Try all 2^(n-1) partitions (fix node 0 in A by convention) + for mask in range(1, 2 ** (n - 1)): + A = [nodes[0]] + [nodes[i + 1] for i in range(n - 1) if (mask >> i) & 1] + B = [nodes[i + 1] for i in range(n - 1) if not ((mask >> i) & 1)] + if not A or not B: + continue + GA = G.subgraph(A) + GB = G.subgraph(B) + if nx.is_tree(GA) and nx.is_tree(GB): + return True, (tuple(A), tuple(B)) + return False, None + + +def derived_level_graph_iso_classes(tris): + """Compute the set of iso class signatures that are derived level + graphs of some Even Level Graph.""" + iso_class_sigs = {canonical_sig(T) for T in tris} + derived = set() + for G in tris: + if len(derived) == len(iso_class_sigs): + break + for source in get_level_sources(G): + is_elg, levels = is_even_level_graph(G, source) + if not is_elg: + continue + reached, _, _ = bfs_orbit(G, levels) + derived.update(reached) + return derived + + +def test_n(n): + t0 = time.time() + tris = enumerate_all_triangulations(n) + iso_sigs = [canonical_sig(T) for T in tris] + derived = derived_level_graph_iso_classes(tris) + n_derived = sum(1 for s in iso_sigs if s in derived) + + # For iso classes that are NOT derived, check intertwining tree + counterexamples = [] + n_intertwining_only = 0 + n_both = 0 + for i, G in enumerate(tris): + is_derived = iso_sigs[i] in derived + is_inter, partition = is_intertwining_tree(G) + if is_derived and is_inter: + n_both += 1 + elif is_derived: + pass # only derived + elif is_inter: + n_intertwining_only += 1 + else: + counterexamples.append((i, G)) + + n_total = len(tris) + n_only_derived = n_derived - n_both + elapsed = time.time() - t0 + return { + 'n': n, + 'total': n_total, + 'derived': n_derived, + 'intertwining_only': n_intertwining_only, + 'both': n_both, + 'only_derived': n_only_derived, + 'counterexamples': counterexamples, + 'elapsed': elapsed, + } + + +def main(): + results = [] + for n in [6, 7, 8, 9, 10, 11, 12]: + print(f'\n=== n = {n} ===') + r = test_n(n) + results.append(r) + print(f' total iso classes: {r["total"]}') + print(f' derived only: {r["only_derived"]}') + print(f' intertwining only: {r["intertwining_only"]}') + print(f' both: {r["both"]}') + print(f' counterexamples: {len(r["counterexamples"])}') + print(f' elapsed: {r["elapsed"]:.1f}s') + if r['counterexamples']: + print(f' COUNTEREXAMPLE FOUND. Stopping.') + for i, G in r['counterexamples'][:3]: + print(f' iso[{i}] degree seq = ' + f'{sorted([G.degree(v) for v in G.nodes()], reverse=True)}') + break + + print('\n=== Final summary ===') + print(f'{"n":>3} {"total":>6} {"deriv":>6} {"inter":>6} {"both":>6} {"missing":>8}') + for r in results: + cov = r['only_derived'] + r['intertwining_only'] + r['both'] + missing = r['total'] - cov + print(f'{r["n"]:>3} {r["total"]:>6} {r["only_derived"]:>6} ' + f'{r["intertwining_only"]:>6} {r["both"]:>6} {missing:>8}') + + +if __name__ == '__main__': + main() diff --git a/papers/even_level_graph_generators/experiments/tutte_dual_treecolor.py b/papers/even_level_graph_generators/experiments/tutte_dual_treecolor.py new file mode 100644 index 0000000..c2ece45 --- /dev/null +++ b/papers/even_level_graph_generators/experiments/tutte_dual_treecolor.py @@ -0,0 +1,141 @@ +"""Test whether the dual of the Tutte graph (46-vertex 3-connected planar +cubic non-Hamiltonian) admits a tree coloring. + +The Lederberg-Bosak-Barnette graph (38 vertices) is the smallest known +counterexample to Tait's conjecture but isn't directly available in +networkx; the Tutte graph (1946 original counterexample) is. +""" +import sys +import os +sys.path.insert(0, '/Users/didericis/Code/math-research/papers/' + 'level_resolutions_of_maximal_planar_graphs/experiments') +import networkx as nx +import time + + +def dual_triangulation(G): + """Build the planar dual of a 3-connected planar cubic graph. + + Each face of G becomes a vertex of the dual; each edge of G between + two faces becomes a dual edge. Cubic G ⇒ every dual face is a + triangle ⇒ dual is a triangulation.""" + ok, emb = nx.check_planarity(G) + assert ok + faces = [] + seen = set() + for u, v in G.edges(): + for src, dst in [(u, v), (v, u)]: + face = tuple(emb.traverse_face(src, dst)) + key = frozenset(face) + if key in seen: + continue + seen.add(key) + faces.append(face) + + # The "outer" face is the one with the largest vertex set (heuristic); + # for connectivity of the dual, we just include all faces. + face_of_edge = {} # edge (u, v) -> set of face indices it borders + for i, face in enumerate(faces): + for j in range(len(face)): + a, b = face[j], face[(j + 1) % len(face)] + key = frozenset((a, b)) + face_of_edge.setdefault(key, []).append(i) + + D = nx.Graph() + D.add_nodes_from(range(len(faces))) + for key, face_ids in face_of_edge.items(): + if len(face_ids) == 2: + D.add_edge(face_ids[0], face_ids[1]) + # If len > 2 or 1, multi-edges or self-loops would appear; for + # 3-connected G this shouldn't happen. + return D, faces + + +def is_tree(subg): + return nx.is_tree(subg) if subg.number_of_nodes() > 0 else True + + +def has_tree_property_for_some_pairing(G, coloring): + pairings = [({0, 1}, {2, 3}), ({0, 2}, {1, 3}), ({0, 3}, {1, 2})] + for p1, p2 in pairings: + V1 = [v for v in G.nodes() if coloring[v] in p1] + V2 = [v for v in G.nodes() if coloring[v] in p2] + if is_tree(G.subgraph(V1)) and is_tree(G.subgraph(V2)): + return True, (p1, p2) + return False, None + + +def find_tree_coloring(G, time_limit=60.0): + """Backtracking search for a 4-coloring with the tree property.""" + nodes = list(G.nodes()) + n = len(nodes) + colors = [None] * n + adj = {v: set(G.neighbors(v)) for v in nodes} + idx_of = {v: i for i, v in enumerate(nodes)} + t0 = time.time() + visited = [0] + + def bt(i): + if time.time() - t0 > time_limit: + return None + visited[0] += 1 + if i == n: + coloring = dict(zip(nodes, colors)) + ok, pair = has_tree_property_for_some_pairing(G, coloring) + return (coloring, pair) if ok else None + v = nodes[i] + forbidden = set() + for w in adj[v]: + if idx_of[w] < i: + forbidden.add(colors[idx_of[w]]) + for c in range(4): + if c in forbidden: + continue + colors[i] = c + r = bt(i + 1) + if r is not None: + return r + colors[i] = None + return None + + return bt(0), visited[0] + + +def main(): + G = nx.tutte_graph() + print(f'Tutte graph: {G.number_of_nodes()} vertices, ' + f'{G.number_of_edges()} edges, planar=True, cubic, 3-connected, ' + f'non-Hamiltonian (Tutte 1946).') + + D, faces = dual_triangulation(G) + print(f'Dual: {D.number_of_nodes()} vertices, ' + f'{D.number_of_edges()} edges') + print(f' is_triangulation (3n-6 edges): ' + f'{D.number_of_edges() == 3 * D.number_of_nodes() - 6}') + print(f' degree sequence (sorted desc): ' + f'{sorted([D.degree(v) for v in D.nodes()], reverse=True)}') + + print('Searching for a tree coloring...') + t0 = time.time() + result, n_visited = find_tree_coloring(D, time_limit=120.0) + elapsed = time.time() - t0 + if result is None: + print(f' no tree coloring found within time limit ' + f'({elapsed:.1f}s, {n_visited} states visited)') + else: + coloring, pair = result + print(f' tree coloring FOUND ({elapsed:.1f}s, {n_visited} states).') + print(f' pairing: {pair}') + p1, p2 = pair + V1 = [v for v in D.nodes() if coloring[v] in p1] + V2 = [v for v in D.nodes() if coloring[v] in p2] + sub1 = D.subgraph(V1) + sub2 = D.subgraph(V2) + print(f' D[V1] (colors {p1}): {len(V1)} vertices, ' + f'{sub1.number_of_edges()} edges, tree={nx.is_tree(sub1)}') + print(f' D[V2] (colors {p2}): {len(V2)} vertices, ' + f'{sub2.number_of_edges()} edges, tree={nx.is_tree(sub2)}') + + +if __name__ == '__main__': + main() diff --git a/papers/even_level_graph_generators/fig_edge_switch.png b/papers/even_level_graph_generators/fig_edge_switch.png new file mode 100644 index 0000000..29b7265 Binary files /dev/null and b/papers/even_level_graph_generators/fig_edge_switch.png differ diff --git a/papers/even_level_graph_generators/fig_level_cycle.png b/papers/even_level_graph_generators/fig_level_cycle.png new file mode 100644 index 0000000..9ad253b Binary files /dev/null and b/papers/even_level_graph_generators/fig_level_cycle.png differ diff --git a/papers/even_level_graph_generators/fig_levels.png b/papers/even_level_graph_generators/fig_levels.png new file mode 100644 index 0000000..193754c Binary files /dev/null and b/papers/even_level_graph_generators/fig_levels.png differ diff --git a/papers/even_level_graph_generators/fig_n9_counterexample.png b/papers/even_level_graph_generators/fig_n9_counterexample.png new file mode 100644 index 0000000..c9b67f2 Binary files /dev/null and b/papers/even_level_graph_generators/fig_n9_counterexample.png differ diff --git a/papers/even_level_graph_generators/fig_n9_valid_partition.png b/papers/even_level_graph_generators/fig_n9_valid_partition.png new file mode 100644 index 0000000..b266724 Binary files /dev/null and b/papers/even_level_graph_generators/fig_n9_valid_partition.png differ diff --git a/papers/even_level_graph_generators/fig_parity_subgraph.png b/papers/even_level_graph_generators/fig_parity_subgraph.png new file mode 100644 index 0000000..0c2108f Binary files /dev/null and b/papers/even_level_graph_generators/fig_parity_subgraph.png differ diff --git a/papers/even_level_graph_generators/paper.aux b/papers/even_level_graph_generators/paper.aux new file mode 100644 index 0000000..a582057 --- /dev/null +++ b/papers/even_level_graph_generators/paper.aux @@ -0,0 +1,46 @@ +\relax +\providecommand\hyper@newdestlabel[2]{} +\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} +\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined +\global\let\oldcontentsline\contentsline +\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}} +\global\let\oldnewlabel\newlabel +\gdef\newlabel#1#2{\newlabelxx{#1}#2} +\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} +\AtEndDocument{\ifx\hyper@anchor\@undefined +\let\contentsline\oldcontentsline +\let\newlabel\oldnewlabel +\fi} +\fi} +\global\let\hyper@last\relax +\gdef\HyperFirstAtBeginDocument#1{#1} +\providecommand\HyField@AuxAddToFields[1]{} +\providecommand\HyField@AuxAddToCoFields[2]{} +\@writefile{toc}{\contentsline {section}{\tocsection {}{1}{Introduction}}{1}{section.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\tocsection {}{2}{Definitions}}{1}{section.2}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces BFS levels from the degree-$3$ vertex source $S = \{4\}$. The source is level $0$, its three neighbours are level $1$, and the remaining vertices are level $2$. Colour encodes the level.}}{1}{figure.1}\protected@file@percent } +\newlabel{fig:levels}{{1}{1}{BFS levels from the degree-$3$ vertex source $S = \{4\}$. The source is level $0$, its three neighbours are level $1$, and the remaining vertices are level $2$. Colour encodes the level}{figure.1}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces A level cycle in the triangulation of Figure\nonbreakingspace \ref {fig:levels}. The triangle $1\!-\!2\!-\!3$ is a simple cycle whose three vertices all lie at level $1$, so it is a level cycle at level $1$.}}{2}{figure.2}\protected@file@percent } +\newlabel{fig:level-cycle}{{2}{2}{A level cycle in the triangulation of Figure~\ref {fig:levels}. The triangle $1\!-\!2\!-\!3$ is a simple cycle whose three vertices all lie at level $1$, so it is a level cycle at level $1$}{figure.2}{}} +\newlabel{def:edge-switch}{{2.4}{2}{Edge switch}{theorem.2.4}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces An edge switch on the level cycle of Figure\nonbreakingspace \ref {fig:level-cycle}. The chosen cycle edge $1\!-\!2$ is shared by the triangular faces $(0,1,2)$ and $(1,2,4)$; the switch deletes $1\!-\!2$ (red, left) and inserts $0\!-\!4$ (green, right). Vertex colours indicate the original levels in $G$.}}{2}{figure.3}\protected@file@percent } +\newlabel{fig:edge-switch}{{3}{2}{An edge switch on the level cycle of Figure~\ref {fig:level-cycle}. The chosen cycle edge $1\!-\!2$ is shared by the triangular faces $(0,1,2)$ and $(1,2,4)$; the switch deletes $1\!-\!2$ (red, left) and inserts $0\!-\!4$ (green, right). Vertex colours indicate the original levels in $G$}{figure.3}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces Parity subgraphs of $G' = T$ with respect to the level structure of Figure\nonbreakingspace \ref {fig:levels} (here we take $G = G' = T$). Left: $T$ with vertices coloured by $\ell _G \nonscript \mskip -\medmuskip \mkern 5mu\mathbin {\mathgroup \symoperators mod}\penalty 900 \mkern 5mu\nonscript \mskip -\medmuskip 2$ (blue $=$ even, orange $=$ odd). Middle: the even parity subgraph $E_{G,S}(G')$, induced on $\{0, 4, 5, 6\}$; only edges with both endpoints even appear. Right: the odd parity subgraph $O_{G,S}(G')$, induced on $\{1, 2, 3\}$; the highlighted triangle shows that $O_{G,S}(G')$ is not bipartite for this choice of $G'$.}}{3}{figure.4}\protected@file@percent } +\newlabel{fig:parity-subgraph}{{4}{3}{Parity subgraphs of $G' = T$ with respect to the level structure of Figure~\ref {fig:levels} (here we take $G = G' = T$). Left: $T$ with vertices coloured by $\ell _G \bmod 2$ (blue $=$ even, orange $=$ odd). Middle: the even parity subgraph $E_{G,S}(G')$, induced on $\{0, 4, 5, 6\}$; only edges with both endpoints even appear. Right: the odd parity subgraph $O_{G,S}(G')$, induced on $\{1, 2, 3\}$; the highlighted triangle shows that $O_{G,S}(G')$ is not bipartite for this choice of $G'$}{figure.4}{}} +\@writefile{toc}{\contentsline {section}{\tocsection {}{3}{Outerplanarity of level components}}{3}{section.3}\protected@file@percent } +\newlabel{sec:outerplanar-components}{{3}{3}{Outerplanarity of level components}{section.3}{}} +\newlabel{thm:outerplanar-component}{{3.1}{3}{}{theorem.3.1}{}} +\@writefile{toc}{\contentsline {section}{\tocsection {}{4}{Even Level Graphs}}{3}{section.4}\protected@file@percent } +\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}{}} +\newlabel{tocindent-1}{0pt} +\newlabel{tocindent0}{14.69437pt} +\newlabel{tocindent1}{17.77782pt} +\newlabel{tocindent2}{0pt} +\newlabel{tocindent3}{0pt} +\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{conj:every-triangulation-derived}{{4.5}{4}{}{theorem.4.5}{}} +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical status}}{4}{section*.1}\protected@file@percent } +\gdef \@abspage@last{4} diff --git a/papers/even_level_graph_generators/paper.log b/papers/even_level_graph_generators/paper.log new file mode 100644 index 0000000..13f382c --- /dev/null +++ b/papers/even_level_graph_generators/paper.log @@ -0,0 +1,418 @@ +This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 16:14 +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**paper.tex +(./paper.tex +LaTeX2e <2021-11-15> patch level 1 +L3 programming layer <2022-02-24> +(/usr/local/texlive/2022/texmf-dist/tex/latex/amscls/amsart.cls +Document Class: amsart 2020/05/29 v2.20.6 +\linespacing=\dimen138 +\normalparindent=\dimen139 +\normaltopskip=\skip47 +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsmath.sty +Package: amsmath 2021/10/15 v2.17l AMS math features +\@mathmargin=\skip48 + +For additional information on amsmath, use the `?' option. +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amstext.sty +Package: amstext 2021/08/26 v2.01 AMS text + +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsgen.sty +File: amsgen.sty 1999/11/30 v2.0 generic functions +\@emptytoks=\toks16 +\ex@=\dimen140 +)) +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsbsy.sty +Package: amsbsy 1999/11/29 v1.2d Bold Symbols +\pmbraise@=\dimen141 +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsopn.sty +Package: amsopn 2021/08/26 v2.02 operator names +) +\inf@bad=\count185 +LaTeX Info: Redefining \frac on input line 234. +\uproot@=\count186 +\leftroot@=\count187 +LaTeX Info: Redefining \overline on input line 399. +\classnum@=\count188 +\DOTSCASE@=\count189 +LaTeX Info: Redefining \ldots on input line 496. +LaTeX Info: Redefining \dots on input line 499. +LaTeX Info: Redefining \cdots on input line 620. +\Mathstrutbox@=\box50 +\strutbox@=\box51 +\big@size=\dimen142 +LaTeX Font Info: Redeclaring font encoding OML on input line 743. +LaTeX Font Info: Redeclaring font encoding OMS on input line 744. +\macc@depth=\count190 +\c@MaxMatrixCols=\count191 +\dotsspace@=\muskip16 +\c@parentequation=\count192 +\dspbrk@lvl=\count193 +\tag@help=\toks17 +\row@=\count194 +\column@=\count195 +\maxfields@=\count196 +\andhelp@=\toks18 +\eqnshift@=\dimen143 +\alignsep@=\dimen144 +\tagshift@=\dimen145 +\tagwidth@=\dimen146 +\totwidth@=\dimen147 +\lineht@=\dimen148 +\@envbody=\toks19 +\multlinegap=\skip49 +\multlinetaggap=\skip50 +\mathdisplay@stack=\toks20 +LaTeX Info: Redefining \[ on input line 2938. +LaTeX Info: Redefining \] on input line 2939. +) +LaTeX Font Info: Trying to load font information for U+msa on input line 397 +. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd +File: umsa.fd 2013/01/14 v3.01 AMS symbols A +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amsfonts.sty +Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support +\symAMSa=\mathgroup4 +\symAMSb=\mathgroup5 +LaTeX Font Info: Redeclaring math symbol \hbar on input line 98. +LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' +(Font) U/euf/m/n --> U/euf/b/n on input line 106. +) +\copyins=\insert199 +\abstractbox=\box52 +\listisep=\skip51 +\c@part=\count197 +\c@section=\count198 +\c@subsection=\count266 +\c@subsubsection=\count267 +\c@paragraph=\count268 +\c@subparagraph=\count269 +\c@figure=\count270 +\c@table=\count271 +\abovecaptionskip=\skip52 +\belowcaptionskip=\skip53 +\captionindent=\dimen149 +\thm@style=\toks21 +\thm@bodyfont=\toks22 +\thm@headfont=\toks23 +\thm@notefont=\toks24 +\thm@headpunct=\toks25 +\thm@preskip=\skip54 +\thm@postskip=\skip55 +\thm@headsep=\skip56 +\dth@everypar=\toks26 +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/hyperref/hyperref.sty +Package: hyperref 2022-02-21 v7.00n Hypertext links for LaTeX + +(/usr/local/texlive/2022/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty +Package: ltxcmds 2020-05-10 v1.25 LaTeX kernel commands for general use (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/iftex/iftex.sty +Package: iftex 2022/02/03 v1.0f TeX engine tests +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty +Package: pdftexcmds 2020-06-27 v0.33 Utility functions of pdfTeX for LuaTeX (HO +) + +(/usr/local/texlive/2022/texmf-dist/tex/generic/infwarerr/infwarerr.sty +Package: infwarerr 2019/12/03 v1.5 Providing info/warning/error messages (HO) +) +Package pdftexcmds Info: \pdf@primitive is available. +Package pdftexcmds Info: \pdf@ifprimitive is available. +Package pdftexcmds Info: \pdfdraftmode found. +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 2014/10/28 v1.15 key=value parser (DPC) +\KV@toks@=\toks27 +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/kvsetkeys/kvsetkeys.sty +Package: kvsetkeys 2019/12/15 v1.18 Key value parser (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty +Package: kvdefinekeys 2019-12-19 v1.6 Define keys (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/pdfescape/pdfescape.sty +Package: pdfescape 2019/12/09 v1.15 Implements pdfTeX's escape features (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/hycolor/hycolor.sty +Package: hycolor 2020-01-27 v1.10 Color options for hyperref/bookmark (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/letltxmacro/letltxmacro.sty +Package: letltxmacro 2019/12/03 v1.6 Let assignment for LaTeX macros (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/auxhook/auxhook.sty +Package: auxhook 2019-12-17 v1.6 Hooks for auxiliary files (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/kvoptions/kvoptions.sty +Package: kvoptions 2020-10-07 v3.14 Key value format for package options (HO) +) +\@linkdim=\dimen150 +\Hy@linkcounter=\count272 +\Hy@pagecounter=\count273 + +(/usr/local/texlive/2022/texmf-dist/tex/latex/hyperref/pd1enc.def +File: pd1enc.def 2022-02-21 v7.00n Hyperref: PDFDocEncoding definition (HO) +Now handling font encoding PD1 ... +... no UTF-8 mapping file for font encoding PD1 +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/intcalc/intcalc.sty +Package: intcalc 2019/12/15 v1.3 Expandable calculations with integers (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/etexcmds/etexcmds.sty +Package: etexcmds 2019/12/15 v1.7 Avoid name clashes with e-TeX commands (HO) +) +\Hy@SavedSpaceFactor=\count274 + +(/usr/local/texlive/2022/texmf-dist/tex/latex/hyperref/puenc.def +File: puenc.def 2022-02-21 v7.00n Hyperref: PDF Unicode definition (HO) +Now handling font encoding PU ... +... no UTF-8 mapping file for font encoding PU +) +Package hyperref Info: Hyper figures OFF on input line 4137. +Package hyperref Info: Link nesting OFF on input line 4142. +Package hyperref Info: Hyper index ON on input line 4145. +Package hyperref Info: Plain pages OFF on input line 4152. +Package hyperref Info: Backreferencing OFF on input line 4157. +Package hyperref Info: Implicit mode ON; LaTeX internals redefined. +Package hyperref Info: Bookmarks ON on input line 4390. +\c@Hy@tempcnt=\count275 + +(/usr/local/texlive/2022/texmf-dist/tex/latex/url/url.sty +\Urlmuskip=\muskip17 +Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. +) +LaTeX Info: Redefining \url on input line 4749. +\XeTeXLinkMargin=\dimen151 + +(/usr/local/texlive/2022/texmf-dist/tex/generic/bitset/bitset.sty +Package: bitset 2019/12/09 v1.3 Handle bit-vector datatype (HO) + +(/usr/local/texlive/2022/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty +Package: bigintcalc 2019/12/15 v1.5 Expandable calculations on big integers (HO +) +)) +\Fld@menulength=\count276 +\Field@Width=\dimen152 +\Fld@charsize=\dimen153 +Package hyperref Info: Hyper figures OFF on input line 6027. +Package hyperref Info: Link nesting OFF on input line 6032. +Package hyperref Info: Hyper index ON on input line 6035. +Package hyperref Info: backreferencing OFF on input line 6042. +Package hyperref Info: Link coloring OFF on input line 6047. +Package hyperref Info: Link coloring with OCG OFF on input line 6052. +Package hyperref Info: PDF/A mode OFF on input line 6057. +LaTeX Info: Redefining \ref on input line 6097. +LaTeX Info: Redefining \pageref on input line 6101. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/base/atbegshi-ltx.sty +Package: atbegshi-ltx 2021/01/10 v1.0c Emulation of the original atbegshi +package with kernel methods +) +\Hy@abspage=\count277 +\c@Item=\count278 +\c@Hfootnote=\count279 +) +Package hyperref Info: Driver (autodetected): hpdftex. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/hyperref/hpdftex.def +File: hpdftex.def 2022-02-21 v7.00n Hyperref driver for pdfTeX + +(/usr/local/texlive/2022/texmf-dist/tex/latex/base/atveryend-ltx.sty +Package: atveryend-ltx 2020/08/19 v1.0a Emulation of the original atveryend pac +kage +with kernel methods +) +\Fld@listcount=\count280 +\c@bookmark@seq@number=\count281 + +(/usr/local/texlive/2022/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty +Package: rerunfilecheck 2019/12/05 v1.9 Rerun checks for auxiliary files (HO) + +(/usr/local/texlive/2022/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty +Package: uniquecounter 2019/12/15 v1.4 Provide unlimited unique counter (HO) +) +Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 2 +86. +) +\Hy@SectionHShift=\skip57 +) (/usr/local/texlive/2022/texmf-dist/tex/latex/enumitem/enumitem.sty +Package: enumitem 2019/06/20 v3.9 Customized lists +\labelindent=\skip58 +\enit@outerparindent=\dimen154 +\enit@toks=\toks28 +\enit@inbox=\box53 +\enit@count@id=\count282 +\enitdp@description=\count283 +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphicx.sty +Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) + +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphics.sty +Package: graphics 2021/03/04 v1.4d Standard LaTeX Graphics (DPC,SPQR) + +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/trig.sty +Package: trig 2021/08/11 v1.11 sin cos tan (DPC) +) +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration +) +Package graphics Info: Driver file: pdftex.def on input line 107. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-def/pdftex.def +File: pdftex.def 2020/10/05 v1.2a Graphics/color driver for pdftex +)) +\Gin@req@height=\dimen155 +\Gin@req@width=\dimen156 +) +\c@theorem=\count284 + +(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def +File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX) +\l__color_backend_stack_int=\count285 +\l__pdf_internal_box=\box54 +) +(./paper.aux) +\openout1 = `paper.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Checking defaults for PU/pdf/m/n on input line 60. +LaTeX Font Info: ... okay on input line 60. +LaTeX Font Info: Trying to load font information for U+msa on input line 60. + + (/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd +File: umsa.fd 2013/01/14 v3.01 AMS symbols A +) +LaTeX Font Info: Trying to load font information for U+msb on input line 60. + + +(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsb.fd +File: umsb.fd 2013/01/14 v3.01 AMS symbols B +) +Package hyperref Info: Link coloring OFF on input line 60. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/hyperref/nameref.sty +Package: nameref 2021-04-02 v2.47 Cross-referencing by name of section + +(/usr/local/texlive/2022/texmf-dist/tex/latex/refcount/refcount.sty +Package: refcount 2019/12/15 v3.6 Data extraction from label references (HO) +) +(/usr/local/texlive/2022/texmf-dist/tex/generic/gettitlestring/gettitlestring.s +ty +Package: gettitlestring 2019/12/15 v1.6 Cleanup title references (HO) +) +\c@section@level=\count286 +) +LaTeX Info: Redefining \ref on input line 60. +LaTeX Info: Redefining \pageref on input line 60. +LaTeX Info: Redefining \nameref on input line 60. + (./paper.out) (./paper.out) +\@outlinefile=\write3 +\openout3 = `paper.out'. + + +(/usr/local/texlive/2022/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +[Loading MPS to PDF converter (version 2006.09.02).] +\scratchcounter=\count287 +\scratchdimen=\dimen157 +\scratchbox=\box55 +\nofMPsegments=\count288 +\nofMParguments=\count289 +\everyMPshowfont=\toks29 +\MPscratchCnt=\count290 +\MPscratchDim=\dimen158 +\MPnumerator=\count291 +\makeMPintoPDFobject=\count292 +\everyMPtoPDFconversion=\toks30 +) (/usr/local/texlive/2022/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty +Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf +Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 +85. + +(/usr/local/texlive/2022/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv +e +)) + +File: fig_levels.png Graphic file (type png) + +Package pdftex.def Info: fig_levels.png used on input line 108. +(pdftex.def) Requested size: 198.0011pt x 170.59666pt. + +File: fig_level_cycle.png Graphic file (type png) + +Package pdftex.def Info: fig_level_cycle.png used on input line 122. +(pdftex.def) Requested size: 198.0011pt x 171.40878pt. + + +LaTeX Warning: `h' float specifier changed to `ht'. + +[1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map} <./fig +_levels.png>] + +File: fig_edge_switch.png Graphic file (type png) + +Package pdftex.def Info: fig_edge_switch.png used on input line 141. +(pdftex.def) Requested size: 341.9989pt x 150.51671pt. + +File: fig_parity_subgraph.png Graphic file (type png) + +Package pdftex.def Info: fig_parity_subgraph.png used on input line 159. +(pdftex.def) Requested size: 360.0pt x 106.9477pt. + + +LaTeX Warning: `h' float specifier changed to `ht'. + +[2 <./fig_level_cycle.png> <./fig_edge_switch.png>] [3 <./fig_parity_subgraph.p +ng>] [4] (./paper.aux) +Package rerunfilecheck Info: File `paper.out' has not changed. +(rerunfilecheck) Checksum: DC9FD452C972FF1938BE7060890FAD7C;765. + ) +Here is how much of TeX's memory you used: + 9727 strings out of 478268 + 150592 string characters out of 5846347 + 450778 words of memory out of 5000000 + 27640 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 + +< +/usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb> +Output written on paper.pdf (4 pages, 530157 bytes). +PDF statistics: + 145 PDF objects out of 1000 (max. 8388607) + 103 compressed objects within 2 object streams + 25 named destinations out of 1000 (max. 500000) + 61 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/papers/even_level_graph_generators/paper.out b/papers/even_level_graph_generators/paper.out new file mode 100644 index 0000000..3b71809 --- /dev/null +++ b/papers/even_level_graph_generators/paper.out @@ -0,0 +1,5 @@ +\BOOKMARK [1][-]{section.1}{\376\377\0001\000.\000\040\000I\000n\000t\000r\000o\000d\000u\000c\000t\000i\000o\000n}{}% 1 +\BOOKMARK [1][-]{section.2}{\376\377\0002\000.\000\040\000D\000e\000f\000i\000n\000i\000t\000i\000o\000n\000s}{}% 2 +\BOOKMARK [1][-]{section.3}{\376\377\0003\000.\000\040\000O\000u\000t\000e\000r\000p\000l\000a\000n\000a\000r\000i\000t\000y\000\040\000o\000f\000\040\000l\000e\000v\000e\000l\000\040\000c\000o\000m\000p\000o\000n\000e\000n\000t\000s}{}% 3 +\BOOKMARK [1][-]{section.4}{\376\377\0004\000.\000\040\000E\000v\000e\000n\000\040\000L\000e\000v\000e\000l\000\040\000G\000r\000a\000p\000h\000s}{}% 4 +\BOOKMARK [2][-]{section*.1}{\376\377\000E\000m\000p\000i\000r\000i\000c\000a\000l\000\040\000s\000t\000a\000t\000u\000s}{section.4}% 5 diff --git a/papers/even_level_graph_generators/paper.pdf b/papers/even_level_graph_generators/paper.pdf new file mode 100644 index 0000000..5c22f93 Binary files /dev/null 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 new file mode 100644 index 0000000..d9c17c4 --- /dev/null +++ b/papers/even_level_graph_generators/paper.tex @@ -0,0 +1,286 @@ +%% filename: amsart-template.tex +%% version: 1.1 +%% date: 2014/07/24 +%% +%% American Mathematical Society +%% Technical Support +%% Publications Technical Group +%% 201 Charles Street +%% Providence, RI 02904 +%% USA +%% tel: (401) 455-4080 +%% (800) 321-4267 (USA and Canada only) +%% fax: (401) 331-3842 +%% email: tech-support@ams.org +%% +%% Copyright 2008-2010, 2014 American Mathematical Society. +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3c +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.3c or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% +%% The Current Maintainer of this work is the American Mathematical +%% Society. +%% +%% ==================================================================== + +% AMS-LaTeX v.2 template for use with amsart +% +% Remove any commented or uncommented macros you do not use. + +\documentclass{amsart} + +\usepackage{hyperref} +\usepackage{enumitem} +\usepackage{graphicx} + +\newtheorem{theorem}{Theorem}[section] +\newtheorem{lemma}[theorem]{Lemma} +\newtheorem{proposition}[theorem]{Proposition} + +\theoremstyle{definition} +\newtheorem{definition}[theorem]{Definition} +\newtheorem{example}[theorem]{Example} +\newtheorem{xca}[theorem]{Exercise} +\newtheorem{conjecture}[theorem]{Conjecture} +\newtheorem{question}[theorem]{Question} +\newtheorem{observation}[theorem]{Observation} + +\theoremstyle{remark} +\newtheorem{remark}[theorem]{Remark} + +\numberwithin{equation}{section} + +\begin{document} + +\title{Even Level Graph Generators} + +% Remove any unused author tags. + +% author one information +\author{Eric Bauerfeld} +\address{} +\curraddr{} +\email{} +\thanks{} + + +\subjclass[2010]{Primary } + +\keywords{} + +\date{} + +\dedicatory{} + +\begin{abstract} +\end{abstract} + +\maketitle + +\section{Introduction} + +\section{Definitions} + +Throughout, $G = (V, E)$ is a plane maximal planar graph (a triangulation) +with a fixed planar embedding $\Pi_G$. We write $|V| = n$, so $|E| = 3n - 6$ +and $G$ has $2n - 4$ triangular faces. + +\begin{definition}[Level source] +A \emph{level source} of $G$ is any vertex $v \in V$; we write +$S = \{v\}$ for the level-0 source. +\end{definition} + +\begin{definition}[Levels] +Given a level source $S \subseteq V$, the \emph{level} of $v \in V$ is +$\ell_G(v) = \mathrm{dist}_G(v, S)$, the graph distance from $v$ to the nearest +source vertex. +\end{definition} + +\begin{figure}[h] +\centering +\includegraphics[width=0.55\textwidth]{fig_levels.png} +\caption{BFS levels from the degree-$3$ vertex source $S = \{4\}$. +The source is level $0$, its three neighbours are level $1$, and the +remaining vertices are level $2$. Colour encodes the level.} +\label{fig:levels} +\end{figure} + +\begin{definition}[Level cycle] +A \emph{level cycle} of $G$ (with respect to a level source $S$) is a +simple cycle in $G$ all of whose vertices have the same level. +\end{definition} + +\begin{figure}[h] +\centering +\includegraphics[width=0.55\textwidth]{fig_level_cycle.png} +\caption{A level cycle in the triangulation of Figure~\ref{fig:levels}. +The triangle $1\!-\!2\!-\!3$ is a simple cycle whose three vertices all +lie at level $1$, so it is a level cycle at level $1$.} +\label{fig:level-cycle} +\end{figure} + +\begin{definition}[Edge switch] +\label{def:edge-switch} +Let $G$ be a triangulation with level source $S$, and let $e = uv$ be an +edge of a level cycle of $G$. The \emph{edge switch} at $e$ is the edge +flip on $e$: writing $uvw$ and $uvx$ for the two triangular faces of $G$ +containing $e$, the edge $uv$ is removed and the edge $wx$ is added. As +with any edge flip, the result is a triangulation on the same vertex set +provided $w$ and $x$ are non-adjacent in $G$. +\end{definition} + +\begin{figure}[h] +\centering +\includegraphics[width=0.95\textwidth]{fig_edge_switch.png} +\caption{An edge switch on the level cycle of +Figure~\ref{fig:level-cycle}. The chosen cycle edge $1\!-\!2$ is shared +by the triangular faces $(0,1,2)$ and $(1,2,4)$; the switch deletes +$1\!-\!2$ (red, left) and inserts $0\!-\!4$ (green, right). Vertex +colours indicate the original levels in $G$.} +\label{fig:edge-switch} +\end{figure} + +\begin{definition}[Parity subgraph] +Let $G$ be a triangulation with level source $S$, and let $G'$ be a triangulation +on the same vertex set as $G$. The \emph{even parity subgraph} $E_{G,S}(G')$ is +the subgraph of $G'$ induced by $\{v \in V : \ell_G(v) \equiv 0 \pmod 2\}$. The +\emph{odd parity subgraph} is defined analogously for odd $\ell_G$. +\end{definition} + +\begin{figure}[h] +\centering +\includegraphics[width=\textwidth]{fig_parity_subgraph.png} +\caption{Parity subgraphs of $G' = T$ with respect to the level structure of +Figure~\ref{fig:levels} (here we take $G = G' = T$). Left: $T$ with vertices +coloured by $\ell_G \bmod 2$ (blue $=$ even, orange $=$ odd). Middle: the +even parity subgraph $E_{G,S}(G')$, induced on $\{0, 4, 5, 6\}$; only +edges with both endpoints even appear. Right: the odd parity subgraph +$O_{G,S}(G')$, induced on $\{1, 2, 3\}$; the highlighted triangle shows +that $O_{G,S}(G')$ is not bipartite for this choice of $G'$.} +\label{fig:parity-subgraph} +\end{figure} + +\section{Outerplanarity of level components} +\label{sec:outerplanar-components} + +For each integer $k \geq 0$ and each $(G, S)$, write $L_k$ for the +subgraph of $G$ induced by the level-$k$ vertices. A \emph{level +component} of $G$ (with respect to $S$) is a connected component of +some $L_k$. + +\begin{theorem} +\label{thm:outerplanar-component} +For every plane triangulation $G$ and every level source $S$ of $G$, +every level component of $G$ is outerplanar. +\end{theorem} + +\begin{proof} +Since every subgraph of an outerplanar graph is outerplanar, it suffices +to show that each level subgraph $L_k$ is outerplanar. For $k = 0$, +$L_0 = S$ is a single vertex and is trivially outerplanar. + +Fix $k \geq 1$ and let $D_k$ be the drawing of $L_k$ inherited from +$\Pi_G$. Let $F^\ast$ be the face of $D_k$ containing the source. +Suppose for contradiction that some $u \in L_k$ does not lie on +$\partial F^\ast$, so $u$ lies on the boundary of some other face of +$D_k$. Take any path $P$ in $G$ from $v_0 \in S$ to $u$. As a curve in +$\Pi_G$, $P$ starts in $F^\ast$ and ends at a point off $\partial +F^\ast$, so it must transition from $F^\ast$ to a different face of +$D_k$; in a planar embedding this can happen only at a vertex of +$D_k$, that is, at a level-$k$ vertex $w$ on $P$. Either $w \neq u$ +(so $P$ has length $\geq \mathrm{dist}_G(S, w) + 1 \geq k + 1$), or +$w = u$ (contradicting $u \notin \partial F^\ast$). Since every +$S$-to-$u$ path has length $\geq k + 1$, $\mathrm{dist}_G(S, u) \geq +k + 1$, contradicting $u \in L_k$. +\end{proof} + +\section{Even Level Graphs} +\label{sec:even-level-graphs} + +\begin{definition}[Even Level Graph] +\label{def:even-level-graph} +A plane triangulation $G$ with level source $S$ is an \emph{Even Level +Graph} if every level cycle of $G$ has even length. +\end{definition} + +\begin{theorem} +\label{thm:even-level-4colorable} +Every Even Level Graph is $4$-colorable. +\end{theorem} + +\begin{proof} +Since adjacent vertices in $G$ have levels differing by at most $1$, +any edge between two same-parity endpoints in fact connects two +vertices at the same level. Hence +\[ + E_{G,S}(G) \;=\; \bigsqcup_{i \geq 0} L_{2i}, + \qquad + O_{G,S}(G) \;=\; \bigsqcup_{i \geq 0} L_{2i+1}, +\] +and each $L_k$ is bipartite because its cycles are level cycles of +$G$, which have even length by hypothesis. Choose a $2$-coloring of +$E_{G,S}(G)$ in $\{\text{red}, \text{blue}\}$ and a $2$-coloring of +$O_{G,S}(G)$ in $\{\text{yellow}, \text{green}\}$. Same-parity edges +of $G$ are properly colored by the respective bipartition; +opposite-parity edges connect $\{\text{red}, \text{blue}\}$ to +$\{\text{yellow}, \text{green}\}$. The combined assignment is a +proper $4$-coloring of $G$. +\end{proof} + +\begin{definition}[Derived level graph] +\label{def:derived-level-graph} +Let $G$ be an Even Level Graph with level source $S$, and let $E$ and +$O$ denote the edge sets of the even and odd parity subgraphs +$E_{G,S}(G)$ and $O_{G,S}(G)$. A \emph{derived level graph} of $G$ is +a triangulation $G'$ on the same vertex set as $G$ obtained by a +sequence of edge switches (Definition~\ref{def:edge-switch}), each +acting on an edge of $E$ or of $O$. We do not update $E$ or $O$ to +reflect the level structure of intermediate triangulations: throughout +the sequence, an edge is classified as belonging to $E$ (resp.\ $O$) if +and only if both of its endpoints have even (resp.\ odd) level in $G$. + +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}[Intertwining tree] +\label{def:intertwining-tree} +A maximal planar graph $G$ is an \emph{intertwining tree} if its +vertex set can be partitioned into two sets $A$ and $B$ such that +both induced subgraphs $G[A]$ and $G[B]$ are trees. +\end{definition} + +\begin{conjecture} +\label{conj:every-triangulation-derived} +Every maximal planar graph is a valid derived level graph of some Even +Level Graph, an intertwining tree, or both. +\end{conjecture} + +\subsection*{Empirical status} + +For each isomorphism class of maximal planar graphs on $n$ vertices, +we ask whether (i) some isomorphic representative is reachable from +some Even Level Graph via $E/O$-edge switches (``derived''), and/or +(ii) it is an intertwining tree. The conjecture holds for the class +iff at least one of (i), (ii) holds. + +\begin{center} +\begin{tabular}{rcccccc} +$n$ & \# iso & derived only & inter.\ only & both & missing & status \\\hline +$6$ & $2$ & $0$ & $0$ & $2$ & $0$ & holds \\ +$7$ & $5$ & $0$ & $0$ & $5$ & $0$ & holds \\ +$8$ & $14$ & $0$ & $0$ & $14$ & $0$ & holds \\ +$9$ & $50$ & $0$ & $1$ & $49$ & $0$ & holds \\ +$10$ & $233$ & $0$ & $0$ & $233$ & $0$ & holds \\ +$11$ & $1249$ & $0$ & $0$ & $1249$ & $0$ & holds \\ +\end{tabular} +\end{center} + +\end{document} diff --git a/papers/level_switching/experiments/leaf_distance_algorithm.py b/papers/level_switching/experiments/leaf_distance_algorithm.py new file mode 100644 index 0000000..3748fbe --- /dev/null +++ b/papers/level_switching/experiments/leaf_distance_algorithm.py @@ -0,0 +1,151 @@ +"""User-proposed simpler algorithm: + +1) Assign each face x = min dual-tree distance to any leaf face (ear). +2) When face F at depth d has no balanced surface switch, edge-switch + on the edge between F and F's neighbour with the smallest x. +3) Repeat until the number of depth-d faces has strictly decreased. +""" +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import math +import networkx as nx +from stress_test_termination import ( + compute_depths, apply_switch, check_balanced, face_edges +) + + +def compute_x(faces, outer_set): + """x(F) = min dual-tree distance from F to any leaf face.""" + D = nx.Graph() + D.add_nodes_from(range(len(faces))) + for i, fi in enumerate(faces): + for j, fj in enumerate(faces): + if i < j and face_edges(fi) & face_edges(fj): + D.add_edge(i, j) + leaves = [i for i in D.nodes if D.degree(i) == 1] + x = {} + for i in range(len(faces)): + if not leaves: + x[i] = float('inf') + continue + x[i] = min(nx.shortest_path_length(D, i, lf) for lf in leaves) + return x, D + + +def neighbour_at_edge(faces, F_idx, e): + for j, fj in enumerate(faces): + if j != F_idx and e in face_edges(fj): + return j + return None + + +def run_leaf_distance_algorithm(faces, outer_set, max_steps=500, + verbose=True): + """Run the algorithm. If F admits a balanced switch, take it; else + pick the neighbour with smallest x and switch on that edge.""" + total = 0 + log = [] + for step in range(max_steps): + depth = compute_depths(faces, outer_set) + d_max = max(depth.values()) + if d_max == 0: + log.append(f'terminated at step {step}') + return faces, total, log + + max_d_faces = [i for i, d in depth.items() if d == d_max] + F_idx = max_d_faces[0] + F = faces[F_idx] + + ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set) + if ok: + Fp = faces[fp_idx] + u, v = tuple(e) + w = [vert for vert in F if vert not in (u, v)][0] + x = [vert for vert in Fp if vert not in (u, v)][0] + log.append(f'step {step}: BALANCED on edge ({u},{v}) ' + f'with F\' = {Fp}') + faces = apply_switch(faces, (u, v), (w, x)) + total += 1 + continue + + # Find x values + x_vals, D = compute_x(faces, outer_set) + # Among neighbours of F (interior edges, hence in dual graph), + # pick the one with smallest x. + nb_choices = [] + for e_test in face_edges(F): + if e_test in outer_set: + continue + nb_idx = neighbour_at_edge(faces, F_idx, e_test) + if nb_idx is None: + continue + nb_choices.append((x_vals[nb_idx], e_test, nb_idx)) + if not nb_choices: + log.append(f'step {step}: no interior neighbour; stuck') + break + nb_choices.sort() + x_min, e_chosen, nb_idx = nb_choices[0] + Fnb = faces[nb_idx] + u, v = tuple(e_chosen) + w = [vert for vert in F if vert not in (u, v)][0] + xv = [vert for vert in Fnb if vert not in (u, v)][0] + log.append(f'step {step}: x-preprocess on edge ({u},{v}) ' + f'(F\'={Fnb}, x={x_min}; other choices: ' + f'{[(c[0]) for c in nb_choices[1:]]})') + faces = apply_switch(faces, (u, v), (w, xv)) + total += 1 + + log.append(f'hit max_steps; final max depth = ' + f'{max(compute_depths(faces, outer_set).values())}') + return faces, total, log + + +# ----- TEST CASE 1: 9-vertex example ----- +n9 = 9 +outer9 = [(i, (i + 1) % n9) for i in range(n9)] +outer_set9 = {frozenset(e) for e in outer9} +faces9 = [ + (0, 1, 2), (0, 2, 3), (3, 4, 5), (3, 5, 6), + (6, 7, 8), (6, 8, 0), (0, 3, 6), +] +print('=== 9-vertex example ===') +_, total, log = run_leaf_distance_algorithm(faces9, outer_set9) +print('\n'.join(log)) +print(f'total switches: {total}\n') + + +# ----- TEST CASE 2: 24-vertex doubly-lopsided example ----- +n24 = 24 +outer24 = [(i, (i + 1) % n24) for i in range(n24)] +outer_set24 = {frozenset(e) for e in outer24} + +def arm(a, b): + return [ + (a, a + 1, a + 2), (a, a + 2, b), (a + 2, a + 3, a + 4), + (a + 2, a + 4, b), (a + 4, a + 5, a + 6), (a + 4, a + 6, b), + (a + 6, a + 7, b), + ] +faces24 = [(0, 8, 16)] +for (a, b) in [(0, 8), (8, 16), (16, 24)]: + fs = arm(a, b) + fs = [tuple(0 if v == 24 else v for v in vt) for vt in fs] + faces24.extend(fs) +print('=== 24-vertex doubly-lopsided example ===') +_, total, log = run_leaf_distance_algorithm(faces24, outer_set24) +print('\n'.join(log[:25])) +if len(log) > 25: + print(f'... ({len(log) - 25} more lines)') +print(f'total switches: {total}\n') + + +# ----- TEST CASE 3: random outerplanar n=40 (one of the previously-slow seeds) ----- +from stress_test_termination import random_triangulation +seed = 94476710 +outer, _, faces = random_triangulation(40, seed=seed) +outer_set = {frozenset(e) for e in outer} +print(f'=== Random n=40 (seed={seed}) with the new algorithm ===') +_, total, log = run_leaf_distance_algorithm(faces, outer_set, max_steps=1000) +print(f'total switches: {total} (random algorithm with cap=10000 needed 863)') +print(f'first 5 lines: {log[:5]}') +print(f'last 2 lines: {log[-2:]}') diff --git a/papers/level_switching/experiments/leaf_distance_blocked.py b/papers/level_switching/experiments/leaf_distance_blocked.py new file mode 100644 index 0000000..983b763 --- /dev/null +++ b/papers/level_switching/experiments/leaf_distance_blocked.py @@ -0,0 +1,140 @@ +"""Leaf-distance algorithm with the additional rule: + +Maintain a `blocked` set of edges; an edge cannot be flipped while +the count x of depth-d faces (d = current max depth) is unchanged. +When x decreases (a depth-d face is removed via a balanced switch), +clear `blocked`. Also clear `blocked` when d itself decreases.""" +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import matplotlib.pyplot as plt +from matplotlib.patches import Polygon +from matplotlib.backends.backend_pdf import PdfPages +import math +from leaf_distance_algorithm import compute_x +from stress_test_termination import ( + compute_depths, apply_switch, check_balanced, face_edges, + random_triangulation +) + +OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) + + +def neighbour_at_edge(faces, F_idx, e): + for j, fj in enumerate(faces): + if j != F_idx and e in face_edges(fj): + return j + return None + + +def run_with_blocking(faces, outer_set, max_steps=10000): + """Run with the blocked-edge rule.""" + log = [] + blocked = set() + prev_x = None + prev_d = None + for step in range(max_steps): + depth = compute_depths(faces, outer_set) + d_max = max(depth.values()) + n_at_max = sum(1 for d in depth.values() if d == d_max) + if prev_d is not None and (d_max < prev_d or + (d_max == prev_d and n_at_max < prev_x)): + blocked = set() # reset: count of depth-d faces dropped + prev_d = d_max + prev_x = n_at_max + + if d_max == 0: + log.append(f'TERMINATED at step {step}') + return faces, log + + max_d_faces = [i for i, d in depth.items() if d == d_max] + F_idx = max_d_faces[0] + F = faces[F_idx] + ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set) + if ok: + Fp = faces[fp_idx] + u, v = tuple(e) + w = [vert for vert in F if vert not in (u, v)][0] + xv = [vert for vert in Fp if vert not in (u, v)][0] + log.append(f'step {step}: BALANCED on ({u},{v}) (d={d_max},x={n_at_max})') + blocked.add(frozenset((u, v))) + faces = apply_switch(faces, (u, v), (w, xv)) + continue + + # Preprocessing: pick lowest-x unblocked neighbour + x_vals, _ = compute_x(faces, outer_set) + nb_choices = [] + for e_test in face_edges(F): + if e_test in outer_set or e_test in blocked: + continue + nb_idx = neighbour_at_edge(faces, F_idx, e_test) + if nb_idx is None: + continue + nb_choices.append((x_vals[nb_idx], e_test, nb_idx)) + if not nb_choices: + log.append(f'step {step}: STUCK -- all interior edges blocked ' + f'or none available; max depth={d_max}, x={n_at_max}') + return faces, log + nb_choices.sort() + _, e_chosen, fp_idx = nb_choices[0] + Fp = faces[fp_idx] + u, v = tuple(e_chosen) + w = [vert for vert in F if vert not in (u, v)][0] + xv = [vert for vert in Fp if vert not in (u, v)][0] + log.append(f'step {step}: preprocess ({u},{v}) (d={d_max},x={n_at_max}, ' + f'#blocked={len(blocked)})') + blocked.add(frozenset((u, v))) + faces = apply_switch(faces, (u, v), (w, xv)) + + log.append(f'hit max_steps') + return faces, log + + +# ----- run on the stuck n=40 case ----- +seed = 94476710 +outer, _, faces = random_triangulation(40, seed=seed) +outer_set = {frozenset(e) for e in outer} + +print(f'Running blocked algorithm on n=40, seed={seed}...') +faces_after, log = run_with_blocking(faces, outer_set, max_steps=5000) +print(f'Result: {log[-1]}') +print(f'Total log lines: {len(log)}') +print('First 25 lines:') +for line in log[:25]: + print(f' {line}') +print('Last 5 lines:') +for line in log[-5:]: + print(f' {line}') + +# ----- also test on 9-vertex and 24-vertex ----- +print('\n=== 9-vertex ===') +n9 = 9 +outer9 = [(i, (i+1) % n9) for i in range(n9)] +outer_set9 = {frozenset(e) for e in outer9} +faces9 = [ + (0, 1, 2), (0, 2, 3), (3, 4, 5), (3, 5, 6), + (6, 7, 8), (6, 8, 0), (0, 3, 6), +] +_, log9 = run_with_blocking(faces9, outer_set9) +for line in log9[:5]: + print(f' {line}') + +print('\n=== 24-vertex doubly-lopsided ===') +n24 = 24 +outer24 = [(i, (i+1) % n24) for i in range(n24)] +outer_set24 = {frozenset(e) for e in outer24} + +def arm(a, b): + return [ + (a, a+1, a+2), (a, a+2, b), (a+2, a+3, a+4), + (a+2, a+4, b), (a+4, a+5, a+6), (a+4, a+6, b), + (a+6, a+7, b), + ] +faces24 = [(0, 8, 16)] +for (a, b) in [(0, 8), (8, 16), (16, 24)]: + fs = arm(a, b) + fs = [tuple(0 if v == 24 else v for v in vt) for vt in fs] + faces24.extend(fs) +_, log24 = run_with_blocking(faces24, outer_set24) +print(f' total: {len(log24) - 1} steps') +print(f' final: {log24[-1]}') diff --git a/papers/level_switching/experiments/plot_n40_every_step.py b/papers/level_switching/experiments/plot_n40_every_step.py new file mode 100644 index 0000000..6b99f95 --- /dev/null +++ b/papers/level_switching/experiments/plot_n40_every_step.py @@ -0,0 +1,140 @@ +"""Generate a multi-page PDF showing the L_k state after each edge +switch, on the n=40 stuck example.""" +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import math +import matplotlib.pyplot as plt +from matplotlib.patches import Polygon +from matplotlib.backends.backend_pdf import PdfPages +from leaf_distance_algorithm import compute_x +from stress_test_termination import ( + compute_depths, apply_switch, check_balanced, face_edges, + random_triangulation +) + +OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) + + +def neighbour_at_edge(faces, F_idx, e): + for j, fj in enumerate(faces): + if j != F_idx and e in face_edges(fj): + return j + return None + + +def positions(n): + return {i: (math.cos(math.radians(90 - i * 360 / n)), + math.sin(math.radians(90 - i * 360 / n))) + for i in range(n)} + + +def draw_state(ax, faces, n, outer_set, switched_edge=None, + action=None, step=None): + POS = positions(n) + depth = compute_depths(faces, outer_set) + palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'} + edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'} + for f in faces: + d = depth.get(faces.index(f), 0) + # Find depth by iterating + for i, ff in enumerate(faces): + if ff == f: + d = depth[i] + break + poly = Polygon([POS[v] for v in f], closed=True, + facecolor=palette.get(d, '#ddd'), + edgecolor=edge_pal.get(d, '#333'), + linewidth=0.7, alpha=0.6, zorder=0) + ax.add_patch(poly) + # Draw all edges + all_edges = set() + for f in faces: + all_edges.update(face_edges(f)) + outer_edges = [tuple(e) for e in all_edges if e in outer_set] + chord_edges = [tuple(e) for e in all_edges if e not in outer_set] + for (a, b) in outer_edges + chord_edges: + color = '#333'; lw = 0.5 + if switched_edge and {a, b} == set(switched_edge): + color = '#dc2626' if action == 'preprocess' else '#16a34a' + lw = 2.5 + ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]], + color=color, linewidth=lw, zorder=1) + # Vertices + for i, (x, y) in POS.items(): + ax.scatter([x], [y], s=70, c='#1f2937', edgecolors='black', + linewidths=0.3, zorder=2) + ax.text(x, y, str(i), ha='center', va='center', + fontsize=5, color='white', fontweight='bold', zorder=3) + ax.set_aspect('equal'); ax.axis('off') + ax.set_xlim(-1.2, 1.2); ax.set_ylim(-1.2, 1.2) + d_max = max(depth.values()) + n_d1 = sum(1 for d in depth.values() if d == 1) + n_d2 = sum(1 for d in depth.values() if d == 2) + title = f'step {step}' + if action and switched_edge: + title += f': {action} on {tuple(sorted(switched_edge))}' + title += f' (max={d_max}, #d1={n_d1}, #d2={n_d2})' + ax.set_title(title, fontsize=9) + + +def run_and_record(faces, outer_set, n, max_steps=80): + """Run algorithm, capturing the state and the action at each step. + Returns list of (faces_at_start_of_step, action, edge).""" + records = [] + for step in range(max_steps): + depth = compute_depths(faces, outer_set) + d_max = max(depth.values()) + if d_max == 0: + records.append((list(faces), None, None)) + return records, faces + + max_d_faces = [i for i, d in depth.items() if d == d_max] + F_idx = max_d_faces[0] + F = faces[F_idx] + ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set) + if ok: + action = 'BALANCED' + else: + x_vals, _ = compute_x(faces, outer_set) + nb_choices = [] + for e_test in face_edges(F): + if e_test in outer_set: + continue + nb_idx = neighbour_at_edge(faces, F_idx, e_test) + if nb_idx is None: + continue + nb_choices.append((x_vals[nb_idx], e_test, nb_idx)) + if not nb_choices: + records.append((list(faces), 'STUCK', None)) + return records, faces + nb_choices.sort() + _, e, fp_idx = nb_choices[0] + action = 'preprocess' + + Fp = faces[fp_idx] + u, v = tuple(e) + w = [vert for vert in F if vert not in (u, v)][0] + x = [vert for vert in Fp if vert not in (u, v)][0] + records.append((list(faces), action, (u, v))) + faces = apply_switch(faces, (u, v), (w, x)) + return records, faces + + +seed = 94476710 +outer, _, faces = random_triangulation(40, seed=seed) +outer_set = {frozenset(e) for e in outer} + +print('recording trajectory...') +records, _ = run_and_record(faces, outer_set, 40, max_steps=80) +print(f'recorded {len(records)} steps') + +out_pdf = os.path.join(OUT_DIR, 'fig_n40_every_step.pdf') +with PdfPages(out_pdf) as pdf: + for step, (state_faces, action, edge) in enumerate(records): + fig, ax = plt.subplots(figsize=(7, 7)) + draw_state(ax, state_faces, 40, outer_set, + switched_edge=edge, action=action, step=step) + pdf.savefig(fig, dpi=120, bbox_inches='tight') + plt.close(fig) +print(f'wrote {out_pdf}') diff --git a/papers/level_switching/experiments/plot_n40_trajectory.py b/papers/level_switching/experiments/plot_n40_trajectory.py new file mode 100644 index 0000000..f20bd55 --- /dev/null +++ b/papers/level_switching/experiments/plot_n40_trajectory.py @@ -0,0 +1,122 @@ +"""Plot the depth distribution over time for the random n=40 trajectory +under the leaf-distance algorithm. Shows whether progress is being made +or whether the algorithm is just grinding.""" +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import matplotlib.pyplot as plt +from leaf_distance_algorithm import compute_x +from stress_test_termination import ( + compute_depths, apply_switch, check_balanced, face_edges, + random_triangulation +) + +OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) + + +def neighbour_at_edge(faces, F_idx, e): + for j, fj in enumerate(faces): + if j != F_idx and e in face_edges(fj): + return j + return None + + +def run_with_full_tracking(faces, outer_set, max_steps=3000): + """Run algorithm; record at each step: (#faces at each depth), and + whether the step was BALANCED or x-preprocess.""" + history = [] + for step in range(max_steps): + depth = compute_depths(faces, outer_set) + d_max = max(depth.values()) + # Record current state + d_dist = {} + for d in depth.values(): + d_dist[d] = d_dist.get(d, 0) + 1 + history.append({'step': step, 'd_max': d_max, 'd_dist': dict(d_dist)}) + if d_max == 0: + return history, faces + + max_d_faces = [i for i, d in depth.items() if d == d_max] + F_idx = max_d_faces[0] + F = faces[F_idx] + ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set) + if ok: + history[-1]['action'] = 'BALANCED' + Fp = faces[fp_idx] + u, v = tuple(e) + w = [vert for vert in F if vert not in (u, v)][0] + x = [vert for vert in Fp if vert not in (u, v)][0] + faces = apply_switch(faces, (u, v), (w, x)) + continue + history[-1]['action'] = 'preprocess' + x_vals, _ = compute_x(faces, outer_set) + nb_choices = [] + for e_test in face_edges(F): + if e_test in outer_set: + continue + nb_idx = neighbour_at_edge(faces, F_idx, e_test) + if nb_idx is None: + continue + nb_choices.append((x_vals[nb_idx], e_test, nb_idx)) + if not nb_choices: + return history, faces + nb_choices.sort() + _, e_chosen, nb_idx = nb_choices[0] + Fnb = faces[nb_idx] + u, v = tuple(e_chosen) + w = [vert for vert in F if vert not in (u, v)][0] + xv = [vert for vert in Fnb if vert not in (u, v)][0] + faces = apply_switch(faces, (u, v), (w, xv)) + return history, faces + + +seed = 94476710 +outer, _, faces = random_triangulation(40, seed=seed) +outer_set = {frozenset(e) for e in outer} +print(f'Running algorithm on n=40, seed={seed}...') +history, final_faces = run_with_full_tracking(faces, outer_set, max_steps=3000) +print(f'finished at step {len(history) - 1}, ' + f'final max depth = {history[-1]["d_max"]}') + +# Plot count of depth-d faces for d = 1 and 2 over time, and mark +# balanced vs preprocessing steps +fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 7), sharex=True) + +steps = [h['step'] for h in history] +n_d1 = [h['d_dist'].get(1, 0) for h in history] +n_d2 = [h['d_dist'].get(2, 0) for h in history] +balanced_steps = [h['step'] for h in history + if h.get('action') == 'BALANCED'] +preprocess_steps = [h['step'] for h in history + if h.get('action') == 'preprocess'] + +ax1.plot(steps, n_d1, color='#d97706', linewidth=1.2, label='depth-1 faces') +ax1.plot(steps, n_d2, color='#dc2626', linewidth=1.2, label='depth-2 faces') +ax1.set_ylabel('count of faces at depth') +ax1.legend(loc='upper right') +ax1.set_title(f'n=40 trajectory under leaf-distance algorithm (seed={seed}, ' + f'{len(history) - 1} steps)') +ax1.grid(alpha=0.3) + +# Add markers showing balanced vs preprocess at bottom +ax2.scatter(balanced_steps, [1] * len(balanced_steps), + color='#16a34a', s=8, label=f'BALANCED ({len(balanced_steps)} steps)') +ax2.scatter(preprocess_steps, [0] * len(preprocess_steps), + color='#3b82f6', s=8, label=f'preprocess ({len(preprocess_steps)} steps)') +ax2.set_yticks([0, 1]) +ax2.set_yticklabels(['preprocess', 'BALANCED']) +ax2.set_xlabel('step') +ax2.legend(loc='upper right') +ax2.grid(alpha=0.3) + +fig.tight_layout() +out = os.path.join(OUT_DIR, 'fig_n40_trajectory.png') +fig.savefig(out, dpi=150, bbox_inches='tight') +plt.close(fig) +print(f'wrote {out}') + +# Summary +print(f'\n#BALANCED switches: {len(balanced_steps)}') +print(f'#preprocess switches: {len(preprocess_steps)}') +print(f'Max depth-1 count seen: {max(n_d1)}') +print(f'Max depth-2 count seen: {max(n_d2)}') diff --git a/papers/level_switching/experiments/v_c_question_diagram.py b/papers/level_switching/experiments/v_c_question_diagram.py new file mode 100644 index 0000000..552b225 --- /dev/null +++ b/papers/level_switching/experiments/v_c_question_diagram.py @@ -0,0 +1,158 @@ +"""Annotated diagram so the user can answer the v_c-rotation +clarification questions. + +Shows the 9-vertex L_k with e_0 = (0, 3) highlighted, both candidate +v_c vertices (0 and 3) labelled, and the four (v_c, direction) fans +laid out around each.""" +import os +import math +import matplotlib.pyplot as plt +from matplotlib.patches import Polygon, FancyArrowPatch + +OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) + +n = 9 +POS = {i: (math.cos(math.radians(90 - i * 360 / n)), + math.sin(math.radians(90 - i * 360 / n))) for i in range(n)} +OUTER_EDGES = [(i, (i + 1) % n) for i in range(n)] +CHORDS = [(0, 2), (0, 3), (3, 5), (3, 6), (0, 6), (6, 8)] + +# Inner faces +FACES = { + (0, 1, 2): 0, + (0, 2, 3): 0, + (3, 4, 5): 0, + (3, 5, 6): 0, + (6, 7, 8): 0, + (6, 8, 0): 0, + (0, 3, 6): 1, # F +} + +F_idx_face = (0, 3, 6) +Fp_face = (0, 2, 3) +e0 = (0, 3) + + +def cw_order_around(v): + """Sort vertices adjacent to v by clockwise angle (decreasing + angle from v, starting at angle 90).""" + adj = set() + for f in FACES: + if v in f: + for u in f: + if u != v: + adj.add(u) + # angle of each adjacent vertex + def angle(u): + return (90 - u * 360 / n) % 360 + return sorted(adj, key=lambda u: -angle(u)) + + +fig, axes = plt.subplots(1, 2, figsize=(14, 7)) + +palette = {0: '#86efac', 1: '#fde68a'} +edge_pal = {0: '#16a34a', 1: '#d97706'} + + +def draw_base(ax, title): + # Fill faces by depth + for face, depth in FACES.items(): + poly = Polygon([POS[v] for v in face], closed=True, + facecolor=palette[depth], edgecolor=edge_pal[depth], + linewidth=1.2, alpha=0.5, zorder=0) + ax.add_patch(poly) + + # Edges + for (a, b) in OUTER_EDGES + CHORDS: + color, lw = '#333', 1.2 + if {a, b} == set(e0): + color, lw = '#dc2626', 3.4 # e_0 highlighted + ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]], + color=color, linewidth=lw, zorder=1) + + # Vertices + for i, (x, y) in POS.items(): + color = '#3b82f6' if i in e0 else '#1f2937' + size = 470 if i in e0 else 280 + ax.scatter([x], [y], s=size, c=color, edgecolors='black', + linewidths=1.2, zorder=2) + ax.text(x, y, str(i), ha='center', va='center', + fontsize=11 if i in e0 else 9, + color='white', fontweight='bold', zorder=3) + + # Annotate F and F' + cx_F = sum(POS[v][0] for v in F_idx_face) / 3 + cy_F = sum(POS[v][1] for v in F_idx_face) / 3 + ax.text(cx_F, cy_F + 0.1, r'$F$', ha='center', fontsize=14, + color='#92400e', fontweight='bold', zorder=4) + ax.text(cx_F, cy_F - 0.1, '(depth 1)', ha='center', fontsize=9, + color='#92400e', zorder=4) + + cx_Fp = sum(POS[v][0] for v in Fp_face) / 3 + cy_Fp = sum(POS[v][1] for v in Fp_face) / 3 + ax.text(cx_Fp + 0.1, cy_Fp, r"$F'$" + '\n(depth 0)', ha='center', + fontsize=10, color='#16a34a', fontweight='bold', zorder=4) + + # F''s outer-cycle edge: (2, 3) + px, py = POS[2], POS[3] + mid = ((px[0] + py[0]) / 2, (px[1] + py[1]) / 2) + ax.annotate("outer edge of $F'$", + xy=(mid[0] + 0.05, mid[1] - 0.05), + xytext=(1.35, 0.5), + fontsize=9, color='#16a34a', + arrowprops=dict(arrowstyle='->', color='#16a34a', + lw=1.0)) + + ax.set_aspect('equal'); ax.axis('off') + ax.set_xlim(-1.5, 1.7); ax.set_ylim(-1.4, 1.4) + ax.set_title(title, fontsize=12) + + +def annotate_fan(ax, vc, label): + """Draw arrows around v_c showing CW order of edges.""" + cw_neighbours = cw_order_around(vc) + # rotate so the e_0 neighbour comes first + e0_other = [u for u in e0 if u != vc][0] + idx = cw_neighbours.index(e0_other) + cw_neighbours = cw_neighbours[idx:] + cw_neighbours[:idx] + + px, py = POS[vc] + # Draw a partial arc around v_c + for i, u in enumerate(cw_neighbours): + # midpoint between v_c and u, slightly inside + ux, uy = POS[u] + midx = px * 0.7 + ux * 0.3 + midy = py * 0.7 + uy * 0.3 + # is edge on outer cycle? + is_outer = (min(vc, u), max(vc, u)) in OUTER_EDGES or \ + (max(vc, u), min(vc, u)) in OUTER_EDGES + marker_color = '#1d4ed8' if not is_outer else '#dc2626' + marker = 'o' + ax.scatter([midx], [midy], s=120, c=marker_color, marker=marker, + edgecolors='white', linewidths=1.5, zorder=5) + ax.text(midx, midy, str(i + 1), ha='center', va='center', + fontsize=8, color='white', fontweight='bold', zorder=6) + + # Label + ax.text(px + 0.05, py + 0.25, label, ha='center', + fontsize=11, color='#1d4ed8', fontweight='bold') + + +draw_base(axes[0], r'Option A: $v_c = 0$ (CW order: 1=$(0,3)$, 2=$(0,6)$, 3=$(0,8)$ outer ...)') +annotate_fan(axes[0], 0, r'$v_c = 0$') + +draw_base(axes[1], r'Option B: $v_c = 3$ (CW order: 1=$(0,3)$, 2=$(2,3)$ outer ...)') +annotate_fan(axes[1], 3, r'$v_c = 3$') + +# Add legend below +fig.text(0.5, 0.02, + 'Blue circles = chord edges (would be switched). ' + 'Red circles = outer-cycle edges (algorithm stops here). ' + 'Numbers = clockwise order around $v_c$ starting from $e_0$.', + ha='center', fontsize=10) + +fig.tight_layout(rect=[0, 0.04, 1, 1]) +out = os.path.join(OUT_DIR, 'fig_v_c_question.png') +fig.savefig(out, dpi=180, bbox_inches='tight') +plt.close(fig) +print(f'wrote {out}') diff --git a/papers/level_switching/fig_n40_every_step.pdf b/papers/level_switching/fig_n40_every_step.pdf new file mode 100644 index 0000000..90d1c87 Binary files /dev/null and b/papers/level_switching/fig_n40_every_step.pdf differ diff --git a/papers/level_switching/fig_n40_trajectory.png b/papers/level_switching/fig_n40_trajectory.png new file mode 100644 index 0000000..a24d970 Binary files /dev/null and b/papers/level_switching/fig_n40_trajectory.png differ diff --git a/papers/level_switching/fig_v_c_question.png b/papers/level_switching/fig_v_c_question.png new file mode 100644 index 0000000..a0e4209 Binary files /dev/null and b/papers/level_switching/fig_v_c_question.png differ diff --git a/papers/level_switching/paper.aux b/papers/level_switching/paper.aux index 32ae500..dca5eaa 100644 --- a/papers/level_switching/paper.aux +++ b/papers/level_switching/paper.aux @@ -51,10 +51,20 @@ \@writefile{lof}{\contentsline {figure}{\numberline {9}{\ignorespaces Recursive lopsidedness at $d = 2$. Left: $F = (0,8,16)$ depth $2$, every arm doubly-lopsided. Middle: one preprocessing switch $(0,8) \DOTSB \mapstochar \rightarrow (2,16)$ exposes the first lopsided layer; the new depth-$2$ face $(2,8,16)$ still has no balanced switch. Right: a second preprocessing switch $(8,2) \DOTSB \mapstochar \rightarrow (4,16)$ reaches the inner balanced face $K_0 = (4,6,8)$, whose two non-$F$ neighbours are both ears; the depth-$2$ face $(4,8,16)$ now admits a balanced surface switch on edge $(4,8)$.}}{8}{figure.9}\protected@file@percent } \newlabel{fig:d2-recursive}{{9}{8}{Recursive lopsidedness at $d = 2$. Left: $F = (0,8,16)$ depth $2$, every arm doubly-lopsided. Middle: one preprocessing switch $(0,8) \mapsto (2,16)$ exposes the first lopsided layer; the new depth-$2$ face $(2,8,16)$ still has no balanced switch. Right: a second preprocessing switch $(8,2) \mapsto (4,16)$ reaches the inner balanced face $K_0 = (4,6,8)$, whose two non-$F$ neighbours are both ears; the depth-$2$ face $(4,8,16)$ now admits a balanced surface switch on edge $(4,8)$}{figure.9}{}} \@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical termination}}{8}{section*.4}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{What the natural monovariants do not give us}}{9}{section*.5}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Empirical termination on random configurations}}{9}{section*.6}\protected@file@percent } +\newlabel{q:preprocessing-terminates}{{3.6}{9}{}{theorem.3.6}{}} +\@writefile{toc}{\contentsline {section}{\tocsection {}{4}{Reachability via edge switches}}{9}{section.4}\protected@file@percent } +\newlabel{sec:reachability}{{4}{9}{Reachability via edge switches}{section.4}{}} +\citation{sleator-tarjan-thurston} +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Two cases on the layer below $k$}}{10}{section*.7}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Combining}}{10}{section*.8}\protected@file@percent } +\newlabel{thm:reachability}{{4.1}{10}{}{theorem.4.1}{}} +\bibcite{sleator-tarjan-thurston}{1} \newlabel{tocindent-1}{0pt} \newlabel{tocindent0}{14.69437pt} \newlabel{tocindent1}{17.77782pt} \newlabel{tocindent2}{0pt} \newlabel{tocindent3}{0pt} -\newlabel{q:preprocessing-terminates}{{3.6}{9}{}{theorem.3.6}{}} -\gdef \@abspage@last{9} +\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{11}{section*.9}\protected@file@percent } +\gdef \@abspage@last{11} diff --git a/papers/level_switching/paper.log b/papers/level_switching/paper.log index 29d788a..b65ec74 100644 --- a/papers/level_switching/paper.log +++ b/papers/level_switching/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) 20 MAY 2026 23:18 +This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 21 MAY 2026 14:28 entering extended mode restricted \write18 enabled. %&-line parsing enabled. @@ -353,12 +353,12 @@ Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv e )) - + File: fig_level_source.png Graphic file (type png) Package pdftex.def Info: fig_level_source.png used on input line 105. (pdftex.def) Requested size: 306.0022pt x 136.07228pt. - + File: fig_levels.png Graphic file (type png) Package pdftex.def Info: fig_levels.png used on input line 122. @@ -367,7 +367,7 @@ Package pdftex.def Info: fig_levels.png used on input line 122. LaTeX Warning: `h' float specifier changed to `ht'. - + File: fig_level_cycle.png Graphic file (type png) Package pdftex.def Info: fig_level_cycle.png used on input line 136. @@ -377,7 +377,7 @@ LaTeX Warning: `h' float specifier changed to `ht'. [1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map} <./fig _level_source.png>] - + File: fig_edge_switch.png Graphic file (type png) Package pdftex.def Info: fig_edge_switch.png used on input line 155. @@ -386,7 +386,7 @@ Package pdftex.def Info: fig_edge_switch.png used on input line 155. LaTeX Warning: `h' float specifier changed to `ht'. - + File: fig_parity_subgraph.png Graphic file (type png) Package pdftex.def Info: fig_parity_subgraph.png used on input line 173. @@ -395,7 +395,7 @@ Package pdftex.def Info: fig_parity_subgraph.png used on input line 173. LaTeX Warning: `h' float specifier changed to `ht'. [2 <./fig_levels.png> <./fig_level_cycle.png>] - + File: fig_facial_depth.png Graphic file (type png) Package pdftex.def Info: fig_facial_depth.png used on input line 200. @@ -413,7 +413,7 @@ bal-anced-ness gives $[](\OML/cmm/m/it/10 A[]\OT1/cmr/m/n/10 ) = \OML/cmm/m/it/ /m/n/10 ) \OMS/cmsy/m/n/10 ^^T [] - + File: fig_no_balanced_switch.png Graphic file (type png) Package pdftex.def Info: fig_no_balanced_switch.png used on input line 395. @@ -421,7 +421,7 @@ Package pdftex.def Info: fig_no_balanced_switch.png used on input line 395. LaTeX Warning: `h' float specifier changed to `ht'. - + File: fig_preprocessing.png Graphic file (type png) Package pdftex.def Info: fig_preprocessing.png used on input line 426. @@ -462,7 +462,7 @@ cmm/m/it/10 ; \OT1/cmr/m/n/10 14)\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 (17\OML/cmm /m/it/10 ; \OT1/cmr/m/n/10 19\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 0)$ [] - + File: fig_d2_recursive.png Graphic file (type png) Package pdftex.def Info: fig_d2_recursive.png used on input line 477. @@ -472,15 +472,24 @@ Overfull \hbox (44.02832pt too wide) detected at line 496 [][] [] -[8 <./fig_d2_recursive.png>] [9] (./paper.aux) +[8 <./fig_d2_recursive.png>] [9] + +Package hyperref Warning: Token not allowed in a PDF string (Unicode): +(hyperref) removing `math shift' on input line 581. + + +Package hyperref Warning: Token not allowed in a PDF string (Unicode): +(hyperref) removing `math shift' on input line 581. + +[10] [11] (./paper.aux) Package rerunfilecheck Info: File `paper.out' has not changed. -(rerunfilecheck) Checksum: DB53A88C1A1F5BD90EDB1F1E02E41C38;1447. +(rerunfilecheck) Checksum: EB616E34045D97804AC077E984931199;2673. ) Here is how much of TeX's memory you used: - 9791 strings out of 478268 - 151840 string characters out of 5846347 - 458685 words of memory out of 5000000 - 27682 multiletter control sequences out of 15000+600000 + 9811 strings out of 478268 + 152095 string characters out of 5846347 + 458816 words of memory out of 5000000 + 27691 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,9n,76p,781b,448s stack positions out of 10000i,1000n,20000p,200000b,200000s @@ -498,10 +507,10 @@ xlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb> -Output written on paper.pdf (9 pages, 1268281 bytes). +Output written on paper.pdf (11 pages, 1286221 bytes). PDF statistics: - 215 PDF objects out of 1000 (max. 8388607) - 156 compressed objects within 2 object streams - 42 named destinations out of 1000 (max. 500000) - 102 words of extra memory for PDF output out of 10000 (max. 10000000) + 259 PDF objects out of 1000 (max. 8388607) + 198 compressed objects within 2 object streams + 53 named destinations out of 1000 (max. 500000) + 150 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/papers/level_switching/paper.out b/papers/level_switching/paper.out index 62f5e23..fddbeea 100644 --- a/papers/level_switching/paper.out +++ b/papers/level_switching/paper.out @@ -5,3 +5,9 @@ \BOOKMARK [2][-]{section*.2}{\376\377\000P\000r\000e\000p\000r\000o\000c\000e\000s\000s\000i\000n\000g\000\040\000t\000o\000w\000a\000r\000d\000\040\000b\000a\000l\000a\000n\000c\000e\000d\000\040\000s\000w\000i\000t\000c\000h\000e\000s}{section.3}% 5 \BOOKMARK [2][-]{section*.3}{\376\377\000T\000h\000e\000\040\000d\000\040\0002\000\040\000a\000n\000a\000l\000o\000g\000\040\000a\000n\000d\000\040\000r\000e\000c\000u\000r\000s\000i\000v\000e\000\040\000l\000o\000p\000s\000i\000d\000e\000d\000n\000e\000s\000s}{section.3}% 6 \BOOKMARK [2][-]{section*.4}{\376\377\000E\000m\000p\000i\000r\000i\000c\000a\000l\000\040\000t\000e\000r\000m\000i\000n\000a\000t\000i\000o\000n}{section.3}% 7 +\BOOKMARK [2][-]{section*.5}{\376\377\000W\000h\000a\000t\000\040\000t\000h\000e\000\040\000n\000a\000t\000u\000r\000a\000l\000\040\000m\000o\000n\000o\000v\000a\000r\000i\000a\000n\000t\000s\000\040\000d\000o\000\040\000n\000o\000t\000\040\000g\000i\000v\000e\000\040\000u\000s}{section.3}% 8 +\BOOKMARK [2][-]{section*.6}{\376\377\000E\000m\000p\000i\000r\000i\000c\000a\000l\000\040\000t\000e\000r\000m\000i\000n\000a\000t\000i\000o\000n\000\040\000o\000n\000\040\000r\000a\000n\000d\000o\000m\000\040\000c\000o\000n\000f\000i\000g\000u\000r\000a\000t\000i\000o\000n\000s}{section.3}% 9 +\BOOKMARK [1][-]{section.4}{\376\377\0004\000.\000\040\000R\000e\000a\000c\000h\000a\000b\000i\000l\000i\000t\000y\000\040\000v\000i\000a\000\040\000e\000d\000g\000e\000\040\000s\000w\000i\000t\000c\000h\000e\000s}{}% 10 +\BOOKMARK [2][-]{section*.7}{\376\377\000T\000w\000o\000\040\000c\000a\000s\000e\000s\000\040\000o\000n\000\040\000t\000h\000e\000\040\000l\000a\000y\000e\000r\000\040\000b\000e\000l\000o\000w\000\040\000k}{section.4}% 11 +\BOOKMARK [2][-]{section*.8}{\376\377\000C\000o\000m\000b\000i\000n\000i\000n\000g}{section.4}% 12 +\BOOKMARK [1][-]{section*.9}{\376\377\000R\000e\000f\000e\000r\000e\000n\000c\000e\000s}{}% 13 diff --git a/papers/level_switching/paper.pdf b/papers/level_switching/paper.pdf index 3b03ea6..4bc0ec4 100644 Binary files a/papers/level_switching/paper.pdf and b/papers/level_switching/paper.pdf differ diff --git a/papers/level_switching/paper.tex b/papers/level_switching/paper.tex index 367457b..737bd3a 100644 --- a/papers/level_switching/paper.tex +++ b/papers/level_switching/paper.tex @@ -561,4 +561,107 @@ reached from the current maximum-depth face by a preprocessing path of length bounded by the dual-tree diameter of $L_k$? \end{question} +\section{Reachability via edge switches} +\label{sec:reachability} + +We now sketch a positive termination argument that bypasses the local +question of balanced surface switches entirely: instead of insisting +that each switch strictly improve facial depth, we show that any +$L_k$ can be transformed by edge switches into a configuration in which +every face has depth $0$. Throughout this section we adopt the +\emph{stable-labelling convention}: the level $\ell_G(v)$ of each +vertex is fixed at the start of the procedure (by BFS from $S$ in the +initial triangulation $G$) and reused thereafter, even after edge +switches modify the triangulation. In particular, the level-$k$ +subgraph $L_k$ of the current triangulation always means ``the +subgraph induced on the vertices labelled $k$ at the start''. + +\subsection*{Two cases on the layer below $k$} + +We split on whether any $L_k$-face has a higher-level vertex in its +interior in the planar embedding inherited from $\Pi_G$. + +\textbf{Case 1: every inner face of $L_k$ is a triangle and contains +no vertex of level $\geq k+1$ in its interior.} + +Under this hypothesis, for every chord $uv \in L_k$ the two $G$-triangles +at $uv$ have their third vertices in $L_k$ (since the interior of the +two $L_k$-faces adjacent to $uv$ in $\Pi_G$ contains no other vertex of +$G$). The edge switch at $uv$ is therefore always in Case~(ii) of +Proposition~\ref{prop:balanced-descent}, and acts as a flip of the +chord $uv$ in $L_k$ regarded purely as a maximal outerplanar graph. + +Maximal outerplanar graphs on $n$ labelled vertices (arranged on a +common outer cycle) are exactly triangulations of a convex $n$-gon. +The set of such triangulations, with chord flips as edges, is the +1-skeleton of the associahedron and is connected; in fact any two +triangulations are joined by $O(n)$ chord flips~\cite{sleator-tarjan-thurston}. +A \emph{fan triangulation} -- the triangulation obtained by adding +chords from a single apex vertex $v_0$ to every other vertex -- has +every inner triangle bounded by an outer-cycle edge (namely the side +opposite $v_0$ in that triangle), so every face of a fan triangulation +lies in $\mathcal{B}$ and has depth $0$. + +Combining: in Case~1, $L_k$ can be transformed into a fan triangulation +by $O(n)$ edge switches, after which every face has depth $0$. + +\textbf{Case 2: some $L_k$-face $F$ has a vertex of level $\geq k+1$ +in its interior.} + +Pick any edge $uv$ of $\partial F$. The $G$-triangle at $uv$ on the +$F$-side has its third vertex $w$ inside $F$, so $w$ is a vertex of +level $\geq k+1$ and in particular $w \notin L_k$. The edge switch +at $uv$ is therefore in Case~(i) of +Proposition~\ref{prop:balanced-descent}: the edge $uv$ is removed from +$L_k$, no new edge is added to $L_k$, and the face $F$ merges with the +$L_k$-face on the opposite side of $uv$ into a single larger face. The +number of inner faces of $L_k$ strictly decreases by $1$. + +\subsection*{Combining} + +\begin{theorem} +\label{thm:reachability} +Under the stable-labelling convention, every $L_k$ can be transformed +by edge switches into a configuration in which every inner face of +$L_k$ has facial depth $0$, in $O(n)$ edge switches. +\end{theorem} + +\begin{proof}[Proof sketch] +Apply Case~2 repeatedly while $L_k$ has any inner face with a +higher-level vertex in its interior. Each application reduces the +number of inner faces of $L_k$ by $1$, so after at most $|L_k| - 2 \leq +n - 2$ such steps we reach one of: +\begin{itemize} +\item A configuration satisfying the hypothesis of Case~1, in which +case Case~1 finishes the job in $O(n)$ flips by reaching a fan +triangulation. +\item A configuration in which $L_k$ has only one inner face -- i.e., +$L_k$ consists of only its outer cycle, with no chords. The unique +inner face is bounded by all $n$ outer-cycle edges, so it lies in +$\mathcal{B}$ and has depth $0$. +\end{itemize} +Both outcomes leave every face at depth $0$. The total step count is +at most $(n - 2) + O(n) = O(n)$. +\end{proof} + +\begin{remark} +Theorem~\ref{thm:reachability} settles the existence question +Question~\ref{q:preprocessing-terminates} affirmatively in the +following sense: \emph{some} sequence of edge switches drives every +face to depth $0$ in $O(n)$ steps. It does not, however, identify the +sequence by a local rule (the leaf-distance algorithm of +Section~\ref{sec:reachability}'s preceding discussion), and in +particular the question of which \emph{rule} produces such a sequence +without backtracking remains open. +\end{remark} + +\begin{thebibliography}{9} + +\bibitem{sleator-tarjan-thurston} +D.~D. Sleator, R.~E. Tarjan, W.~P. Thurston. +\emph{Rotation distance, triangulations, and hyperbolic geometry}. +Journal of the American Mathematical Society, 1988. + +\end{thebibliography} + \end{document}