Add Level Switching paper with surface-switch framework

Defines level cycles, edge switches, surface switches, and facial depth
on level components of plane triangulations. Proves outerplanarity of
level components and a depth-descent lemma. Introduces balanced surface
switches and proves they remove a depth-d level cycle while creating
1-2 new depth-(d-1) cycles. Documents the 9-vertex counterexample where
no balanced switch exists and sketches preprocessing toward
balancedness, leaving general termination open.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 23:08:22 -04:00
parent e8b2e47e44
commit 7183dc1b67
18 changed files with 1744 additions and 0 deletions
@@ -0,0 +1,98 @@
"""9-vertex L_k where the unique depth-1 face has NO balanced surface switch.
Outer cycle: 0..8. Triangulated with chords 0-2, 0-3, 3-5, 3-6, 0-6, 6-8.
Central triangle F = (0,3,6) has depth 1; its three neighbours
(0,2,3), (3,5,6), (6,8,0) are all depth 0 but each has only ONE
outer-cycle edge (not two), so none is an "ear" of F.
For d = 1, balancedness requires F' to be an ear of uv (both non-uv
edges on the outer cycle). No neighbour of F qualifies.
"""
import os
import math
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
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)]
FACES = [
(0, 1, 2), # ear
(0, 2, 3), # 1 outer edge, depth 0
(3, 4, 5), # ear
(3, 5, 6), # 1 outer edge, depth 0
(6, 7, 8), # ear
(6, 8, 0), # 1 outer edge, depth 0
(0, 3, 6), # central, depth 1 -- the troublemaker
]
def face_edges(f):
return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
frozenset((f[0], f[2]))}
outer_set = {frozenset(e) for e in OUTER_EDGES}
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)
B = [i for i, f in enumerate(FACES)
if len(face_edges(f) & outer_set) >= 1]
depth = {i: min(nx.shortest_path_length(D, i, b) for b in B)
for i in range(len(FACES))}
palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}
fig, ax = plt.subplots(figsize=(7, 7))
for i, f in enumerate(FACES):
d = depth[i]
poly = Polygon([POS[v] for v in f], closed=True,
facecolor=palette[d], edgecolor=edge_pal[d],
linewidth=1.6, alpha=0.7, zorder=0)
ax.add_patch(poly)
cx = sum(POS[v][0] for v in f) / 3
cy = sum(POS[v][1] for v in f) / 3
ax.text(cx, cy, rf'$\mathrm{{depth}}={d}$',
ha='center', va='center', fontsize=10,
color=edge_pal[d], fontweight='bold')
# Mark the three "bad" chord edges (would-be-switched edges of F that
# fail balancedness because the chord side has no outer-cycle edge to
# pair with).
F_edges = [(0, 3), (3, 6), (0, 6)]
for (a, b) in OUTER_EDGES + CHORDS:
color = '#333'; lw = 1.2
if (a, b) in F_edges or (b, a) in F_edges:
color = '#dc2626'; lw = 3.0
ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
color=color, linewidth=lw, zorder=1)
for i, (x, y) in POS.items():
ax.scatter([x], [y], s=300, c='#1f2937', edgecolors='black',
linewidths=1.0, zorder=2)
ax.text(x, y, str(i), ha='center', va='center',
fontsize=10, color='white', fontweight='bold', zorder=3)
ax.set_aspect('equal'); ax.axis('off')
ax.set_xlim(-1.3, 1.3); ax.set_ylim(-1.3, 1.3)
ax.set_title('Depth-1 face with no balanced surface switch',
fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_no_balanced_switch.png')
fig.savefig(out, dpi=180, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
for i, f in enumerate(FACES):
print(f' {f} -> depth {depth[i]}')
@@ -0,0 +1,179 @@
"""Counterexample showing that a surface switch on the edge between a
depth-d face F and a depth-(d-1) face F' can create a new face of depth
d (not d-1) when the depth-0 neighbor of F' lies on only one side of
the shared edge.
14-vertex maximal outerplanar L_k. Outer cycle order:
u, p1, p2, p3, x, q1, v, b1, b2, b3, w, a1, a2, a3 -> u
Central triangle F = (u, v, w) has depth 2.
F' = (u, v, x) has depth 1 (its depth-0 neighbor is the q1-ear on the
v-side of x; its u-side neighbor A_ux is depth 1).
A_uw = (u, a2, w), A_vw = (v, b2, w) are both depth 1.
Surface switch on uv: flip uv -> wx. New faces are
A = (u, w, x) and B = (v, w, x).
B inherits the depth-0 q1-ear, so depth(B) = 1.
A's neighbors are A_uw (1), A_ux (1), B (1), so depth(A) = 2 = d. BAD.
"""
import os
import math
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
OUTER = ['u', 'p1', 'p2', 'p3', 'x', 'q1', 'v',
'b1', 'b2', 'b3', 'w', 'a1', 'a2', 'a3']
n = len(OUTER)
POS = {}
for i, name in enumerate(OUTER):
a = math.radians(90 - i * (360 / n))
POS[name] = (math.cos(a), math.sin(a))
OUTER_EDGES = [(OUTER[i], OUTER[(i + 1) % n]) for i in range(n)]
CHORDS_BEFORE = [
('u', 'v'), ('u', 'w'), ('v', 'w'), # central F = (u,v,w)
('u', 'x'), ('v', 'x'), # F' = (u,v,x)
('u', 'a2'), ('a2', 'w'), # A_uw = (u,a2,w)
('v', 'b2'), ('b2', 'w'), # A_vw = (v,b2,w)
('u', 'p2'), ('p2', 'x'), # A_ux = (u,p2,x)
]
CHORDS_AFTER = [c for c in CHORDS_BEFORE if set(c) != {'u', 'v'}] + [('w', 'x')]
FACES_BEFORE = [
('u', 'v', 'w'), # F (depth 2 -- bad)
('u', 'v', 'x'), # F' (depth 1)
('u', 'a2', 'w'), # A_uw (depth 1)
('v', 'b2', 'w'), # A_vw (depth 1)
('u', 'p2', 'x'), # A_ux (depth 1)
('v', 'q1', 'x'), # A_vx (depth 0)
('u', 'a1', 'a2'), # depth 0
('a2', 'a3', 'w'), # depth 0
('v', 'b1', 'b2'), # depth 0
('b2', 'b3', 'w'), # depth 0
('u', 'p1', 'p2'), # depth 0
('p2', 'p3', 'x'), # depth 0
]
FACES_AFTER = [
('u', 'w', 'x'), # A (depth 2 -- still bad!)
('v', 'w', 'x'), # B (depth 1)
('u', 'a2', 'w'),
('v', 'b2', 'w'),
('u', 'p2', 'x'),
('v', 'q1', 'x'),
('u', 'a1', 'a2'),
('a2', 'a3', 'w'),
('v', 'b1', 'b2'),
('b2', 'b3', 'w'),
('u', 'p1', 'p2'),
('p2', 'p3', 'x'),
]
def compute_depths(faces, chords):
"""Compute facial depth for each face using threshold-1 definition."""
outer_set = {frozenset(e) for e in OUTER_EDGES}
def face_edges(f):
return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
frozenset((f[0], f[2]))}
# Build inner-face dual
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)
B = [i for i, f in enumerate(faces)
if len(face_edges(f) & outer_set) >= 1]
depth = {}
for i in range(len(faces)):
if not B:
depth[i] = float('inf')
continue
depth[i] = min(nx.shortest_path_length(D, i, b) for b in B)
return depth
def draw_panel(ax, faces, chords, depth, title, highlight_edge=None,
highlight_face=None):
palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}
for i, f in enumerate(faces):
d = depth[i]
face_color = palette.get(d, '#ddd')
face_edge = edge_pal.get(d, '#333')
lw = 1.4
if highlight_face is not None and i == highlight_face:
face_edge = '#7c2d12'
lw = 3.0
poly = Polygon([POS[v] for v in f], closed=True,
facecolor=face_color, edgecolor=face_edge,
linewidth=lw, alpha=0.7, zorder=0)
ax.add_patch(poly)
cx = sum(POS[v][0] for v in f) / 3
cy = sum(POS[v][1] for v in f) / 3
ax.text(cx, cy, str(d), ha='center', va='center', fontsize=11,
color=edge_pal.get(d, '#333'), fontweight='bold')
# Draw edges
all_edges = OUTER_EDGES + list(chords)
for (a, b) in all_edges:
color = '#333'; lw = 1.2
if highlight_edge is not None and {a, b} == set(highlight_edge):
color = '#dc2626'; lw = 3.5
elif (a, b) == ('w', 'x') or (a, b) == ('x', 'w'):
color = '#16a34a'; lw = 3.0
ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
color=color, linewidth=lw, zorder=1)
# Draw vertices
for name, (x, y) in POS.items():
ax.scatter([x], [y], s=260, c='#1f2937', edgecolors='black',
linewidths=0.8, zorder=2)
ax.text(x, y, name, ha='center', va='center',
fontsize=8, color='white', fontweight='bold', zorder=3)
ax.set_aspect('equal'); ax.axis('off')
ax.set_xlim(-1.35, 1.35); ax.set_ylim(-1.35, 1.35)
ax.set_title(title, fontsize=12)
def main():
depth_before = compute_depths(FACES_BEFORE, CHORDS_BEFORE)
depth_after = compute_depths(FACES_AFTER, CHORDS_AFTER)
print("BEFORE:")
for i, f in enumerate(FACES_BEFORE):
print(f" {f} -> depth {depth_before[i]}")
print("AFTER:")
for i, f in enumerate(FACES_AFTER):
print(f" {f} -> depth {depth_after[i]}")
fig, axes = plt.subplots(1, 2, figsize=(15, 7.5))
draw_panel(axes[0], FACES_BEFORE, CHORDS_BEFORE, depth_before,
r'Before: $F=(u,v,w)$ depth 2, $F\'=(u,v,x)$ depth 1',
highlight_edge=('u', 'v'), highlight_face=0)
draw_panel(axes[1], FACES_AFTER, CHORDS_AFTER, depth_after,
r'After surface switch on $uv$: $A=(u,w,x)$ still depth 2!',
highlight_face=0)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_counterexample_surface_switch.png')
fig.savefig(out, dpi=180, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
if __name__ == '__main__':
main()
@@ -0,0 +1,370 @@
"""Generate the three definition figures for the Level Switching paper.
Uses a stacked 7-vertex triangulation T:
outer triangle {0,1,2}, inner vertex 3 connected to all three,
then vertices 4,5,6 inserted into faces (1,2,3),(0,2,3),(0,1,3).
"""
import os
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
# Vertex positions (hand-placed for a clean planar drawing).
POS = {
0: (-1.5, -0.9),
1: (1.5, -0.9),
2: (0.0, 1.6),
3: (0.0, 0.0),
4: (0.55, 0.2), # in face (1,2,3)
5: (-0.55, 0.2), # in face (0,2,3)
6: (0.0, -0.55), # in face (0,1,3)
}
EDGES = [
(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3), # K_4
(4, 1), (4, 2), (4, 3), # stack in (1,2,3)
(5, 0), (5, 2), (5, 3), # stack in (0,2,3)
(6, 0), (6, 1), (6, 3), # stack in (0,1,3)
]
def make_graph():
G = nx.Graph()
G.add_nodes_from(POS.keys())
G.add_edges_from(EDGES)
return G
def draw_base(ax, G, node_colors, node_size=520, font_color='white',
edge_color='#555', edge_width=1.4):
nx.draw_networkx_edges(G, POS, ax=ax, edge_color=edge_color, width=edge_width)
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=node_colors,
node_size=node_size, edgecolors='black', linewidths=1.0)
nx.draw_networkx_labels(G, POS, ax=ax, font_color=font_color,
font_size=11, font_weight='bold')
ax.set_aspect('equal')
ax.axis('off')
# ---------------------------------------------------------------------------
# Figure 1: Level source (face source vs. degree-3 vertex source)
# ---------------------------------------------------------------------------
def fig_level_source():
G = make_graph()
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
# Panel A: face source S = {0,1,2}
ax = axes[0]
face_S = {0, 1, 2}
colors = ['#ef4444' if v in face_S else '#cbd5e1' for v in G.nodes()]
# Highlight the source face
tri = Polygon([POS[v] for v in [0, 1, 2]], closed=True,
facecolor='#fecaca', edgecolor='#ef4444', linewidth=2.0,
alpha=0.45, zorder=0)
ax.add_patch(tri)
draw_base(ax, G, colors)
ax.set_title(r'Face source $S = \{0,1,2\}$', fontsize=12)
# Panel B: degree-3 vertex source S = {4}
ax = axes[1]
vert_S = {4}
colors = ['#ef4444' if v in vert_S else '#cbd5e1' for v in G.nodes()]
draw_base(ax, G, colors)
ax.set_title(r'Degree-3 vertex source $S = \{4\}$', fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_level_source.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
# ---------------------------------------------------------------------------
# Figure 2: Levels (BFS distance from a source)
# ---------------------------------------------------------------------------
def fig_levels():
G = make_graph()
source = 4 # degree-3 vertex source
levels = nx.single_source_shortest_path_length(G, source)
# Color by level
palette = {0: '#ef4444', 1: '#f59e0b', 2: '#3b82f6'}
colors = [palette[levels[v]] for v in G.nodes()]
# Labels = level numbers
labels = {v: rf'$\ell={levels[v]}$' for v in G.nodes()}
fig, ax = plt.subplots(figsize=(6.5, 5.5))
nx.draw_networkx_edges(G, POS, ax=ax, edge_color='#555', width=1.4)
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=colors,
node_size=720, edgecolors='black', linewidths=1.0)
# Draw vertex id slightly above, level label inside
for v, (x, y) in POS.items():
ax.text(x, y, str(v), ha='center', va='center',
fontsize=10, fontweight='bold', color='white')
ax.text(x + 0.18, y + 0.18, rf'$\ell={levels[v]}$',
fontsize=10, color='black',
bbox=dict(boxstyle='round,pad=0.15',
facecolor='white', edgecolor='#999', linewidth=0.6))
ax.set_aspect('equal')
ax.axis('off')
ax.set_title(r'Levels $\ell_G(v)$ from source $S=\{4\}$', fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_levels.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
# ---------------------------------------------------------------------------
# Figure 3: Parity subgraph (even and odd induced subgraphs)
# ---------------------------------------------------------------------------
def fig_parity_subgraph():
G = make_graph()
source = 4
levels = nx.single_source_shortest_path_length(G, source)
parity = {v: levels[v] % 2 for v in G.nodes()}
even = [v for v in G.nodes() if parity[v] == 0]
odd = [v for v in G.nodes() if parity[v] == 1]
even_color = '#3b82f6' # blue
odd_color = '#f59e0b' # orange
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Panel A: full triangulation, vertices coloured by parity
ax = axes[0]
colors = [even_color if parity[v] == 0 else odd_color for v in G.nodes()]
draw_base(ax, G, colors)
ax.set_title(r"$G'$ with vertices coloured by $\ell_G$ mod 2", fontsize=12)
# Panel B: even parity subgraph (induced on even vertices)
ax = axes[1]
# Draw all edges faintly, then the induced subgraph in colour
nx.draw_networkx_edges(G, POS, ax=ax, edge_color='#ddd', width=1.0)
even_sub = G.subgraph(even)
nx.draw_networkx_edges(even_sub, POS, ax=ax, edge_color=even_color, width=2.4)
node_colors = [even_color if v in even else '#e5e7eb' for v in G.nodes()]
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=node_colors,
node_size=520, edgecolors='black', linewidths=1.0)
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(r"Even parity subgraph $E_{G,S}(G')$", fontsize=12)
# Panel C: odd parity subgraph
ax = axes[2]
nx.draw_networkx_edges(G, POS, ax=ax, edge_color='#ddd', width=1.0)
odd_sub = G.subgraph(odd)
nx.draw_networkx_edges(odd_sub, POS, ax=ax, edge_color=odd_color, width=2.4)
node_colors = [odd_color if v in odd else '#e5e7eb' for v in G.nodes()]
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=node_colors,
node_size=520, edgecolors='black', linewidths=1.0)
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(r"Odd parity subgraph $O_{G,S}(G')$", fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_parity_subgraph.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
# ---------------------------------------------------------------------------
# Figure: Level cycle (simple cycle within a single level)
# ---------------------------------------------------------------------------
def fig_level_cycle():
G = make_graph()
source = 4
levels = nx.single_source_shortest_path_length(G, source)
palette = {0: '#ef4444', 1: '#f59e0b', 2: '#3b82f6'}
colors = [palette[levels[v]] for v in G.nodes()]
# Level cycle: 1-2-3-1 lies entirely in L_1
cycle_edges = [(1, 2), (2, 3), (1, 3)]
fig, ax = plt.subplots(figsize=(6.5, 5.5))
nx.draw_networkx_edges(G, POS, ax=ax, edge_color='#bbb', width=1.2)
nx.draw_networkx_edges(G, POS, edgelist=cycle_edges, ax=ax,
edge_color='#dc2626', width=3.4)
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=colors,
node_size=620, edgecolors='black', linewidths=1.0)
nx.draw_networkx_labels(G, POS, ax=ax, font_color='white',
font_size=11, font_weight='bold')
# Annotate levels in small floating labels
for v, (x, y) in POS.items():
ax.text(x + 0.18, y + 0.18, rf'$\ell={levels[v]}$',
fontsize=9, color='black',
bbox=dict(boxstyle='round,pad=0.12',
facecolor='white', edgecolor='#999', linewidth=0.5))
ax.set_aspect('equal')
ax.axis('off')
ax.set_title(r'Level cycle in $L_1 = \{1,2,3\}$ (highlighted)', fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_level_cycle.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
# ---------------------------------------------------------------------------
# Figure: Edge switch (flip on a level-cycle edge)
# ---------------------------------------------------------------------------
def fig_edge_switch():
G = make_graph()
source = 4
levels = nx.single_source_shortest_path_length(G, source)
palette = {0: '#ef4444', 1: '#f59e0b', 2: '#3b82f6'}
colors = [palette[levels[v]] for v in G.nodes()]
# We switch edge (1,2), which lies in the L_1 cycle 1-2-3-1.
# Its two adjacent faces in T are (0,1,2) and (1,2,4); the flip
# removes 1-2 and adds 0-4.
removed = (1, 2)
added = (0, 4)
Gprime = G.copy()
Gprime.remove_edge(*removed)
Gprime.add_edge(*added)
fig, axes = plt.subplots(1, 2, figsize=(12, 5.5))
# Panel A: before — highlight the level-cycle edge to be switched
ax = axes[0]
other_edges = [e for e in G.edges() if set(e) != set(removed)]
nx.draw_networkx_edges(G, POS, edgelist=other_edges, ax=ax,
edge_color='#bbb', width=1.2)
nx.draw_networkx_edges(G, POS, edgelist=[removed], ax=ax,
edge_color='#dc2626', width=3.4)
nx.draw_networkx_nodes(G, POS, ax=ax, node_color=colors,
node_size=560, edgecolors='black', linewidths=1.0)
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(r'Before: edge $1\!-\!2$ lies on the $L_1$ cycle',
fontsize=12)
# Panel B: after — the new edge highlighted in green
ax = axes[1]
other_edges = [e for e in Gprime.edges() if set(e) != set(added)]
nx.draw_networkx_edges(Gprime, POS, edgelist=other_edges, ax=ax,
edge_color='#bbb', width=1.2)
nx.draw_networkx_edges(Gprime, POS, edgelist=[added], ax=ax,
edge_color='#16a34a', width=3.4)
nx.draw_networkx_nodes(Gprime, POS, ax=ax, node_color=colors,
node_size=560, edgecolors='black', linewidths=1.0)
nx.draw_networkx_labels(Gprime, POS, ax=ax, font_color='white',
font_size=11, font_weight='bold')
ax.set_aspect('equal'); ax.axis('off')
ax.set_title(r'After: $1\!-\!2$ replaced by $0\!-\!4$',
fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_edge_switch.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
# ---------------------------------------------------------------------------
# Figure: Facial depth (depths in an outerplanar L_k)
# ---------------------------------------------------------------------------
def fig_facial_depth():
import math
# 12-vertex maximal outerplanar graph with 3-fold symmetry.
# Central triangle (0,4,8); three "in-between" triangles (0,2,4),
# (4,6,8), (0,8,10) sit between the central triangle and the
# outer "ears" (0,1,2), (2,3,4), (4,5,6), (6,7,8), (8,9,10),
# (10,11,0).
n = 12
pos = {}
for i in range(n):
a = math.radians(90 - i * (360 / n))
pos[i] = (math.cos(a), math.sin(a))
outer_edges = [(i, (i + 1) % n) for i in range(n)]
diagonals = [(0, 2), (2, 4), (4, 6), (6, 8), (8, 10), (10, 0), # "short" chords
(0, 4), (4, 8), (0, 8)] # central triangle
L = nx.Graph()
L.add_nodes_from(pos)
L.add_edges_from(outer_edges + diagonals)
inner_faces = [
(0, 1, 2), (2, 3, 4), (4, 5, 6),
(6, 7, 8), (8, 9, 10), (10, 11, 0), # 6 outer "ears"
(0, 2, 4), (4, 6, 8), (0, 8, 10), # 3 in-between
(0, 4, 8), # central
]
# Build dual graph on inner faces: edge iff faces share an edge
def face_edges(f):
a, b, c = f
return {frozenset((a, b)), frozenset((b, c)), frozenset((a, c))}
outer_edge_set = {frozenset(e) for e in outer_edges}
D = nx.Graph()
D.add_nodes_from(range(len(inner_faces)))
for i, fi in enumerate(inner_faces):
for j, fj in enumerate(inner_faces):
if i < j and face_edges(fi) & face_edges(fj):
D.add_edge(i, j)
# Boundary set B: faces whose bounding level cycle has >= 1 outer-cycle edge
B = [i for i, f in enumerate(inner_faces)
if len(face_edges(f) & outer_edge_set) >= 1]
# Depth = min distance in D to any face in B
depth = {}
for i in range(len(inner_faces)):
dists = [nx.shortest_path_length(D, i, b) for b in B]
depth[i] = min(dists)
# Colour by depth
depth_color = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
depth_edge = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}
fig, ax = plt.subplots(figsize=(7, 7))
# Fill faces by depth
for i, f in enumerate(inner_faces):
poly = Polygon([pos[v] for v in f], closed=True,
facecolor=depth_color[depth[i]],
edgecolor=depth_edge[depth[i]],
linewidth=1.5, alpha=0.7, zorder=0)
ax.add_patch(poly)
cx = sum(pos[v][0] for v in f) / 3
cy = sum(pos[v][1] for v in f) / 3
ax.text(cx, cy, rf'$\mathrm{{depth}}={depth[i]}$',
ha='center', va='center', fontsize=10,
color=depth_edge[depth[i]], fontweight='bold')
# Draw the graph on top
nx.draw_networkx_edges(L, pos, ax=ax, edge_color='#333', width=1.4)
nx.draw_networkx_nodes(L, pos, ax=ax, node_color='#1f2937',
node_size=320, edgecolors='black', linewidths=1.0)
nx.draw_networkx_labels(L, pos, ax=ax, font_color='white',
font_size=10, font_weight='bold')
ax.set_aspect('equal'); ax.axis('off')
ax.set_xlim(-1.3, 1.3); ax.set_ylim(-1.3, 1.3)
ax.set_title(r'Facial depth in an outerplanar $L_k$', fontsize=12)
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_facial_depth.png')
fig.savefig(out, dpi=200, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
if __name__ == '__main__':
fig_level_source()
fig_levels()
fig_level_cycle()
fig_edge_switch()
fig_parity_subgraph()
fig_facial_depth()
@@ -0,0 +1,118 @@
"""Demonstrate the preprocessing strategy on the 9-vertex example.
Start: F = (0,3,6) at depth 1, no balanced surface switch exists
(F has no edge of "span 1" -- no edge with a single outer-cycle
vertex between the endpoints, hence no ear neighbour).
Step: perform the (unbalanced) surface switch on edge uv = 03, with
F' = (0,2,3) and third vertex x = 2; in Case (ii) the flip removes 03
and adds wx = 62.
Result: A = (0,2,6) at depth 1 has edge 02 at span 1, so the ear
(0,1,2) is now a balanced-switch target.
"""
import os
import math
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
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)]
outer_set = {frozenset(e) for e in OUTER_EDGES}
def face_edges(f):
return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
frozenset((f[0], f[2]))}
def compute_depths(faces):
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)
B = [i for i, f in enumerate(faces)
if len(face_edges(f) & outer_set) >= 1]
return {i: min(nx.shortest_path_length(D, i, b) for b in B)
for i in range(len(faces))}
CHORDS_BEFORE = [(0, 2), (0, 3), (3, 5), (3, 6), (0, 6), (6, 8)]
FACES_BEFORE = [
(0, 1, 2), (0, 2, 3), (3, 4, 5), (3, 5, 6),
(6, 7, 8), (6, 8, 0), (0, 3, 6),
]
# After non-balanced switch on edge 03: remove 03, add 26
CHORDS_AFTER = [c for c in CHORDS_BEFORE if set(c) != {0, 3}] + [(2, 6)]
FACES_AFTER = [
(0, 1, 2), (3, 4, 5), (3, 5, 6),
(6, 7, 8), (6, 8, 0),
(0, 2, 6), (2, 3, 6),
]
def draw(ax, faces, chords, depth, title, highlight_edges=None,
green_edges=None):
palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}
for i, f in enumerate(faces):
d = depth[i]
poly = Polygon([POS[v] for v in f], closed=True,
facecolor=palette.get(d, '#ddd'),
edgecolor=edge_pal.get(d, '#333'),
linewidth=1.4, alpha=0.7, zorder=0)
ax.add_patch(poly)
cx = sum(POS[v][0] for v in f) / 3
cy = sum(POS[v][1] for v in f) / 3
ax.text(cx, cy, str(d), ha='center', va='center', fontsize=11,
color=edge_pal.get(d, '#333'), fontweight='bold')
for (a, b) in OUTER_EDGES + chords:
color = '#333'; lw = 1.2
if highlight_edges and ((a, b) in highlight_edges or
(b, a) in highlight_edges):
color = '#dc2626'; lw = 3.0
if green_edges and ((a, b) in green_edges or
(b, a) in green_edges):
color = '#16a34a'; lw = 3.0
ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
color=color, linewidth=lw, zorder=1)
for i, (x, y) in POS.items():
ax.scatter([x], [y], s=270, c='#1f2937', edgecolors='black',
linewidths=1.0, zorder=2)
ax.text(x, y, str(i), ha='center', va='center',
fontsize=9, color='white', fontweight='bold', zorder=3)
ax.set_aspect('equal'); ax.axis('off')
ax.set_xlim(-1.3, 1.3); ax.set_ylim(-1.3, 1.3)
ax.set_title(title, fontsize=11)
depth_before = compute_depths(FACES_BEFORE)
depth_after = compute_depths(FACES_AFTER)
print('BEFORE:')
for i, f in enumerate(FACES_BEFORE):
print(f' {f} -> depth {depth_before[i]}')
print('AFTER:')
for i, f in enumerate(FACES_AFTER):
print(f' {f} -> depth {depth_after[i]}')
fig, axes = plt.subplots(1, 2, figsize=(14, 7))
draw(axes[0], FACES_BEFORE, CHORDS_BEFORE, depth_before,
'Before: F=(0,3,6) depth 1; spans (2,2,2) so no ear neighbour',
highlight_edges=[(0, 3)])
draw(axes[1], FACES_AFTER, CHORDS_AFTER, depth_after,
'After non-balanced switch 03->26: A=(0,2,6) depth 1; edge 02 has span 1',
green_edges=[(2, 6)], highlight_edges=[(0, 2)])
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_preprocessing.png')
fig.savefig(out, dpi=180, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

+56
View File
@@ -0,0 +1,56 @@
\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 The two kinds of level source on a 7-vertex triangulation $T$ (K\textsubscript {4} with vertices $4,5,6$ stacked into the three interior faces). Left: the face source $S = \{0,1,2\}$ (level-0 vertices are the corners of the highlighted triangle). Right: the degree-$3$ vertex source $S = \{4\}$.}}{1}{figure.1}\protected@file@percent }
\newlabel{fig:level-source}{{1}{1}{The two kinds of level source on a 7-vertex triangulation $T$ (K\textsubscript {4} with vertices $4,5,6$ stacked into the three interior faces). Left: the face source $S = \{0,1,2\}$ (level-0 vertices are the corners of the highlighted triangle). Right: the degree-$3$ vertex source $S = \{4\}$}{figure.1}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {2}{\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.}}{2}{figure.2}\protected@file@percent }
\newlabel{fig:levels}{{2}{2}{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.2}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\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.3}\protected@file@percent }
\newlabel{fig:level-cycle}{{3}{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.3}{}}
\newlabel{def:edge-switch}{{2.4}{2}{Edge switch}{theorem.2.4}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {4}{\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$.}}{3}{figure.4}\protected@file@percent }
\newlabel{fig:edge-switch}{{4}{3}{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.4}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {5}{\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.5}\protected@file@percent }
\newlabel{fig:parity-subgraph}{{5}{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.5}{}}
\newlabel{def:facial-depth}{{2.6}{3}{Facial depth}{theorem.2.6}{}}
\newlabel{def:surface-switch}{{2.7}{3}{Surface switch}{theorem.2.7}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces Facial depths in a maximal outerplanar graph on $12$ vertices. The six green ear-triangles share an edge with the outer $12$-cycle and so lie in $\mathcal {B}$ (depth $0$). The three yellow ``in-between'' triangles $(0,2,4),(4,6,8),(0,8,10)$ have only diagonal edges but each is dual-adjacent to ears, giving them $\mathrm {depth} = 1$. The central triangle $(0,4,8)$ is also all-diagonal; its dual neighbours are the three depth-$1$ triangles, so it is isolated with $\mathrm {depth} = 2$.}}{4}{figure.6}\protected@file@percent }
\newlabel{fig:facial-depth}{{6}{4}{Facial depths in a maximal outerplanar graph on $12$ vertices. The six green ear-triangles share an edge with the outer $12$-cycle and so lie in $\mathcal {B}$ (depth $0$). The three yellow ``in-between'' triangles $(0,2,4),(4,6,8),(0,8,10)$ have only diagonal edges but each is dual-adjacent to ears, giving them $\mathrm {depth} = 1$. The central triangle $(0,4,8)$ is also all-diagonal; its dual neighbours are the three depth-$1$ triangles, so it is isolated with $\mathrm {depth} = 2$}{figure.6}{}}
\newlabel{def:balanced-surface-switch}{{2.8}{4}{Balanced surface switch}{theorem.2.8}{}}
\@writefile{toc}{\contentsline {section}{\tocsection {}{3}{Outerplanarity of level components}}{4}{section.3}\protected@file@percent }
\newlabel{sec:outerplanar-components}{{3}{4}{Outerplanarity of level components}{section.3}{}}
\newlabel{thm:outerplanar-component}{{3.1}{4}{}{theorem.3.1}{}}
\newlabel{lem:depth-descent}{{3.2}{5}{}{theorem.3.2}{}}
\newlabel{prop:balanced-descent}{{3.3}{5}{}{theorem.3.3}{}}
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{When does a balanced surface switch exist?}}{6}{section*.1}\protected@file@percent }
\newlabel{obs:span1-balanced-d1}{{3.4}{6}{}{theorem.3.4}{}}
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{Preprocessing toward balanced switches}}{6}{section*.2}\protected@file@percent }
\newlabel{ex:preprocessing}{{3.5}{6}{}{theorem.3.5}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {7}{\ignorespaces $9$-vertex maximal outerplanar $L_k$. $F = (0,3,6)$ has $\mathrm {depth} = 1$ and all three of its edges have span $2$, so none of $F$'s depth-$0$ neighbours is an ear. No balanced surface switch is available on $F$.}}{7}{figure.7}\protected@file@percent }
\newlabel{fig:no-balanced}{{7}{7}{$9$-vertex maximal outerplanar $L_k$. $F = (0,3,6)$ has $\mathrm {depth} = 1$ and all three of its edges have span $2$, so none of $F$'s depth-$0$ neighbours is an ear. No balanced surface switch is available on $F$}{figure.7}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {8}{\ignorespaces One step of preprocessing on the $9$-vertex example. Left: $F = (0,3,6)$ has no edge of span $1$; the chosen surface-switch edge $uv = 03$ (red) is unbalanced. Right: after the switch $03 \DOTSB \mapstochar \rightarrow 26$ (green), the new depth-$1$ face $A = (0,2,6)$ has its edge $02$ (red) at span $1$, exposing the ear $(0,1,2)$ as a balanced surface-switch target.}}{7}{figure.8}\protected@file@percent }
\newlabel{fig:preprocessing}{{8}{7}{One step of preprocessing on the $9$-vertex example. Left: $F = (0,3,6)$ has no edge of span $1$; the chosen surface-switch edge $uv = 03$ (red) is unbalanced. Right: after the switch $03 \mapsto 26$ (green), the new depth-$1$ face $A = (0,2,6)$ has its edge $02$ (red) at span $1$, exposing the ear $(0,1,2)$ as a balanced surface-switch target}{figure.8}{}}
\newlabel{tocindent-1}{0pt}
\newlabel{tocindent0}{14.69437pt}
\newlabel{tocindent1}{17.77782pt}
\newlabel{tocindent2}{0pt}
\newlabel{tocindent3}{0pt}
\newlabel{q:preprocessing-terminates}{{3.6}{8}{}{theorem.3.6}{}}
\gdef \@abspage@last{8}
+465
View File
@@ -0,0 +1,465 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 20 MAY 2026 23:02
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
))
<fig_level_source.png, id=24, 715.11165pt x 317.988pt>
File: fig_level_source.png Graphic file (type png)
<use fig_level_source.png>
Package pdftex.def Info: fig_level_source.png used on input line 105.
(pdftex.def) Requested size: 306.0022pt x 136.07228pt.
<fig_levels.png, id=26, 454.21695pt x 391.34206pt>
File: fig_levels.png Graphic file (type png)
<use fig_levels.png>
Package pdftex.def Info: fig_levels.png used on input line 122.
(pdftex.def) Requested size: 198.0011pt x 170.59666pt.
LaTeX Warning: `h' float specifier changed to `ht'.
<fig_level_cycle.png, id=27, 452.04884pt x 391.34206pt>
File: fig_level_cycle.png Graphic file (type png)
<use fig_level_cycle.png>
Package pdftex.def Info: fig_level_cycle.png used on input line 136.
(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
_level_source.png>]
<fig_edge_switch.png, id=49, 859.65166pt x 378.33345pt>
File: fig_edge_switch.png Graphic file (type png)
<use fig_edge_switch.png>
Package pdftex.def Info: fig_edge_switch.png used on input line 155.
(pdftex.def) Requested size: 341.9989pt x 150.51671pt.
LaTeX Warning: `h' float specifier changed to `ht'.
<fig_parity_subgraph.png, id=51, 1076.46165pt x 319.79475pt>
File: fig_parity_subgraph.png Graphic file (type png)
<use fig_parity_subgraph.png>
Package pdftex.def Info: fig_parity_subgraph.png used on input line 173.
(pdftex.def) Requested size: 360.0pt x 106.9477pt.
LaTeX Warning: `h' float specifier changed to `ht'.
[2 <./fig_levels.png> <./fig_level_cycle.png>]
<fig_facial_depth.png, id=64, 482.40225pt x 498.663pt>
File: fig_facial_depth.png Graphic file (type png)
<use fig_facial_depth.png>
Package pdftex.def Info: fig_facial_depth.png used on input line 200.
(pdftex.def) Requested size: 216.0022pt x 223.28535pt.
LaTeX Warning: `h' float specifier changed to `ht'.
[3 <./fig_edge_switch.png> <./fig_parity_subgraph.png>] [4 <./fig_facial_depth.
png>] [5]
Overfull \hbox (1.09193pt too wide) in paragraph at lines 354--357
[]\OT1/cmr/m/n/10 if $\OML/cmm/m/it/10 A[]$ \OT1/cmr/m/n/10 is an in-ner face,
bal-anced-ness gives $[](\OML/cmm/m/it/10 A[]\OT1/cmr/m/n/10 ) = \OML/cmm/m/it/
10 d \OMS/cmsy/m/n/10 ^^@ \OT1/cmr/m/n/10 2$, so $[](\OML/cmm/m/it/10 A\OT1/cmr
/m/n/10 ) \OMS/cmsy/m/n/10 ^^T
[]
<fig_no_balanced_switch.png, id=99, 483.0045pt x 498.2615pt>
File: fig_no_balanced_switch.png Graphic file (type png)
<use fig_no_balanced_switch.png>
Package pdftex.def Info: fig_no_balanced_switch.png used on input line 395.
(pdftex.def) Requested size: 198.0011pt x 204.25052pt.
LaTeX Warning: `h' float specifier changed to `ht'.
<fig_preprocessing.png, id=102, 998.932pt x 513.5185pt>
File: fig_preprocessing.png Graphic file (type png)
<use fig_preprocessing.png>
Package pdftex.def Info: fig_preprocessing.png used on input line 426.
(pdftex.def) Requested size: 360.0pt x 185.0624pt.
LaTeX Warning: `h' float specifier changed to `ht'.
[6] [7 <./fig_no_balanced_switch.png> <./fig_preprocessing.png>] [8]
(./paper.aux)
Package rerunfilecheck Info: File `paper.out' has not changed.
(rerunfilecheck) Checksum: AF95E9EBA8283D2CBF07B09833C039FF;1010.
)
Here is how much of TeX's memory you used:
9777 strings out of 478268
151593 string characters out of 5846347
458194 words of memory out of 5000000
27673 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,448s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb
></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmcsc10.pfb
></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb>
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb><
/usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb></u
sr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr
/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb></usr/lo
cal/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb></usr/local
/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb></usr/local/
texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy5.pfb></usr/local/te
xlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb></usr/local/texl
ive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/local/texli
ve/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/local/texlive
/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (8 pages, 1034024 bytes).
PDF statistics:
196 PDF objects out of 1000 (max. 8388607)
140 compressed objects within 2 object streams
38 named destinations out of 1000 (max. 500000)
81 words of extra memory for PDF output out of 10000 (max. 10000000)
+5
View File
@@ -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 [2][-]{section*.1}{\376\377\000W\000h\000e\000n\000\040\000d\000o\000e\000s\000\040\000a\000\040\000b\000a\000l\000a\000n\000c\000e\000d\000\040\000s\000u\000r\000f\000a\000c\000e\000\040\000s\000w\000i\000t\000c\000h\000\040\000e\000x\000i\000s\000t\000?}{section.3}% 4
\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
Binary file not shown.
+453
View File
@@ -0,0 +1,453 @@
%% 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{Level Switching}
% 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 either:
\begin{itemize}
\item a face $F$ of $G$ (all vertices of $F$ are level-0 sources), or
\item a vertex $v$ of degree 3 (the singleton $\{v\}$ is a level-0 source).
\end{itemize}
\end{definition}
\begin{figure}[h]
\centering
\includegraphics[width=0.85\textwidth]{fig_level_source.png}
\caption{The two kinds of level source on a 7-vertex triangulation $T$
(K\textsubscript{4} with vertices $4,5,6$ stacked into the three
interior faces). Left: the face source $S = \{0,1,2\}$
(level-0 vertices are the corners of the highlighted triangle).
Right: the degree-$3$ vertex source $S = \{4\}$.}
\label{fig:level-source}
\end{figure}
\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}
\begin{definition}[Facial depth]
\label{def:facial-depth}
Let $L_k$ be drawn with the outerplanar embedding inherited from $\Pi_G$,
let $D$ be the dual graph of this drawing with the outer face removed,
and let $\mathcal{B}$ be the set of inner faces of $L_k$ whose bounding
level cycle contains at least one edge of the outer cycle of $L_k$. The
\emph{facial depth} of an inner face $F$ of $L_k$ is
\[
\mathrm{depth}(F) \;=\; \min_{F' \in \mathcal{B}} \mathrm{dist}_D(F, F'),
\]
with the convention $\mathrm{depth}(F) = \infty$ if no such $F'$ exists.
An inner face is \emph{isolated} if $\mathrm{depth}(F) \geq 1$.
\end{definition}
\begin{figure}[h]
\centering
\includegraphics[width=0.6\textwidth]{fig_facial_depth.png}
\caption{Facial depths in a maximal outerplanar graph on $12$ vertices.
The six green ear-triangles share an edge with the outer $12$-cycle and
so lie in $\mathcal{B}$ (depth $0$). The three yellow ``in-between''
triangles $(0,2,4),(4,6,8),(0,8,10)$ have only diagonal edges but each
is dual-adjacent to ears, giving them $\mathrm{depth} = 1$. The central
triangle $(0,4,8)$ is also all-diagonal; its dual neighbours are the
three depth-$1$ triangles, so it is isolated with
$\mathrm{depth} = 2$.}
\label{fig:facial-depth}
\end{figure}
\begin{definition}[Surface switch]
\label{def:surface-switch}
A \emph{surface switch} is an edge switch (Definition~\ref{def:edge-switch})
applied to an edge incident to two level cycles, one of facial depth $d$
and the other of facial depth $d - 1$.
\end{definition}
\begin{definition}[Balanced surface switch]
\label{def:balanced-surface-switch}
Let $\sigma$ be a surface switch on an edge $e = uv$ separating an inner
face $F$ of $L_k$ of depth $d \geq 1$ from an adjacent inner face
$F' = uvx$ of depth $d - 1$. We say $\sigma$ is \emph{balanced} if each
of the two edges of $\partial F'$ other than $uv$ (namely $ux$ and $vx$)
either lies on the outer cycle of $L_k$ or is shared with an inner face
of $L_k$ of depth $d - 2$.
\end{definition}
When $d = 1$ the condition reduces to ``both $ux$ and $vx$ lie on the
outer cycle of $L_k$'', because no inner face has depth $-1$; in that
case $F'$ is a triangular ``ear'' hanging off $uv$.
\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$ is either a single vertex (when $S$ is a degree-$3$ vertex) or
the triangle bounding the source face (when $S$ is a face), both
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}
\begin{lemma}
\label{lem:depth-descent}
Let $C$ be a level component of $G$ with respect to $S$, drawn with the
outerplanar embedding inherited from $\Pi_G$, and let $D$ be its
inner-face dual. If $F$ is an inner face of $C$ with
$\mathrm{depth}(F) = d > 0$, then $F$ is dual-adjacent to an inner
face $F'$ with $\mathrm{depth}(F') = d - 1$.
\end{lemma}
\begin{proof}
By Theorem~\ref{thm:outerplanar-component}, $C$ is outerplanar, so the
inner-face dual $D$ is a forest (a standard fact; a tree when $C$ is
$2$-connected).
Each leaf $F_\ell$ of $D$ contains a single interior edge of $C$, so
the remaining edges of $\partial F_\ell$ lie on the outer cycle of $C$.
In particular $F_\ell$ has at least one outer-cycle edge, so
$F_\ell \in \mathcal{B}$ and $\mathrm{depth}(F_\ell) = 0$. Hence every
tree component of $D$ contains an element of $\mathcal{B}$, so the
depths of all of its vertices are finite.
Choose a shortest path $F = F_0, F_1, \ldots, F_d = F^\ast$ in $D$ from
$F$ to some $F^\ast \in \mathcal{B}$ realising $\mathrm{depth}(F) = d$.
The suffix $F_1, \ldots, F_d$ witnesses $\mathrm{depth}(F_1) \leq d - 1$.
If $\mathrm{depth}(F_1) \leq d - 2$, prepending the edge $F\,F_1$ to a
witnessing path would give $\mathrm{depth}(F) \leq d - 1$, contradicting
$\mathrm{depth}(F) = d$. Hence $\mathrm{depth}(F_1) = d - 1$, and we
may take $F' := F_1$.
\end{proof}
\begin{proposition}
\label{prop:balanced-descent}
Let $\sigma$ be a balanced surface switch on the edge $e = uv$ separating
$F$ (depth $d \geq 1$) from $F' = uvx$ (depth $d - 1$), and let
$G' = G - uv + wx$ be the result of the underlying edge flip, with
$uvw, uvx$ the two triangular faces of $G$ at $uv$. Then in $L_k'$ (the
level-$k$ subgraph of $G'$, with the level assignment of $G$):
\begin{enumerate}
\item the level cycle $\partial F$ is destroyed; and
\item one or two new inner faces appear in $L_k'$, each of depth exactly
$d - 1$.
\end{enumerate}
\end{proposition}
\begin{proof}
The flip removes $uv$ from $G$, so $\partial F$ is no longer a cycle of
$L_k'$, proving (1). For (2) we split on whether the new edge $wx$
re-enters $L_k$.
\textbf{Case (i): $\{w, x\} \not\subseteq L_k$.} Then $L_k' = L_k - uv$.
The faces $F$ and $F'$ merge into a single new inner face $\widetilde F$
with boundary $(\partial F \cup \partial F') \setminus \{uv\}$. The dual
neighbours of $\widetilde F$ in $L_k'$ are exactly the former neighbours
of $F$ and $F'$ other than each other; in particular they include all
inner faces previously adjacent to $F'$ across $ux$ or $vx$, whose depths
are at most $d - 2$ by Lemma~\ref{lem:depth-descent} applied to $F'$
(when $d \geq 2$). Thus $\mathrm{depth}(\widetilde F) \leq d - 1$.
For the matching lower bound, every neighbour of $\widetilde F$ has depth
$\geq d - 2$ (neighbours inherited from $F$ have depth $\geq d - 1$;
neighbours inherited from $F'$ have depth $\geq d - 2$). When $d \geq 2$,
neither $F$ nor $F'$ has an outer-cycle edge, so neither does
$\widetilde F$, giving $\mathrm{depth}(\widetilde F) \geq d - 1$. When
$d = 1$, $F' \in \mathcal{B}$ and its outer-cycle edge (necessarily
distinct from the interior edge $uv$) survives on $\partial \widetilde F$,
so $\widetilde F \in \mathcal{B}'$ and
$\mathrm{depth}(\widetilde F) = 0 = d - 1$. In either case
$\mathrm{depth}(\widetilde F) = d - 1$, giving the unique new face
required by~(2).
\textbf{Case (ii): $\{w, x\} \subseteq L_k$.} Then $F = uvw$ and
$F' = uvx$ are triangular faces of $L_k$, and $L_k' = L_k - uv + wx$.
The chord $wx$ splits the quadrilateral $\partial(F \cup F')$ into two
triangular faces $A = uwx$ and $B = vwx$ of $L_k'$. We show
$\mathrm{depth}(A) = \mathrm{depth}(B) = d - 1$.
By symmetry it suffices to handle $A$. The dual neighbours of $A$ in
$L_k'$ are $A_{uw}$ (the inner face across $uw$, unchanged from $L_k$),
$A_{ux}$ (the inner face across $ux$, unchanged), and $B$ (across the
new edge $wx$). By balancedness of $\sigma$ applied to the edge $ux$:
\begin{itemize}
\item if $ux$ lies on the outer cycle of $L_k$, it remains on the outer
cycle of $L_k'$, so $A \in \mathcal{B}'$ and $\mathrm{depth}(A) = 0$
(which equals $d - 1$ because the balanced-with-outer-cycle case forces
$d = 1$); or
\item if $A_{ux}$ is an inner face, balancedness gives
$\mathrm{depth}(A_{ux}) = d - 2$, so
$\mathrm{depth}(A) \leq 1 + (d - 2) = d - 1$.
\end{itemize}
For the lower bound in the second sub-case ($d \geq 2$): $A$'s edges are
$uw$ (an edge of $F$, interior because $F$ has depth $d \geq 1$), $wx$
(new, not on the outer cycle), and $ux$ (interior in this sub-case), so
$A \notin \mathcal{B}'$. Moreover every neighbour of $A$ has depth $\geq
d - 2$: $A_{uw}$ inherits depth $\geq d - 1$ from being a former
neighbour of $F$, $A_{ux}$ has depth $d - 2$, and $B$ has depth $\geq
d - 2$ by the same argument applied symmetrically. Therefore
$\mathrm{depth}(A) \geq d - 1$, and combined with the upper bound,
$\mathrm{depth}(A) = d - 1$.
\end{proof}
\subsection*{When does a balanced surface switch exist?}
For a chord $uv$ of a maximal outerplanar graph, the \emph{span} of $uv$
is the minimum, over the two arcs from $u$ to $v$ on the outer cycle,
of the number of outer-cycle vertices strictly between them.
\begin{observation}
\label{obs:span1-balanced-d1}
For $d = 1$, an inner face $F$ admits a balanced surface switch on some
edge iff at least one edge of $F$ has span $1$ in the outer cycle of
$L_k$. The opposite triangle across that edge -- using the single
outer-cycle vertex between its endpoints -- is then an ear of $F$ in
$\mathcal{B}$, satisfying the $d = 1$ form of
Definition~\ref{def:balanced-surface-switch}.
\end{observation}
The smallest maximal-outerplanar configuration violating this is a
$9$-vertex outer cycle triangulated so that the unique interior face
$F = (0,3,6)$ has spans $(2,2,2)$ on its three edges
(Figure~\ref{fig:no-balanced}). Each depth-$0$ neighbour of $F$ carries
exactly one outer-cycle edge, not two, so none qualifies as an ear of
$F$; no balanced surface switch is available.
\begin{figure}[h]
\centering
\includegraphics[width=0.55\textwidth]{fig_no_balanced_switch.png}
\caption{$9$-vertex maximal outerplanar $L_k$. $F = (0,3,6)$ has
$\mathrm{depth} = 1$ and all three of its edges have span $2$, so none
of $F$'s depth-$0$ neighbours is an ear. No balanced surface switch is
available on $F$.}
\label{fig:no-balanced}
\end{figure}
\subsection*{Preprocessing toward balanced switches}
When $F$ has depth $d \geq 1$ but admits no balanced surface switch,
perform a single (unbalanced) surface switch on any edge of $F$ shared
with a depth-$(d-1)$ neighbour. By Proposition~\ref{prop:balanced-descent}
the result is at least one new depth-$(d-1)$ face; in Case~(ii) it is
accompanied by a new depth-$d$ face $A$ that replaces $F$ as the next
candidate. The hope is that the resulting $A$ admits a balanced
surface switch, or that iterating the preprocessing eventually exposes
one.
\begin{example}
\label{ex:preprocessing}
On the $9$-vertex example, the (unbalanced) surface switch on edge
$uv = 03$ -- with $F' = (0,2,3)$, third vertex $x = 2$, and $w = 6$ --
flips $03 \mapsto 26$ in $G$ and produces $A = (0,2,6)$ at depth $1$.
The new face has spans $(1, 3, 2)$ on its edges, and the ear
$(0,1,2)$ across the span-$1$ edge $02$ is now a balanced
surface-switch target on $A$ (Figure~\ref{fig:preprocessing}).
\end{example}
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{fig_preprocessing.png}
\caption{One step of preprocessing on the $9$-vertex example. Left:
$F = (0,3,6)$ has no edge of span $1$; the chosen surface-switch edge
$uv = 03$ (red) is unbalanced. Right: after the switch $03 \mapsto 26$
(green), the new depth-$1$ face $A = (0,2,6)$ has its edge $02$ (red)
at span $1$, exposing the ear $(0,1,2)$ as a balanced surface-switch
target.}
\label{fig:preprocessing}
\end{figure}
We do not have a general termination theorem. The natural candidate
monovariant for $d = 1$ is the minimum span among edges of the current
depth-$1$ face that are shared with a depth-$0$ neighbour; in
Example~\ref{ex:preprocessing} this drops from $2$ to $1$ in a single
step. Whether such a monovariant strictly decreases under every
unbalanced surface switch -- and a corresponding statement for $d \geq
2$, where balancedness depends on depth-$(d-2)$ structure rather than
just spans -- remains open.
\begin{question}
\label{q:preprocessing-terminates}
Does iterated preprocessing reach a balanced surface switch in finitely
many steps from every initial configuration? Equivalently, is there a
monovariant on the inner-face structure of $L_k$ that strictly decreases
at every unbalanced surface switch on a maximum-depth face?
\end{question}
\end{document}