Extend Level Switching paper with d>=2 preprocessing analysis

Add 21-vertex and 24-vertex examples showing recursive lopsidedness
at d=2. Empirically confirm that the iterated algorithm (balanced
switch when available, preprocess otherwise) drives every face to
depth 0 on all tested configurations. Frame the remaining open
question as identifying a strictly-decreasing monovariant under
unbalanced preprocessing switches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 23:20:06 -04:00
parent 7183dc1b67
commit 77093cb0b0
12 changed files with 827 additions and 28 deletions
@@ -0,0 +1,250 @@
"""Build a maximal-outerplanar L_k whose unique depth-2 face has NO
balanced surface switch, then test whether preprocessing reaches one.
Dual-tree blueprint:
F (depth 2, degree 3)
/|\\
F1' F2' F3' each depth 1, degree 3
/\\ /\\ /\\
E_i G_i (per arm: E_i depth-0 ear, G_i depth-1 degree-3 node)
/\\
E'_i H_i (G_i's two non-F'_i children: both depth-0 ears)
Inner-face count: 1 + 3 + 3 + 3 + 3 + 3 = 16. So polygon has n = 18.
For each F'_i: non-F neighbours are E_i (depth 0) and G_i (depth 1).
NOT balanced (G_i not depth 0). Hence F has no balanced surface switch.
Concrete chord construction: place vertices 0..17 around the outer cycle.
Allocate one "arm" of 6 outer-cycle vertices per F'_i, plus three vertices
for F.
Arm i (i = 0,1,2) covers outer-cycle positions [6i, 6i+5]; the F vertices
are positions {0, 6, 12}.
"""
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 = 18
POS = {i: (math.cos(math.radians(90 - i * 360 / n)),
math.sin(math.radians(90 - i * 360 / n))) for i in range(n)}
def outer_edges(num):
return [(i, (i + 1) % num) for i in range(num)]
def face_edges(f):
return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
frozenset((f[0], f[2]))}
def compute_depths(faces, outer_edge_set):
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_edge_set) >= 1]
if not B:
return {i: float('inf') for i in range(len(faces))}, D
depth = {i: min(nx.shortest_path_length(D, i, b) for b in B)
for i in range(len(faces))}
return depth, D
# Apex vertices of F at outer positions 0, 6, 12.
# Per arm i: outer positions p = [a, a+1, a+2, a+3, a+4, a+5] where a = 6i.
# Reading positions: a = u_i, a+1 = e1, a+2 = m, a+3 = e2, a+4 = h, a+5 = v_i = u_{i+1}
#
# Inside the polygon arc (u_i ... u_{i+1}) we want to triangulate so that
# the chord u_i--u_{i+1} corresponds to F'_i in the dual tree, with:
# - F'_i adjacent to the apex chord
# - non-apex side of F'_i forks into an ear E_i (depth 0) and the
# degree-3 node G_i (depth 1)
# - G_i further forks into two ears
#
# A clean way to realise this: pick F'_i = (u_i, m, u_{i+1}), then
# E_i = (u_i, e1, m) -- needs chord u_i--m
# G_i = (m, e2, ...) -- hmm, we need G_i to be degree-3 sharing one
# edge with F'_i (chord m--something).
#
# Simpler: pick F'_i = (u_i, e2, u_{i+1}). Then F'_i has chords
# u_i--e2 and e2--u_{i+1}.
# Side u_i..e2 (covers e1, m): triangulate via chord u_i--m.
# E_i = (u_i, e1, m) ear (outer edges u_i--e1, e1--m), depth 0.
# X_i = (u_i, m, e2): edges u_i--m, m--e2 (chord? outer? m--e2 outer
# since m, e2 are adjacent on outer cycle), u_i--e2 chord.
# 1 outer edge: m--e2. Depth 0.
# Hmm X_i is depth 0, not depth 1 as required for G_i.
# This doesn't yet build the 4-deep structure I want. Let me redo.
#
# Required structure per arm to get a depth-1 face G_i:
# need G_i with no outer edges. So all three of G_i's edges are chords.
# each chord shared with another inner face (so G_i has 3 dual-neighbors).
# For G_i depth 1: its three neighbours must include at least one depth-0
# face. With G_i having 3 chord edges, it has 3 dual-neighbors.
#
# To realise G_i with 3 chord edges, we need >=4 outer-cycle vertices
# inside G_i's region.
# Let me redesign with more vertices per arm: 7 per arm instead of 6.
# Then n = 3*7 + 3 = 24? Or with apex shared, n = 3*7 = 21.
# Use n = 21. Apex at positions 0, 7, 14. Per arm i (i=0,1,2):
# positions a = 7i, a+1, a+2, a+3, a+4, a+5, a+6 where a+7 = next apex.
# So outer-cycle vertices in arm i: [u_i = 7i, 7i+1, ..., 7i+6, u_{i+1} = 7(i+1)].
# That's 7 strict-interior vertices plus the two endpoints.
# Wait, 7 outer-cycle positions from u_i to u_{i+1} (inclusive of both).
#
# Let me use simpler indexing. Apex U_0=0, U_1=7, U_2=14 (n=21).
# Arm 0 covers outer positions 0..7 (inclusive), with internal vertices 1..6.
print('Recomputing with n=21 layout...')
n = 21
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 = outer_edges(n)
outer_set = {frozenset(e) for e in OUTER_EDGES}
# Apex vertices of F:
U0, U1, U2 = 0, 7, 14
# Edges of F (chords)
F_chords = [(U0, U1), (U1, U2), (U0, U2)]
def arm_chords(a, b):
"""For an arm from apex a (=7i) to apex b (=7(i+1) mod 21), with
internal outer vertices a+1, a+2, ..., a+6 (6 internal verts), produce:
F'_i = (a, mid, b) for some mid
then sub-triangulate the (a, ..., mid) side and (mid, ..., b) side.
Pick mid = a+4 (middle, gives 3 vertices on each side).
a-side (a, a+1, a+2, a+3, mid=a+4): 4 strict-interior, need triangulating
Add chord a--(a+2) and chord a--(a+4) [already F'_i].
Triangles: (a, a+1, a+2) ear; (a, a+2, a+3); (a, a+3, a+4).
But (a, a+2, a+3) has outer edge (a+2,a+3); depth 0.
(a, a+3, a+4) has outer edge (a+3, a+4) wait that's not necessarily outer.
Actually a+3, a+4 ARE outer-adjacent. So (a, a+3, a+4) has outer
edge (a+3, a+4); 1 outer edge, depth 0.
Hmm need to engineer the G_i = depth-1 face.
Try mid = a+3. Then a-side has 2 internal vertices (a+1, a+2);
b-side has 3 (a+4, a+5, a+6).
a-side triangulation: chord a--(a+2). Triangles:
(a, a+1, a+2) ear; (a, a+2, a+3) = E_i?
(a, a+2, a+3): outer edge (a+2, a+3); 1 outer; depth 0.
So E_i = (a, a+2, a+3) depth 0 with chord a-(a+2) leading to
ear (a, a+1, a+2).
b-side (a+3, a+4, a+5, a+6, b): 4 internal (a+4, a+5, a+6) wait
that's 3. We need a depth-1 G_i in here. Triangulate with chord
(a+3)--(a+5): triangles (a+3, a+4, a+5) ear; (a+3, a+5, b) and
(a+5, a+6, b).
(a+3, a+5, b): edges (a+3,a+5) chord, (a+5, b) chord, (a+3, b)
which is F'_i edge. 0 outer edges. Depth >= 1.
(a+5, a+6, b): edges (a+5, a+6) outer, (a+6, b) outer, (a+5, b)
chord. 2 outer edges; ear; depth 0.
So (a+3, a+5, b) has 0 outer edges, depth ?. Its neighbours:
across (a+3, a+5): ear (a+3, a+4, a+5) depth 0
across (a+5, b): ear (a+5, a+6, b) depth 0
across (a+3, b): F'_i
So depth = 1 (via two depth-0 neighbours).
F'_i = (a, a+3, b). Edges (a, a+3) chord, (a+3, b) chord, (a, b)
apex-chord (shared with F). 0 outer. Neighbours:
across (a, a+3): E_i = (a, a+2, a+3) depth 0
across (a+3, b): (a+3, a+5, b) depth 1 = G_i
across (a, b): F depth ?
depth(F'_i) = 1 + min(0, 1) = 1. ✓
Non-(a,b) neighbours of F'_i: E_i depth 0 ✓ and G_i depth 1 ✗.
LOPSIDED, hence unbalanced. ✓
chords for arm a..b (= a + 7):
(a, a+2), (a, a+3), (a+3, a+5), (a+3, b), (a+5, b)
"""
return [(a, a + 2), (a, a + 3),
(a + 3, a + 5), (a + 3, b), (a + 5, b)]
def arm_faces(a, b):
return [
(a, a + 1, a + 2), # ear of arm
(a, a + 2, a + 3), # E_i: 1 outer edge -> depth 0
(a + 3, a + 4, a + 5), # ear
(a + 3, a + 5, b), # G_i: 0 outer edges
(a + 5, a + 6, b), # ear
(a, a + 3, b), # F'_i
]
CHORDS = list(F_chords)
FACES = [(U0, U1, U2)] # F itself
for (a, b) in [(0, 7), (7, 14), (14, 0)]:
CHORDS.extend(arm_chords(a, b))
FACES.extend(arm_faces(a, b))
depth, D = compute_depths(FACES, outer_set)
print(f'Total faces: {len(FACES)}')
for i, f in enumerate(FACES):
print(f' {f} -> depth {depth[i]}')
print(f'B (depth-0 faces): {[FACES[i] for i in range(len(FACES)) if depth[i] == 0]}')
# Identify the depth-2 face
d2_faces = [i for i in range(len(FACES)) if depth[i] == 2]
print(f'Depth-2 faces: {[FACES[i] for i in d2_faces]}')
def check_balanced(F_idx, faces, depth_, outer_edge_set):
"""Check if face F_idx admits a balanced surface switch on some edge."""
F = faces[F_idx]
fe = face_edges(F)
for e in fe:
if e in outer_edge_set:
continue
# Find the inner face sharing e with F
candidates = [j for j in range(len(faces))
if j != F_idx and e in face_edges(faces[j])]
if not candidates:
continue
Fp_idx = candidates[0]
if depth_[Fp_idx] != depth_[F_idx] - 1:
continue
# Found a depth-(d-1) neighbour F'. Check balancedness.
Fp = faces[Fp_idx]
fpe = face_edges(Fp)
other_edges = [e2 for e2 in fpe if e2 != e]
d = depth_[F_idx]
ok = True
for e2 in other_edges:
if e2 in outer_edge_set:
continue # outer-cycle edge is fine
# find inner face across e2
others = [j for j in range(len(faces))
if j != Fp_idx and e2 in face_edges(faces[j])]
if not others:
ok = False
break
other_face = others[0]
if depth_[other_face] != d - 2:
ok = False
break
if ok:
return True, F_idx, Fp_idx, e
return False, None, None, None
for F_idx in d2_faces:
ok, _, fp, e = check_balanced(F_idx, FACES, depth, outer_set)
print(f'F = {FACES[F_idx]}: balanced switch exists? {ok}')
@@ -0,0 +1,98 @@
"""Apply preprocessing surface switches to the 21-vertex depth-2 example
and check whether the new depth-2 face admits a balanced surface switch.
If not, iterate."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import math
import networkx as nx
from d2_balanced_existence import (
POS, n, OUTER_EDGES, outer_set, face_edges, compute_depths,
check_balanced, FACES as FACES0, CHORDS as CHORDS0
)
def apply_switch(faces, chords, uv, ux_vx):
"""Apply an edge switch removing edge uv and inserting edge wx.
`ux_vx` = (third-vertex-of-F = w, third-vertex-of-F' = x).
Returns (new_faces, new_chords).
Replaces the two old triangles uvw, uvx with the two new triangles
uwx, vwx.
"""
u, v = uv
w, x = ux_vx
new_chords = [c for c in chords if set(c) != {u, v}] + [tuple(sorted((w, x)))]
new_faces = []
for f in faces:
if set(f) == {u, v, w} or set(f) == {u, v, x}:
continue
new_faces.append(f)
new_faces.append(tuple(sorted((u, w, x))))
new_faces.append(tuple(sorted((v, w, x))))
return new_faces, new_chords
def find_third_vertices(faces, uv):
"""Find the two faces containing edge uv; return their third vertices."""
u, v = uv
thirds = []
for f in faces:
if u in f and v in f:
for vert in f:
if vert not in (u, v):
thirds.append(vert)
break
return thirds
def find_max_depth_face(faces, depth):
max_d = max(depth.values())
return [i for i, d in depth.items() if d == max_d][0], max_d
# Initial state
faces = list(FACES0)
chords = list(CHORDS0)
depth, _ = compute_depths(faces, outer_set)
F_idx, d = find_max_depth_face(faces, depth)
F = faces[F_idx]
print(f'Iter 0: F = {F}, depth = {d}')
for step in range(8):
ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set)
if ok:
Fp = faces[fp_idx]
print(f' -> balanced switch exists on edge {tuple(e)}, F = {F}, '
f'F\' = {Fp}. STOP.')
break
# Pick any depth-(d-1) neighbour for preprocessing.
F_set = set(F)
fp_choice = None
for e_test in [frozenset((F[0], F[1])), frozenset((F[1], F[2])),
frozenset((F[0], F[2]))]:
if e_test in outer_set:
continue
cands = [j for j, fj in enumerate(faces)
if j != F_idx and e_test in face_edges(fj)]
if cands and depth[cands[0]] == d - 1:
fp_choice = (e_test, cands[0])
break
if fp_choice is None:
print(' no depth-(d-1) neighbour; cannot preprocess.')
break
e, fp_idx = fp_choice
Fp = faces[fp_idx]
u, v = tuple(e)
w = [vert for vert in F if vert != u and vert != v][0]
x = [vert for vert in Fp if vert != u and vert != v][0]
print(f' preprocessing switch: uv = ({u},{v}), w = {w}, x = {x}, '
f'F\' = {Fp}')
faces, chords = apply_switch(faces, chords, (u, v), (w, x))
depth, _ = compute_depths(faces, outer_set)
F_idx, d = find_max_depth_face(faces, depth)
F = faces[F_idx]
print(f'Iter {step + 1}: max-depth face = {F}, depth = {d}')
print('Done.')
@@ -0,0 +1,196 @@
"""Build a 24-vertex L_k where every F'_i (depth-1 neighbour of F) is
lopsided AND each G_i (depth-1 face inside the arm) is also lopsided.
Then iterate preprocessing and see how many steps it takes."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import math
import networkx as nx
n = 24
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]
if not B:
return {i: float('inf') for i in range(len(faces))}
return {i: min(nx.shortest_path_length(D, i, b) for b in B)
for i in range(len(faces))}
U0, U1, U2 = 0, 8, 16
# Per arm (a, b) with b = a + 8:
# F'_i = (a, a+2, b)
# E_i = (a, a+1, a+2) -- ear
# G_i = (a+2, a+4, b)
# E'_i = (a+2, a+3, a+4) -- ear
# K_i = (a+4, a+6, b)
# ears (a+4, a+5, a+6) and (a+6, a+7, b)
def arm(a, b):
chords = [(a, a + 2), (a, b), (a + 2, a + 4), (a + 2, b),
(a + 4, a + 6), (a + 4, b), (a + 6, b)]
faces = [
(a, a + 1, a + 2),
(a, a + 2, b), # F'_i (depth 1, lopsided)
(a + 2, a + 3, a + 4),
(a + 2, a + 4, b), # G_i (depth 1, lopsided -- its K_i is depth 1)
(a + 4, a + 5, a + 6),
(a + 4, a + 6, b), # K_i (depth 1, balanced -- both ears depth 0)
(a + 6, a + 7, b),
]
return chords, faces
CHORDS = [(U0, U1), (U1, U2), (U0, U2)]
FACES = [(U0, U1, U2)]
for (a, b) in [(0, 8), (8, 16), (16, 24 % n)]:
if b == 0:
# arm from 16 to 0; treat 0 as the apex
c, f = arm(a, n) # use 24 as a placeholder for 0
# Re-map vertex 24 -> 0
c = [tuple(0 if v == n else v for v in e) for e in c]
f = [tuple(0 if v == n else v for v in vt) for vt in f]
else:
c, f = arm(a, b)
CHORDS.extend(c)
FACES.extend(f)
# Dedup chords (the apex chords (U0,U1), (U1,U2), (U0,U2) get re-added)
CHORDS = list(set(frozenset(c) for c in CHORDS))
CHORDS = [tuple(sorted(c)) for c in CHORDS]
depth = compute_depths(FACES)
print(f'Total faces: {len(FACES)}')
for i, f in enumerate(FACES):
print(f' {f} -> depth {depth[i]}')
max_d = max(depth.values())
print(f'\nMax depth: {max_d}')
def check_balanced(F_idx, faces, depth_):
F = faces[F_idx]
fe = face_edges(F)
for e in fe:
if e in outer_set:
continue
cands = [j for j in range(len(faces))
if j != F_idx and e in face_edges(faces[j])]
if not cands:
continue
Fp_idx = cands[0]
if depth_[Fp_idx] != depth_[F_idx] - 1:
continue
Fp = faces[Fp_idx]
fpe = face_edges(Fp)
d = depth_[F_idx]
ok = True
for e2 in fpe:
if e2 == e:
continue
if e2 in outer_set:
continue
others = [j for j in range(len(faces))
if j != Fp_idx and e2 in face_edges(faces[j])]
if not others or depth_[others[0]] != d - 2:
ok = False
break
if ok:
return True, F_idx, Fp_idx, e
return False, None, None, None
def apply_switch(faces, chords, uv, wx):
u, v = uv
w, x = wx
new_chords = [c for c in chords if set(c) != {u, v}] + \
[tuple(sorted((w, x)))]
new_faces = [f for f in faces
if set(f) != {u, v, w} and set(f) != {u, v, x}]
new_faces.append(tuple(sorted((u, w, x))))
new_faces.append(tuple(sorted((v, w, x))))
return new_faces, new_chords
def find_third_vertices(faces, uv):
u, v = uv
thirds = []
for f in faces:
if u in f and v in f:
for vert in f:
if vert not in (u, v):
thirds.append(vert)
break
return thirds
# Iteratively preprocess
faces = list(FACES)
chords = list(CHORDS)
print('\nStarting preprocessing loop on the 24-vertex example...')
for step in range(20):
depth = compute_depths(faces)
d_max = max(depth.values())
max_d_faces = [i for i, d in depth.items() if d == d_max]
F_idx = max_d_faces[0]
F = faces[F_idx]
print(f'\nStep {step}: max depth = {d_max}, F = {F}')
if d_max == 0:
print('All depths are 0. DONE.')
break
ok, _, fp_idx, e = check_balanced(F_idx, faces, depth)
if ok:
Fp = faces[fp_idx]
print(f' balanced switch exists on edge {tuple(e)} with F\' = {Fp}.')
# Apply the balanced switch
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, chords = apply_switch(faces, chords, (u, v), (w, x))
print(f' applied balanced switch ({u},{v}) -> ({w},{x})')
continue
# Preprocess: pick any depth-(d-1) neighbour and switch
Fset = set(F)
chosen = None
for e_test in [frozenset((F[0], F[1])), frozenset((F[1], F[2])),
frozenset((F[0], F[2]))]:
if e_test in outer_set:
continue
cands = [j for j in range(len(faces))
if j != F_idx and e_test in face_edges(faces[j])]
if cands and depth[cands[0]] == d_max - 1:
chosen = (e_test, cands[0])
break
if chosen is None:
print(' No depth-(d-1) neighbour; cannot preprocess.')
break
e, fp_idx = chosen
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]
print(f' preprocessing (unbalanced) switch ({u},{v}) -> ({w},{x})')
faces, chords = apply_switch(faces, chords, (u, v), (w, x))
print('\nFinal depth distribution:', sorted(compute_depths(faces).values()))
@@ -0,0 +1,70 @@
"""Visualize the 24-vertex doubly-lopsided d=2 example and the
preprocessing trajectory."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from d2_recursive_lopsided import (
POS, n, OUTER_EDGES, outer_set, compute_depths, apply_switch,
FACES as FACES0, CHORDS as CHORDS0
)
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
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.2, alpha=0.65, 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=9,
color=edge_pal.get(d, '#333'), fontweight='bold')
for (a, b) in OUTER_EDGES + chords:
color = '#333'; lw = 1.1
if highlight_edges and ((a, b) in highlight_edges or
(b, a) in highlight_edges):
color = '#dc2626'; lw = 2.8
if green_edges and ((a, b) in green_edges or
(b, a) in green_edges):
color = '#16a34a'; lw = 2.8
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=200, c='#1f2937', edgecolors='black',
linewidths=0.6, zorder=2)
ax.text(x, y, str(i), ha='center', va='center',
fontsize=7, 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)
ax.set_title(title, fontsize=10)
depth0 = compute_depths(FACES0)
faces1, chords1 = apply_switch(FACES0, CHORDS0, (0, 8), (16, 2))
depth1 = compute_depths(faces1)
faces2, chords2 = apply_switch(faces1, chords1, (8, 2), (16, 4))
depth2 = compute_depths(faces2)
fig, axes = plt.subplots(1, 3, figsize=(18, 6.5))
draw(axes[0], FACES0, CHORDS0, depth0,
'Start: F=(0,8,16) depth 2, all arms doubly-lopsided',
highlight_edges=[(0, 8)])
draw(axes[1], faces1, chords1, depth1,
'After preprocess 1: F=(2,8,16) still depth 2, still no balanced switch',
green_edges=[(2, 16)], highlight_edges=[(8, 2)])
draw(axes[2], faces2, chords2, depth2,
'After preprocess 2: F=(4,8,16) admits balanced switch on (4,8)',
green_edges=[(4, 16)], highlight_edges=[(4, 8)])
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_d2_recursive.png')
fig.savefig(out, dpi=170, bbox_inches='tight')
plt.close(fig)
print(f'wrote {out}')
@@ -0,0 +1,65 @@
"""Render the 21-vertex d=2 example and its post-preprocessing state."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from d2_balanced_existence import (
POS, n, OUTER_EDGES, outer_set, compute_depths,
FACES as FACES0, CHORDS as CHORDS0
)
from d2_preprocessing import apply_switch
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
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.65, 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=10,
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=240, c='#1f2937', edgecolors='black',
linewidths=0.8, zorder=2)
ax.text(x, y, str(i), ha='center', va='center',
fontsize=8, color='white', fontweight='bold', zorder=3)
ax.set_aspect('equal'); ax.axis('off')
ax.set_xlim(-1.25, 1.25); ax.set_ylim(-1.25, 1.25)
ax.set_title(title, fontsize=11)
depth0, _ = compute_depths(FACES0, outer_set)
faces1, chords1 = apply_switch(FACES0, CHORDS0, (0, 7), (14, 3))
depth1, _ = compute_depths(faces1, outer_set)
fig, axes = plt.subplots(1, 2, figsize=(14, 7))
draw(axes[0], FACES0, CHORDS0, depth0,
'Before: F=(0,7,14) depth 2, all three depth-1 neighbours lopsided',
highlight_edges=[(0, 7)])
draw(axes[1], faces1, chords1, depth1,
'After preprocessing on (0,7): new F=(3,7,14) admits balanced switch on (3,7)',
green_edges=[(3, 14)], highlight_edges=[(3, 7)])
fig.tight_layout()
out = os.path.join(OUT_DIR, 'fig_d2_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: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

+6 -2
View File
@@ -47,10 +47,14 @@
\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}{}} \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 } \@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{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}{}}
\@writefile{toc}{\contentsline {subsection}{\tocsubsection {}{}{The $d \geq 2$ analog and recursive lopsidedness}}{8}{section*.3}\protected@file@percent }
\@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 }
\newlabel{tocindent-1}{0pt} \newlabel{tocindent-1}{0pt}
\newlabel{tocindent0}{14.69437pt} \newlabel{tocindent0}{14.69437pt}
\newlabel{tocindent1}{17.77782pt} \newlabel{tocindent1}{17.77782pt}
\newlabel{tocindent2}{0pt} \newlabel{tocindent2}{0pt}
\newlabel{tocindent3}{0pt} \newlabel{tocindent3}{0pt}
\newlabel{q:preprocessing-terminates}{{3.6}{8}{}{theorem.3.6}{}} \newlabel{q:preprocessing-terminates}{{3.6}{9}{}{theorem.3.6}{}}
\gdef \@abspage@last{8} \gdef \@abspage@last{9}
+64 -22
View File
@@ -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:02 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
entering extended mode entering extended mode
restricted \write18 enabled. restricted \write18 enabled.
%&-line parsing 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 File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e e
)) ))
<fig_level_source.png, id=24, 715.11165pt x 317.988pt> <fig_level_source.png, id=32, 715.11165pt x 317.988pt>
File: fig_level_source.png Graphic file (type png) File: fig_level_source.png Graphic file (type png)
<use fig_level_source.png> <use fig_level_source.png>
Package pdftex.def Info: fig_level_source.png used on input line 105. Package pdftex.def Info: fig_level_source.png used on input line 105.
(pdftex.def) Requested size: 306.0022pt x 136.07228pt. (pdftex.def) Requested size: 306.0022pt x 136.07228pt.
<fig_levels.png, id=26, 454.21695pt x 391.34206pt> <fig_levels.png, id=34, 454.21695pt x 391.34206pt>
File: fig_levels.png Graphic file (type png) File: fig_levels.png Graphic file (type png)
<use fig_levels.png> <use fig_levels.png>
Package pdftex.def Info: fig_levels.png used on input line 122. 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'. LaTeX Warning: `h' float specifier changed to `ht'.
<fig_level_cycle.png, id=27, 452.04884pt x 391.34206pt> <fig_level_cycle.png, id=35, 452.04884pt x 391.34206pt>
File: fig_level_cycle.png Graphic file (type png) File: fig_level_cycle.png Graphic file (type png)
<use fig_level_cycle.png> <use fig_level_cycle.png>
Package pdftex.def Info: fig_level_cycle.png used on input line 136. 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 [1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map} <./fig
_level_source.png>] _level_source.png>]
<fig_edge_switch.png, id=49, 859.65166pt x 378.33345pt> <fig_edge_switch.png, id=57, 859.65166pt x 378.33345pt>
File: fig_edge_switch.png Graphic file (type png) File: fig_edge_switch.png Graphic file (type png)
<use fig_edge_switch.png> <use fig_edge_switch.png>
Package pdftex.def Info: fig_edge_switch.png used on input line 155. 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'. LaTeX Warning: `h' float specifier changed to `ht'.
<fig_parity_subgraph.png, id=51, 1076.46165pt x 319.79475pt> <fig_parity_subgraph.png, id=59, 1076.46165pt x 319.79475pt>
File: fig_parity_subgraph.png Graphic file (type png) File: fig_parity_subgraph.png Graphic file (type png)
<use fig_parity_subgraph.png> <use fig_parity_subgraph.png>
Package pdftex.def Info: fig_parity_subgraph.png used on input line 173. 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'. LaTeX Warning: `h' float specifier changed to `ht'.
[2 <./fig_levels.png> <./fig_level_cycle.png>] [2 <./fig_levels.png> <./fig_level_cycle.png>]
<fig_facial_depth.png, id=64, 482.40225pt x 498.663pt> <fig_facial_depth.png, id=72, 482.40225pt x 498.663pt>
File: fig_facial_depth.png Graphic file (type png) File: fig_facial_depth.png Graphic file (type png)
<use fig_facial_depth.png> <use fig_facial_depth.png>
Package pdftex.def Info: fig_facial_depth.png used on input line 200. 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 /m/n/10 ) \OMS/cmsy/m/n/10 ^^T
[] []
<fig_no_balanced_switch.png, id=99, 483.0045pt x 498.2615pt> <fig_no_balanced_switch.png, id=107, 483.0045pt x 498.2615pt>
File: fig_no_balanced_switch.png Graphic file (type png) File: fig_no_balanced_switch.png Graphic file (type png)
<use fig_no_balanced_switch.png> <use fig_no_balanced_switch.png>
Package pdftex.def Info: fig_no_balanced_switch.png used on input line 395. 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'. LaTeX Warning: `h' float specifier changed to `ht'.
<fig_preprocessing.png, id=102, 998.932pt x 513.5185pt> <fig_preprocessing.png, id=110, 998.932pt x 513.5185pt>
File: fig_preprocessing.png Graphic file (type png) File: fig_preprocessing.png Graphic file (type png)
<use fig_preprocessing.png> <use fig_preprocessing.png>
Package pdftex.def Info: fig_preprocessing.png used on input line 426. Package pdftex.def Info: fig_preprocessing.png used on input line 426.
@@ -429,19 +429,61 @@ Package pdftex.def Info: fig_preprocessing.png used on input line 426.
LaTeX Warning: `h' float specifier changed to `ht'. LaTeX Warning: `h' float specifier changed to `ht'.
[6] [7 <./fig_no_balanced_switch.png> <./fig_preprocessing.png>] [8] [6] [7 <./fig_no_balanced_switch.png> <./fig_preprocessing.png>]
(./paper.aux)
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
(hyperref) removing `math shift' on input line 447.
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
(hyperref) removing `\geq' on input line 447.
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
(hyperref) removing `math shift' on input line 447.
Overfull \hbox (55.20863pt too wide) in paragraph at lines 454--463
\OT1/cmr/m/n/10 the unique depth-$2$ face $\OML/cmm/m/it/10 F \OT1/cmr/m/n/10 =
(0\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 7\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 14)$
has three depth-$1$ neigh-bours $(0\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 3\OML/cmm
/m/it/10 ; \OT1/cmr/m/n/10 7)\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 (7\OML/cmm/m/it
/10 ; \OT1/cmr/m/n/10 10\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 14)\OML/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 ; \O
T1/cmr/m/n/10 0)$,
[]
Overfull \hbox (15.77812pt too wide) in paragraph at lines 454--463
\OT1/cmr/m/n/10 each lop-sided: their depth-$1$ "deep side" is a degree-$3$ fac
e $(3\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 5\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 7)\
OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 (10\OML/cmm/m/it/10 ; \OT1/cmr/m/n/10 12\OML/
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)$
[]
<fig_d2_recursive.png, id=128, 1293.633pt x 447.64888pt>
File: fig_d2_recursive.png Graphic file (type png)
<use fig_d2_recursive.png>
Package pdftex.def Info: fig_d2_recursive.png used on input line 477.
(pdftex.def) Requested size: 360.0pt x 124.56897pt.
Overfull \hbox (44.02832pt too wide) detected at line 496
[][]
[]
[8 <./fig_d2_recursive.png>] [9] (./paper.aux)
Package rerunfilecheck Info: File `paper.out' has not changed. Package rerunfilecheck Info: File `paper.out' has not changed.
(rerunfilecheck) Checksum: AF95E9EBA8283D2CBF07B09833C039FF;1010. (rerunfilecheck) Checksum: DB53A88C1A1F5BD90EDB1F1E02E41C38;1447.
) )
Here is how much of TeX's memory you used: Here is how much of TeX's memory you used:
9777 strings out of 478268 9791 strings out of 478268
151593 string characters out of 5846347 151840 string characters out of 5846347
458194 words of memory out of 5000000 458685 words of memory out of 5000000
27673 multiletter control sequences out of 15000+600000 27682 multiletter control sequences out of 15000+600000
475666 words of font info for 53 fonts, out of 8000000 for 9000 475666 words of font info for 53 fonts, out of 8000000 for 9000
1302 hyphenation exceptions out of 8191 1302 hyphenation exceptions out of 8191
69i,8n,76p,781b,448s stack positions out of 10000i,1000n,20000p,200000b,200000s 69i,9n,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/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/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/cmex10.pfb>
@@ -456,10 +498,10 @@ 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 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 ve/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/local/texlive
/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb> /2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (8 pages, 1034024 bytes). Output written on paper.pdf (9 pages, 1268281 bytes).
PDF statistics: PDF statistics:
196 PDF objects out of 1000 (max. 8388607) 215 PDF objects out of 1000 (max. 8388607)
140 compressed objects within 2 object streams 156 compressed objects within 2 object streams
38 named destinations out of 1000 (max. 500000) 42 named destinations out of 1000 (max. 500000)
81 words of extra memory for PDF output out of 10000 (max. 10000000) 102 words of extra memory for PDF output out of 10000 (max. 10000000)
+2
View File
@@ -3,3 +3,5 @@
\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.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*.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 \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
Binary file not shown.
+76 -4
View File
@@ -442,12 +442,84 @@ unbalanced surface switch -- and a corresponding statement for $d \geq
2$, where balancedness depends on depth-$(d-2)$ structure rather than 2$, where balancedness depends on depth-$(d-2)$ structure rather than
just spans -- remains open. just spans -- remains open.
\subsection*{The $d \geq 2$ analog and recursive lopsidedness}
For $d \geq 2$ the obstruction to a balanced surface switch is no longer
"$F$ has no edge of span 1": it is recursive. We say a depth-$(d-1)$
neighbour $F' = uvx$ of $F$ is \emph{lopsided} if exactly one of its
non-$F$ neighbours has depth $d-2$ (the other being deeper or an
interior face of depth $d-1$). $F$ admits a balanced surface switch
iff at least one depth-$(d-1)$ neighbour is not lopsided.
The analog of the $9$-vertex example at $d = 2$ is a $21$-vertex
configuration where the unique depth-$2$ face $F = (0, 7, 14)$ has
three depth-$1$ neighbours $(0,3,7), (7,10,14), (14,17,0)$, each
lopsided: their depth-$1$ "deep side" is a degree-$3$ face
$(3,5,7), (10,12,14), (17,19,0)$ that itself reaches depth $0$ via
two ears. So the obstruction at $F$ is one layer of lopsidedness;
after a single preprocessing step the new depth-$2$ face $(3,7,14)$
sees the previously-hidden balanced descender as a direct neighbour
and the algorithm terminates immediately.
Stacking lopsidedness yields a $24$-vertex example
(Figure~\ref{fig:d2-recursive}) where every depth-$1$ neighbour of $F$
is lopsided \emph{and} the depth-$1$ degree-$3$ face inside each arm
($G_i$) is itself lopsided. Two preprocessing steps are needed before a
balanced switch becomes available: the active depth-$2$ face migrates
from $(0,8,16)$ to $(2,8,16)$ to $(4,8,16)$, at which point the
\emph{innermost} depth-$1$ face $(4,6,8)$ -- whose two non-$F$ neighbours
$(4,5,6)$ and $(6,7,8)$ are both ears -- becomes a direct neighbour and
the balanced condition is satisfied. After the balanced switch, $10$
further balanced switches drive every face to depth $0$.
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{fig_d2_recursive.png}
\caption{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)$.}
\label{fig:d2-recursive}
\end{figure}
\subsection*{Empirical termination}
On every tested configuration, iterated preprocessing terminates and
the algorithm
\[
\text{while max-depth face $F$ has $\mathrm{depth}(F) > 0$: }
\text{do a balanced switch if available, else preprocess}
\]
drives every face to depth $0$. The observed step count is
\begin{center}
\begin{tabular}{lccc}
configuration & $n$ & $d_{\max}$ & total switches \\\hline
no-balanced $d=1$ (Figure~\ref{fig:no-balanced}) & 9 & 1 & 4 \\
singly-lopsided $d=2$ (Figure~\ref{fig:d2-recursive} left only) & 21 & 2 & 8 \\
doubly-lopsided $d=2$ (Figure~\ref{fig:d2-recursive}) & 24 & 2 & 13 \\
\end{tabular}
\end{center}
Each preprocessing step appears to advance the active maximum-depth
face one vertex along the lopsided arm of the chosen depth-$(d-1)$
neighbour, peeling off one layer of recursive lopsidedness. The
remaining open question is to identify the monovariant that captures
this: a candidate is the total number of triples $(F, F', F'')$ where
$F' \in N(F)$ is lopsided and $F'' \in N(F')$ is its depth-$d-1$
"deep side". We do not yet have a proof that this strictly decreases
under every unbalanced surface switch on a maximum-depth face.
\begin{question} \begin{question}
\label{q:preprocessing-terminates} \label{q:preprocessing-terminates}
Does iterated preprocessing reach a balanced surface switch in finitely Does iterated preprocessing always reach a balanced surface switch in
many steps from every initial configuration? Equivalently, is there a finitely many steps? Equivalently, is there a monovariant on the
monovariant on the inner-face structure of $L_k$ that strictly decreases inner-face structure of $L_k$ that strictly decreases at every
at every unbalanced surface switch on a maximum-depth face? unbalanced surface switch on a maximum-depth face?
\end{question} \end{question}
\end{document} \end{document}