c947ce75ff
- New paper papers/even_level_graph_generators/: defines Even Level Graph (every level cycle even), derived level graphs, intertwining trees, and the disjunction conjecture (every maximal planar graph is a derived level graph or intertwining tree). Empirically tested through n=11: every iso class is at least an intertwining tree, so the disjunction holds trivially in this range. The intertwining tree disjunct fails at the Tutte graph dual (n=25), so the disjunction becomes non-trivial past some unknown threshold. - Level Switching paper: adds Section 4 (Reachability via edge switches) with the two-step argument (Sleator-Tarjan-Thurston for Case 1; face-merges for Case 2) and Theorem 4.1 (O(n) edge switches suffice to reach all-depth-0). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
159 lines
5.4 KiB
Python
159 lines
5.4 KiB
Python
"""Annotated diagram so the user can answer the v_c-rotation
|
|
clarification questions.
|
|
|
|
Shows the 9-vertex L_k with e_0 = (0, 3) highlighted, both candidate
|
|
v_c vertices (0 and 3) labelled, and the four (v_c, direction) fans
|
|
laid out around each."""
|
|
import os
|
|
import math
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.patches import Polygon, FancyArrowPatch
|
|
|
|
OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)
|
|
|
|
n = 9
|
|
POS = {i: (math.cos(math.radians(90 - i * 360 / n)),
|
|
math.sin(math.radians(90 - i * 360 / n))) for i in range(n)}
|
|
OUTER_EDGES = [(i, (i + 1) % n) for i in range(n)]
|
|
CHORDS = [(0, 2), (0, 3), (3, 5), (3, 6), (0, 6), (6, 8)]
|
|
|
|
# Inner faces
|
|
FACES = {
|
|
(0, 1, 2): 0,
|
|
(0, 2, 3): 0,
|
|
(3, 4, 5): 0,
|
|
(3, 5, 6): 0,
|
|
(6, 7, 8): 0,
|
|
(6, 8, 0): 0,
|
|
(0, 3, 6): 1, # F
|
|
}
|
|
|
|
F_idx_face = (0, 3, 6)
|
|
Fp_face = (0, 2, 3)
|
|
e0 = (0, 3)
|
|
|
|
|
|
def cw_order_around(v):
|
|
"""Sort vertices adjacent to v by clockwise angle (decreasing
|
|
angle from v, starting at angle 90)."""
|
|
adj = set()
|
|
for f in FACES:
|
|
if v in f:
|
|
for u in f:
|
|
if u != v:
|
|
adj.add(u)
|
|
# angle of each adjacent vertex
|
|
def angle(u):
|
|
return (90 - u * 360 / n) % 360
|
|
return sorted(adj, key=lambda u: -angle(u))
|
|
|
|
|
|
fig, axes = plt.subplots(1, 2, figsize=(14, 7))
|
|
|
|
palette = {0: '#86efac', 1: '#fde68a'}
|
|
edge_pal = {0: '#16a34a', 1: '#d97706'}
|
|
|
|
|
|
def draw_base(ax, title):
|
|
# Fill faces by depth
|
|
for face, depth in FACES.items():
|
|
poly = Polygon([POS[v] for v in face], closed=True,
|
|
facecolor=palette[depth], edgecolor=edge_pal[depth],
|
|
linewidth=1.2, alpha=0.5, zorder=0)
|
|
ax.add_patch(poly)
|
|
|
|
# Edges
|
|
for (a, b) in OUTER_EDGES + CHORDS:
|
|
color, lw = '#333', 1.2
|
|
if {a, b} == set(e0):
|
|
color, lw = '#dc2626', 3.4 # e_0 highlighted
|
|
ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
|
|
color=color, linewidth=lw, zorder=1)
|
|
|
|
# Vertices
|
|
for i, (x, y) in POS.items():
|
|
color = '#3b82f6' if i in e0 else '#1f2937'
|
|
size = 470 if i in e0 else 280
|
|
ax.scatter([x], [y], s=size, c=color, edgecolors='black',
|
|
linewidths=1.2, zorder=2)
|
|
ax.text(x, y, str(i), ha='center', va='center',
|
|
fontsize=11 if i in e0 else 9,
|
|
color='white', fontweight='bold', zorder=3)
|
|
|
|
# Annotate F and F'
|
|
cx_F = sum(POS[v][0] for v in F_idx_face) / 3
|
|
cy_F = sum(POS[v][1] for v in F_idx_face) / 3
|
|
ax.text(cx_F, cy_F + 0.1, r'$F$', ha='center', fontsize=14,
|
|
color='#92400e', fontweight='bold', zorder=4)
|
|
ax.text(cx_F, cy_F - 0.1, '(depth 1)', ha='center', fontsize=9,
|
|
color='#92400e', zorder=4)
|
|
|
|
cx_Fp = sum(POS[v][0] for v in Fp_face) / 3
|
|
cy_Fp = sum(POS[v][1] for v in Fp_face) / 3
|
|
ax.text(cx_Fp + 0.1, cy_Fp, r"$F'$" + '\n(depth 0)', ha='center',
|
|
fontsize=10, color='#16a34a', fontweight='bold', zorder=4)
|
|
|
|
# F''s outer-cycle edge: (2, 3)
|
|
px, py = POS[2], POS[3]
|
|
mid = ((px[0] + py[0]) / 2, (px[1] + py[1]) / 2)
|
|
ax.annotate("outer edge of $F'$",
|
|
xy=(mid[0] + 0.05, mid[1] - 0.05),
|
|
xytext=(1.35, 0.5),
|
|
fontsize=9, color='#16a34a',
|
|
arrowprops=dict(arrowstyle='->', color='#16a34a',
|
|
lw=1.0))
|
|
|
|
ax.set_aspect('equal'); ax.axis('off')
|
|
ax.set_xlim(-1.5, 1.7); ax.set_ylim(-1.4, 1.4)
|
|
ax.set_title(title, fontsize=12)
|
|
|
|
|
|
def annotate_fan(ax, vc, label):
|
|
"""Draw arrows around v_c showing CW order of edges."""
|
|
cw_neighbours = cw_order_around(vc)
|
|
# rotate so the e_0 neighbour comes first
|
|
e0_other = [u for u in e0 if u != vc][0]
|
|
idx = cw_neighbours.index(e0_other)
|
|
cw_neighbours = cw_neighbours[idx:] + cw_neighbours[:idx]
|
|
|
|
px, py = POS[vc]
|
|
# Draw a partial arc around v_c
|
|
for i, u in enumerate(cw_neighbours):
|
|
# midpoint between v_c and u, slightly inside
|
|
ux, uy = POS[u]
|
|
midx = px * 0.7 + ux * 0.3
|
|
midy = py * 0.7 + uy * 0.3
|
|
# is edge on outer cycle?
|
|
is_outer = (min(vc, u), max(vc, u)) in OUTER_EDGES or \
|
|
(max(vc, u), min(vc, u)) in OUTER_EDGES
|
|
marker_color = '#1d4ed8' if not is_outer else '#dc2626'
|
|
marker = 'o'
|
|
ax.scatter([midx], [midy], s=120, c=marker_color, marker=marker,
|
|
edgecolors='white', linewidths=1.5, zorder=5)
|
|
ax.text(midx, midy, str(i + 1), ha='center', va='center',
|
|
fontsize=8, color='white', fontweight='bold', zorder=6)
|
|
|
|
# Label
|
|
ax.text(px + 0.05, py + 0.25, label, ha='center',
|
|
fontsize=11, color='#1d4ed8', fontweight='bold')
|
|
|
|
|
|
draw_base(axes[0], r'Option A: $v_c = 0$ (CW order: 1=$(0,3)$, 2=$(0,6)$, 3=$(0,8)$ outer ...)')
|
|
annotate_fan(axes[0], 0, r'$v_c = 0$')
|
|
|
|
draw_base(axes[1], r'Option B: $v_c = 3$ (CW order: 1=$(0,3)$, 2=$(2,3)$ outer ...)')
|
|
annotate_fan(axes[1], 3, r'$v_c = 3$')
|
|
|
|
# Add legend below
|
|
fig.text(0.5, 0.02,
|
|
'Blue circles = chord edges (would be switched). '
|
|
'Red circles = outer-cycle edges (algorithm stops here). '
|
|
'Numbers = clockwise order around $v_c$ starting from $e_0$.',
|
|
ha='center', fontsize=10)
|
|
|
|
fig.tight_layout(rect=[0, 0.04, 1, 1])
|
|
out = os.path.join(OUT_DIR, 'fig_v_c_question.png')
|
|
fig.savefig(out, dpi=180, bbox_inches='tight')
|
|
plt.close(fig)
|
|
print(f'wrote {out}')
|