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:
2026-05-22 11:23:36 -04:00
parent 7034f21ad8
commit b3998fbdb3
16 changed files with 140 additions and 103 deletions
@@ -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__':
Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 KiB

+7 -1
View File
@@ -49,5 +49,11 @@
\newlabel{tocindent1}{17.77782pt}
\newlabel{tocindent2}{0pt}
\newlabel{tocindent3}{0pt}
\@writefile{lot}{\contentsline {table}{\numberline {1}{\ignorespaces The six Holton--McKay duals at $n = 21$, the first triangulations that are not intertwining trees. Each is a bridge-derived level graph: duals $1$ and $2$ are Even Level Graphs outright (zero switches), and the remaining four reach an Even Level Graph in $1$--$4$ bridge switches. All witnesses are step-verified.}}{6}{table.1}\protected@file@percent }
\newlabel{tab:n21}{{1}{6}{The six Holton--McKay duals at $n = 21$, the first triangulations that are not intertwining trees. Each is a bridge-derived level graph: duals $1$ and $2$ are Even Level Graphs outright (zero switches), and the remaining four reach an Even Level Graph in $1$--$4$ bridge switches. All witnesses are step-verified}{table.1}{}}
\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{6}{section*.3}\protected@file@percent }
\gdef \@abspage@last{6}
\@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces The witness Even Level Graph for each of the six Holton--McKay duals, drawn as a crossing-free planar graph and coloured by parity (blue even, orange odd, with respect to the fixed level-parity labelling). The dashed red edges are the same-parity edges that the bridge switches flip; flipping them yields the corresponding dual in Figure\nonbreakingspace \ref {fig:n21-duals}. Duals $1$ and $2$ are Even Level Graphs outright, so no edge is flipped.}}{7}{figure.5}\protected@file@percent }
\newlabel{fig:n21-elgs}{{5}{7}{The witness Even Level Graph for each of the six Holton--McKay duals, drawn as a crossing-free planar graph and coloured by parity (blue even, orange odd, with respect to the fixed level-parity labelling). The dashed red edges are the same-parity edges that the bridge switches flip; flipping them yields the corresponding dual in Figure~\ref {fig:n21-duals}. Duals $1$ and $2$ are Even Level Graphs outright, so no edge is flipped}{figure.5}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces The six Holton--McKay duals, drawn as crossing-free planar graphs with the same parity colouring. The solid green edges are the bridge edges introduced by the switches from the Even Level Graphs of Figure\nonbreakingspace \ref {fig:n21-elgs}. Each green edge is a bridge of its parity subgraph, so no new cycle -- and in particular no odd cycle -- is created; duals $1$ and $2$ coincide with their Even Level Graphs and have no added edge.}}{8}{figure.6}\protected@file@percent }
\newlabel{fig:n21-duals}{{6}{8}{The six Holton--McKay duals, drawn as crossing-free planar graphs with the same parity colouring. The solid green edges are the bridge edges introduced by the switches from the Even Level Graphs of Figure~\ref {fig:n21-elgs}. Each green edge is a bridge of its parity subgraph, so no new cycle -- and in particular no odd cycle -- is created; duals $1$ and $2$ coincide with their Even Level Graphs and have no added edge}{figure.6}{}}
\gdef \@abspage@last{8}
@@ -1,6 +1,6 @@
# Fdb version 3
["pdflatex"] 1779461873 "/Users/didericis/Code/math-research/papers/even_level_graph_generators/paper.tex" "paper.pdf" "paper" 1779461874
"/Users/didericis/Code/math-research/papers/even_level_graph_generators/paper.tex" 1779461873 15887 19952eaaf0fc5ca355be18e4eff4aaed ""
["pdflatex"] 1779463291 "/Users/didericis/Code/math-research/papers/even_level_graph_generators/paper.tex" "paper.pdf" "paper" 1779463293
"/Users/didericis/Code/math-research/papers/even_level_graph_generators/paper.tex" 1779463291 18288 7a31bbe6e6a18516ee92fc4bff904881 ""
"/usr/local/texlive/2022/texmf-dist/fonts/map/fontname/texfonts.map" 1577235249 3524 cb3e574dea2d1052e39280babc910dc8 ""
"/usr/local/texlive/2022/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1246382020 1004 54797486969f23fa377b128694d548df ""
"/usr/local/texlive/2022/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1246382020 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 ""
@@ -90,9 +90,10 @@
"fig_level_cycle.png" 1779389598 83736 ee54074ab1383a0dcc7fc20387e34bdc ""
"fig_levels.png" 1779389598 88029 5564f46c0a183f3777727b651e7dc461 ""
"fig_parity_subgraph.png" 1779389598 191771 f069aa94c8f49b3c7fd9c71426feff2d ""
"paper.aux" 1779461874 5767 d6f46d8ee667b35ce0393ad310fffd64 "pdflatex"
"paper.out" 1779461874 1047 aeccb746cf11915bcb68f1e7ff8075a7 "pdflatex"
"paper.tex" 1779461873 15887 19952eaaf0fc5ca355be18e4eff4aaed ""
"figures/n21_witnesses.png" 1779463145 347960 9cff26f0427b981c675f214ca5cc1513 ""
"paper.aux" 1779463292 7658 3054ab5e5e2c2d6239a5f60f25708981 "pdflatex"
"paper.out" 1779463292 1047 aeccb746cf11915bcb68f1e7ff8075a7 "pdflatex"
"paper.tex" 1779463291 18288 7a31bbe6e6a18516ee92fc4bff904881 ""
(generated)
"paper.aux"
"paper.log"
@@ -566,6 +566,11 @@ INPUT ./fig_parity_subgraph.png
INPUT fig_parity_subgraph.png
INPUT ./fig_parity_subgraph.png
INPUT ./fig_parity_subgraph.png
INPUT ./figures/n21_witnesses.png
INPUT ./figures/n21_witnesses.png
INPUT figures/n21_witnesses.png
INPUT ./figures/n21_witnesses.png
INPUT ./figures/n21_witnesses.png
INPUT paper.aux
INPUT ./paper.out
INPUT ./paper.out
+23 -12
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) 22 MAY 2026 11:11
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 22 MAY 2026 11:23
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
@@ -393,18 +393,29 @@ Package hyperref Warning: Token not allowed in a PDF string (Unicode):
Package hyperref Warning: Token not allowed in a PDF string (Unicode):
(hyperref) removing `math shift' on input line 371.
[5] [6] (./paper.aux)
[5]
<figures/n21_elgs.png, id=106, 1373.13pt x 867.24pt>
File: figures/n21_elgs.png Graphic file (type png)
<use figures/n21_elgs.png>
Package pdftex.def Info: figures/n21_elgs.png used on input line 431.
(pdftex.def) Requested size: 360.0pt x 227.35617pt.
<figures/n21_duals.png, id=108, 1373.13pt x 867.24pt>
File: figures/n21_duals.png Graphic file (type png)
<use figures/n21_duals.png>
Package pdftex.def Info: figures/n21_duals.png used on input line 444.
(pdftex.def) Requested size: 360.0pt x 227.35617pt.
[6] [7 <./figures/n21_elgs.png>] [8 <./figures/n21_duals.png>] (./paper.aux)
Package rerunfilecheck Info: File `paper.out' has not changed.
(rerunfilecheck) Checksum: AECCB746CF11915BCB68F1E7FF8075A7;1047.
)
Here is how much of TeX's memory you used:
9742 strings out of 478268
150825 string characters out of 5846347
453933 words of memory out of 5000000
27647 multiletter control sequences out of 15000+600000
9765 strings out of 478268
151239 string characters out of 5846347
455154 words of memory out of 5000000
27662 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,450s stack positions out of 10000i,1000n,20000p,200000b,200000s
69i,9n,76p,781b,450s 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>
@@ -418,10 +429,10 @@ texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb></usr/local/te
xlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/local/tex
live/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/local/texli
ve/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (6 pages, 550793 bytes).
Output written on paper.pdf (8 pages, 1620175 bytes).
PDF statistics:
173 PDF objects out of 1000 (max. 8388607)
129 compressed objects within 2 object streams
33 named destinations out of 1000 (max. 500000)
77 words of extra memory for PDF output out of 10000 (max. 10000000)
196 PDF objects out of 1000 (max. 8388607)
146 compressed objects within 2 object streams
38 named destinations out of 1000 (max. 500000)
87 words of extra memory for PDF output out of 10000 (max. 10000000)
Binary file not shown.
+21 -8
View File
@@ -428,15 +428,28 @@ witnesses are step-verified.}
\begin{figure}[ht]
\centering
\includegraphics[width=\textwidth]{figures/n21_witnesses.png}
\includegraphics[width=\textwidth]{figures/n21_elgs.png}
\caption{The witness Even Level Graph for each of the six Holton--McKay
duals, drawn radially by level (source at the centre, level-$k$ vertices on
ring $k$) and coloured by parity (blue even, orange odd). Dashed red edges
are the same-parity edges flipped by the bridge switches; solid green edges
are the bridge edges they introduce; applying the switches turns each Even
Level Graph into the corresponding dual. Duals $1$ and $2$ are Even Level
Graphs outright, so no switch is shown.}
\label{fig:n21-witnesses}
duals, drawn as a crossing-free planar graph and coloured by parity (blue
even, orange odd, with respect to the fixed level-parity labelling). The
dashed red edges are the same-parity edges that the bridge switches flip;
flipping them yields the corresponding dual in
Figure~\ref{fig:n21-duals}. Duals $1$ and $2$ are Even Level Graphs
outright, so no edge is flipped.}
\label{fig:n21-elgs}
\end{figure}
\begin{figure}[ht]
\centering
\includegraphics[width=\textwidth]{figures/n21_duals.png}
\caption{The six Holton--McKay duals, drawn as crossing-free planar graphs
with the same parity colouring. The solid green edges are the bridge edges
introduced by the switches from the Even Level Graphs of
Figure~\ref{fig:n21-elgs}. Each green edge is a bridge of its parity
subgraph, so no new cycle -- and in particular no odd cycle -- is created;
duals $1$ and $2$ coincide with their Even Level Graphs and have no added
edge.}
\label{fig:n21-duals}
\end{figure}
\begin{thebibliography}{9}