Redraw n=21 witness figures as crossing-free planar graphs
Replace the radial (crossing-heavy) figure with two crossing-free planar
drawings (networkx planar_layout / Chrobak-Payne):
fig:n21-elgs -- the six witness Even Level Graphs, parity-coloured, with
the bridge-switch-flipped edges dashed red;
fig:n21-duals -- the six resulting duals, with the introduced bridge edges
solid green.
ELG and dual are drawn with independent planar layouts so neither has any
edge crossing (a flip diagonal would otherwise cross other edges when its
quadrilateral is non-convex, which happens for duals 0 and 3). Drop forced
equal aspect so panels fill and labels separate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
"""Draw each of the six Holton-McKay duals as its witness Even Level Graph
|
||||
in a radial-by-level layout (source at centre, level-k vertices on ring k),
|
||||
coloured by parity, with the bridge switches highlighted: removed edges in
|
||||
red (dashed), added edges in green. Reads experiments/witnesses/dual_*.json.
|
||||
"""Draw the six Holton-McKay duals and their witness Even Level Graphs as
|
||||
crossing-free planar drawings (networkx planar_layout, Chrobak-Payne).
|
||||
Two figures:
|
||||
n21_elgs.png -- the six witness Even Level Graphs, parity-coloured,
|
||||
with the edges flipped by the bridge switches dashed red;
|
||||
n21_duals.png -- the six resulting duals, parity-coloured (same fixed
|
||||
parity labelling), with the introduced bridge edges green.
|
||||
Reads experiments/witnesses/dual_*.json. ELG and dual are drawn with
|
||||
independent planar layouts so neither has any edge crossing.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import math
|
||||
sys.path.insert(0, '/Users/didericis/Code/math-research/papers/'
|
||||
'level_resolutions_of_maximal_planar_graphs/experiments')
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -15,98 +19,95 @@ import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.lines import Line2D
|
||||
from test_conjecture import bfs_levels
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
WDIR = os.path.join(HERE, 'witnesses')
|
||||
FDIR = os.path.join(HERE, '..', 'figures')
|
||||
|
||||
EVEN_C = '#9ecae1' # even-level vertices
|
||||
ODD_C = '#fdae6b' # odd-level vertices
|
||||
EVEN_C = '#9ecae1' # even-parity vertices
|
||||
ODD_C = '#fdae6b' # odd-parity vertices
|
||||
|
||||
|
||||
def radial_pos(G, source):
|
||||
levels = bfs_levels(G, frozenset({source}))
|
||||
by_lvl = {}
|
||||
for v, k in levels.items():
|
||||
by_lvl.setdefault(k, []).append(v)
|
||||
pos = {}
|
||||
for k, verts in by_lvl.items():
|
||||
verts = sorted(verts)
|
||||
if k == 0:
|
||||
pos[verts[0]] = (0.0, 0.0)
|
||||
continue
|
||||
m = len(verts)
|
||||
for j, v in enumerate(verts):
|
||||
ang = 2 * math.pi * j / m + (k * 0.6)
|
||||
pos[v] = (k * math.cos(ang), k * math.sin(ang))
|
||||
return pos, levels
|
||||
def load(i):
|
||||
return json.load(open(os.path.join(WDIR, f'dual_{i}.json')))
|
||||
|
||||
|
||||
def draw(dual_index, ax):
|
||||
data = json.load(open(os.path.join(WDIR, f'dual_{dual_index}.json')))
|
||||
src = data['elg_source']
|
||||
def graph_of(edges, labels):
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(int(v) for v in data['labels'])
|
||||
G.add_edges_from((a, b) for a, b in data['elg_edges'])
|
||||
pos, levels = radial_pos(G, src)
|
||||
colors = [EVEN_C if levels[v] % 2 == 0 else ODD_C for v in G.nodes()]
|
||||
G.add_nodes_from(int(v) for v in labels)
|
||||
G.add_edges_from((a, b) for a, b in edges)
|
||||
return G
|
||||
|
||||
removed = {frozenset(s['remove']) for s in data['bridge_switches']}
|
||||
added = [tuple(s['add']) for s in data['bridge_switches']]
|
||||
|
||||
plain = [e for e in G.edges() if frozenset(e) not in removed]
|
||||
def draw(ax, G, labels, highlight, hcolor, hstyle, title):
|
||||
pos = nx.planar_layout(G)
|
||||
colors = [EVEN_C if labels[str(v)] == 0 else ODD_C for v in G.nodes()]
|
||||
hl = {frozenset(e) for e in highlight}
|
||||
plain = [e for e in G.edges() if frozenset(e) not in hl]
|
||||
nx.draw_networkx_edges(G, pos, edgelist=plain, ax=ax,
|
||||
edge_color='#bdbdbd', width=0.8)
|
||||
if removed:
|
||||
nx.draw_networkx_edges(G, pos, edgelist=[tuple(e) for e in removed],
|
||||
ax=ax, edge_color='#d62728', width=2.2,
|
||||
style='dashed')
|
||||
if added:
|
||||
nx.draw_networkx_edges(nx.Graph(added), pos, edgelist=added, ax=ax,
|
||||
edge_color='#2ca02c', width=2.2)
|
||||
nx.draw_networkx_nodes(G, pos, node_color=colors, node_size=210,
|
||||
edge_color='#b0b0b0', width=0.8)
|
||||
if hl:
|
||||
nx.draw_networkx_edges(G, pos, edgelist=[tuple(e) for e in hl], ax=ax,
|
||||
edge_color=hcolor, width=2.4, style=hstyle)
|
||||
nx.draw_networkx_nodes(G, pos, node_color=colors, node_size=200,
|
||||
edgecolors='#444444', linewidths=0.6, ax=ax)
|
||||
nx.draw_networkx_labels(G, pos, font_size=7, ax=ax)
|
||||
k = data['num_bridge_switches']
|
||||
title = (f'dual {dual_index}: ELG (source {src})'
|
||||
+ (f'\n{k} bridge switch' + ('es' if k != 1 else '')
|
||||
if k else '\n(Even Level Graph outright)'))
|
||||
ax.set_title(title, fontsize=9)
|
||||
ax.set_aspect('equal')
|
||||
nx.draw_networkx_labels(G, pos, font_size=8, ax=ax)
|
||||
ax.set_title(title, fontsize=10)
|
||||
ax.margins(0.12)
|
||||
ax.axis('off')
|
||||
|
||||
|
||||
def legend(fig, kind):
|
||||
handles = [
|
||||
Line2D([0], [0], marker='o', color='w', markerfacecolor=EVEN_C,
|
||||
markeredgecolor='#444', markersize=9, label='even parity'),
|
||||
Line2D([0], [0], marker='o', color='w', markerfacecolor=ODD_C,
|
||||
markeredgecolor='#444', markersize=9, label='odd parity'),
|
||||
]
|
||||
if kind == 'elg':
|
||||
handles.append(Line2D([0], [0], color='#d62728', lw=2.4, ls='dashed',
|
||||
label='edge flipped by a bridge switch'))
|
||||
else:
|
||||
handles.append(Line2D([0], [0], color='#2ca02c', lw=2.4,
|
||||
label='bridge edge introduced'))
|
||||
fig.legend(handles=handles, loc='lower center', ncol=3, fontsize=9,
|
||||
frameon=False)
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(FDIR, exist_ok=True)
|
||||
# one combined 2x3 figure, plus individual files
|
||||
fig, axes = plt.subplots(2, 3, figsize=(13, 9))
|
||||
for i, ax in zip(range(6), axes.flat):
|
||||
draw(i, ax)
|
||||
legend = [
|
||||
Line2D([0], [0], marker='o', color='w', markerfacecolor=EVEN_C,
|
||||
markeredgecolor='#444', markersize=9, label='even level'),
|
||||
Line2D([0], [0], marker='o', color='w', markerfacecolor=ODD_C,
|
||||
markeredgecolor='#444', markersize=9, label='odd level'),
|
||||
Line2D([0], [0], color='#d62728', lw=2.2, ls='dashed',
|
||||
label='removed (flipped) edge'),
|
||||
Line2D([0], [0], color='#2ca02c', lw=2.2, label='added (bridge) edge'),
|
||||
]
|
||||
fig.legend(handles=legend, loc='lower center', ncol=4, fontsize=9,
|
||||
frameon=False)
|
||||
fig.tight_layout(rect=(0, 0.04, 1, 1))
|
||||
out = os.path.join(FDIR, 'n21_witnesses.png')
|
||||
fig.savefig(out, dpi=160)
|
||||
print(f'wrote {out}')
|
||||
|
||||
for i in range(6):
|
||||
f, a = plt.subplots(figsize=(5, 5))
|
||||
draw(i, a)
|
||||
f.tight_layout()
|
||||
p = os.path.join(FDIR, f'n21_dual_{i}.png')
|
||||
f.savefig(p, dpi=160)
|
||||
plt.close(f)
|
||||
print(f'wrote {p}')
|
||||
# Figure 1: the six witness Even Level Graphs (flipped edges red dashed)
|
||||
fig, axes = plt.subplots(2, 3, figsize=(19, 12))
|
||||
for i, ax in zip(range(6), axes.flat):
|
||||
d = load(i)
|
||||
G = graph_of(d['elg_edges'], d['labels'])
|
||||
removed = [s['remove'] for s in d['bridge_switches']]
|
||||
k = d['num_bridge_switches']
|
||||
sub = (f"{k} bridge switch" + ("es" if k != 1 else "")
|
||||
if k else "Even Level Graph outright")
|
||||
draw(ax, G, d['labels'], removed, '#d62728', 'dashed',
|
||||
f"dual {i}: ELG (source {d['elg_source']})\n{sub}")
|
||||
legend(fig, 'elg')
|
||||
fig.tight_layout(rect=(0, 0.04, 1, 1))
|
||||
p1 = os.path.join(FDIR, 'n21_elgs.png')
|
||||
fig.savefig(p1, dpi=160)
|
||||
plt.close(fig)
|
||||
print(f'wrote {p1}')
|
||||
|
||||
# Figure 2: the six resulting duals (introduced bridge edges green)
|
||||
fig, axes = plt.subplots(2, 3, figsize=(19, 12))
|
||||
for i, ax in zip(range(6), axes.flat):
|
||||
d = load(i)
|
||||
G = graph_of(d['dual_edges'], d['labels'])
|
||||
added = [s['add'] for s in d['bridge_switches']]
|
||||
draw(ax, G, d['labels'], added, '#2ca02c', 'solid', f"dual {i}")
|
||||
legend(fig, 'dual')
|
||||
fig.tight_layout(rect=(0, 0.04, 1, 1))
|
||||
p2 = os.path.join(FDIR, 'n21_duals.png')
|
||||
fig.savefig(p2, dpi=160)
|
||||
plt.close(fig)
|
||||
print(f'wrote {p2}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user