dual_decomposition: swap algorithm trace to n=14 + final-graph conjecture

- Replace the dodecahedron trace at the end of section 3 with the n=14
  triangulation found by search_kempe_property.py: its H_1 admits a
  proper 3-edge-colouring satisfying both chord-apex and Kempe-cycle
  conditions (Lemmas 2.6, 2.7).
- experiments/draw_iterated_reduction_n14.py: rebuilds fig_alg_step{0,1,2}
  with Tutte barycentric layouts (outer face chosen to keep v_n in the
  interior); also runs the algorithm to completion, checking chord-apex +
  Kempe at each step (step 1 satisfies all; step 2 fails chord-apex;
  step 3 terminates).
- Add Conjecture 3.4: G is a minimal counterexample iff no proper
  3-edge-colouring of the final reduced graph H_{t*} has all (spike_t,
  merged_t) pairs in distinct colours.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 13:20:32 -04:00
parent c987259c14
commit 03dcd7c2fa
8 changed files with 630 additions and 59 deletions
@@ -0,0 +1,520 @@
"""Draw the iterated reduction trace on the smallest triangulation where
the chord-apex + Kempe-cycle property is satisfied: the first min-degree-5
plantri triangulation on n = 14 vertices, found by search_kempe_property.py.
Overwrites fig_alg_step{0,1,2}.png in the paper directory with this
triangulation's trace (replacing the dodecahedron version).
Run with: sage experiments/draw_iterated_reduction_n14.py
"""
from sage.all import Graph
from sage.graphs.graph_generators import graphs
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import math
import os
def tutte_layout(G_sage, avoid_verts=None, iterations=300):
"""Tutte's barycentric embedding: pick the largest face whose vertex set
avoids `avoid_verts` as the outer face, place its vertices on a regular
polygon, then iterate each interior vertex to the barycenter of its
neighbors. For 3-connected planar graphs this converges to the unique
straight-line planar embedding with the chosen outer face --- balanced
by construction and free of edge crossings."""
avoid = set(avoid_verts or ())
candidates = []
for face in G_sage.faces():
verts = [u for (u, v) in face]
if not (set(verts) & avoid):
candidates.append(verts)
if not candidates:
outer = [u for (u, v) in max(G_sage.faces(), key=len)]
else:
outer = max(candidates, key=len)
n_outer = len(outer)
pos = {}
for k, v in enumerate(outer):
ang = 2 * math.pi * k / n_outer + math.pi / 2
pos[v] = (math.cos(ang), math.sin(ang))
interior = [v for v in G_sage.vertex_iterator() if v not in pos]
for v in interior:
pos[v] = (0.0, 0.0)
for _ in range(iterations):
new_pos = dict(pos)
for v in interior:
nbrs = list(G_sage.neighbor_iterator(v))
sx = sum(pos[w][0] for w in nbrs) / len(nbrs)
sy = sum(pos[w][1] for w in nbrs) / len(nbrs)
new_pos[v] = (sx, sy)
pos = new_pos
return pos
OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
C = ['#dc2626', '#16a34a', '#2563eb']
GRAY = '#9ca3af'
DARK = '#374151'
HIGHLIGHT = '#fef3c7'
SHADE = '#fef3c7'
def dual_of(G):
faces = G.faces()
edge_to_faces = {}
for fi, face in enumerate(faces):
for u, v in face:
e = frozenset((u, v))
edge_to_faces.setdefault(e, []).append(fi)
dual_edges = []
for e, fs in edge_to_faces.items():
if len(fs) == 2:
dual_edges.append((fs[0], fs[1]))
return Graph(dual_edges, multiedges=False, loops=False)
def apply_reduction(G, face, i, v_n_label):
boundary = [u for (u, v) in face]
if len(set(boundary)) != 5:
return None
A = []
for B_k in boundary:
outer = [w for w in G.neighbor_iterator(B_k) if w not in boundary]
if len(outer) != 1:
return None
A.append(outer[0])
if len(set(A)) != 5:
return None
if A[(i + 3) % 5] == A[(i + 4) % 5]:
return None
H = G.copy()
for v in boundary:
H.delete_vertex(v)
H.add_vertex(v_n_label)
side_0 = (v_n_label, A[i % 5])
spike = (v_n_label, A[(i + 1) % 5])
side_1 = (v_n_label, A[(i + 2) % 5])
merged = (A[(i + 3) % 5], A[(i + 4) % 5])
H.add_edges([side_0, spike, side_1, merged])
if H.has_multiple_edges() or H.has_loops():
return None
if not H.is_planar(set_embedding=True):
return None
if not all(H.degree(v) == 3 for v in H.vertex_iterator()):
return None
named = {
'spike': frozenset(spike),
'side_0': frozenset(side_0),
'side_1': frozenset(side_1),
'merged': frozenset(merged),
}
return H, named, boundary, A
def proper_3_edge_colorings(G):
edges = list(G.edges(labels=False))
n_edges = len(edges)
adj = [[] for _ in range(n_edges)]
for i in range(n_edges):
u, v = edges[i][0], edges[i][1]
for j in range(i):
x, y = edges[j][0], edges[j][1]
if u in (x, y) or v in (x, y):
adj[i].append(j)
adj[j].append(i)
coloring = [-1] * n_edges
def back(k):
if k == n_edges:
yield tuple(coloring)
return
for c in range(3):
if all(coloring[j] != c for j in adj[k]):
coloring[k] = c
yield from back(k + 1)
coloring[k] = -1
return edges, back(0)
def kempe_cycle(edges, coloring, start_idx, color_pair):
a, b = color_pair
in_sub = [i for i in range(len(edges)) if coloring[i] in (a, b)]
if start_idx not in in_sub:
return None
visited = {start_idx}
stack = [start_idx]
while stack:
cur = stack.pop()
u, v = edges[cur][0], edges[cur][1]
for j in in_sub:
if j in visited:
continue
x, y = edges[j][0], edges[j][1]
if u in (x, y) or v in (x, y):
visited.add(j)
stack.append(j)
return visited
def matches_property(edges, col, named):
idx = {}
for ii, e in enumerate(edges):
es = frozenset((e[0], e[1]))
for role, ns in named.items():
if es == ns:
idx[role] = ii
if len(idx) != 4:
return False
c_spike = col[idx['spike']]
c_merged = col[idx['merged']]
if c_spike != c_merged:
return False
c_s0 = col[idx['side_0']]
c_s1 = col[idx['side_1']]
kc0 = kempe_cycle(edges, col, idx['spike'], (c_spike, c_s0))
if idx['side_0'] not in kc0 or idx['merged'] not in kc0:
return False
kc1 = kempe_cycle(edges, col, idx['spike'], (c_spike, c_s1))
if idx['side_1'] not in kc1 or idx['merged'] not in kc1:
return False
return True
def find_first_match():
"""Iterate over (G, face, i_red, coloring) and return the first hit."""
for G in graphs.triangulations(14, minimum_degree=5):
if not G.is_planar(set_embedding=True):
continue
D = dual_of(G)
D.is_planar(set_embedding=True)
for face in D.faces():
if len(face) != 5:
continue
for i_red in range(5):
res = apply_reduction(D, face, i_red, '__v_n_1__')
if res is None:
continue
H, named, boundary, A = res
edges, gen = proper_3_edge_colorings(H)
for col in gen:
if matches_property(edges, col, named):
coloring_dict = {frozenset((e[0], e[1])): c
for e, c in zip(edges, col)}
return G, D, face, i_red, H, named, boundary, A, coloring_dict
return None
def draw_graph(ax, G, pos, *, coloring=None, protected=None,
shade_vertices=None, vn_labels=None):
if shade_vertices:
poly = [pos[v] for v in shade_vertices]
ax.add_patch(Polygon(poly, closed=True, facecolor=SHADE,
edgecolor='none', zorder=0))
protected = protected or set()
vn_labels = vn_labels or {}
for u, v, _ in G.edges():
e = frozenset([u, v])
c = C[coloring[e]] if (coloring is not None and e in coloring) else GRAY
lw = 3.8 if e in protected else 1.4
(x0, y0), (x1, y1) = pos[u], pos[v]
ax.plot([x0, x1], [y0, y1], color=c, lw=lw, zorder=2)
for v in G.vertices(sort=False):
x, y = pos[v]
if v in vn_labels:
ax.scatter(x, y, s=320, color=HIGHLIGHT, marker='s',
edgecolors='black', linewidths=1.2, zorder=4)
ax.annotate(vn_labels[v], (x, y),
textcoords='offset points', xytext=(16, 16),
ha='left', fontsize=14, fontweight='bold',
color=DARK, zorder=6,
bbox=dict(boxstyle='round,pad=0.2', fc='white',
ec=DARK, lw=0.6))
else:
ax.scatter(x, y, s=60, color=DARK, zorder=3)
ax.set_aspect('equal')
ax.axis('off')
def main():
print("Searching for the first match at n = 14 ...")
result = find_first_match()
if result is None:
print("No match found at n = 14.")
return
G14, D, face, i_red, H1, named1, boundary1, A1, coloring1 = result
print(f"Found at i_red = {i_red}")
print(f" G (n=14): |V|={G14.order()}, |E|={G14.size()}, "
f"min_deg={min(G14.degree())}")
print(f" D = G': |V|={D.order()}, |E|={D.size()}")
print(f" H_1: |V|={H1.order()}, |E|={H1.size()}")
# Relabel H_1 in place so all vertex labels are comparable integers
# (Sage's planar layout and face enumeration need comparable labels).
# Translate coloring1 and named1 accordingly.
H1_relabel_map = {v: i for i, v in enumerate(H1.vertex_iterator())}
H1.relabel(perm=H1_relabel_map, inplace=True)
vn1_int = H1_relabel_map['__v_n_1__']
coloring1 = {frozenset(H1_relabel_map[u] for u in e): c
for e, c in coloring1.items()}
named1 = {role: frozenset(H1_relabel_map[u] for u in e)
for role, e in named1.items()}
D.is_planar(set_embedding=True)
D_layout = tutte_layout(D, avoid_verts=set(u for (u, v) in face))
H1.is_planar(set_embedding=True)
H1_layout = tutte_layout(H1, avoid_verts={vn1_int})
boundary_face_verts = [u for (u, v) in face]
fig, ax = plt.subplots(figsize=(8, 8))
draw_graph(ax, D, D_layout, shade_vertices=boundary_face_verts)
fig.savefig(os.path.join(OUT_DIR, 'fig_alg_step0.png'),
dpi=170, bbox_inches='tight')
plt.close(fig)
print("Wrote fig_alg_step0.png")
E = set(named1.values())
fig, ax = plt.subplots(figsize=(8, 8))
draw_graph(ax, H1, H1_layout, coloring=coloring1, protected=E,
vn_labels={vn1_int: '$v_n^{(1)}$'})
fig.savefig(os.path.join(OUT_DIR, 'fig_alg_step1.png'),
dpi=170, bbox_inches='tight')
plt.close(fig)
print("Wrote fig_alg_step1.png")
# ----- Step 2: try to continue -----
H1.is_planar(set_embedding=True)
chosen2 = None
for face2 in H1.faces():
if len(face2) != 5:
continue
boundary2 = [u for (u, v) in face2]
boundary2_edges = [frozenset([u, v]) for (u, v) in face2]
externals2 = []
A2 = []
valid_face = True
for B_k in boundary2:
outer = [w for w in H1.neighbor_iterator(B_k) if w not in boundary2]
if len(outer) != 1:
valid_face = False
break
externals2.append(frozenset([B_k, outer[0]]))
A2.append(outer[0])
if not valid_face:
continue
if any(e in E for e in boundary2_edges + externals2):
continue
# find valid i_t
f_vec = [coloring1[e] for e in externals2]
for i_t in range(5):
if f_vec[(i_t + 3) % 5] != f_vec[(i_t + 4) % 5]:
continue
if len({f_vec[i_t], f_vec[(i_t + 1) % 5], f_vec[(i_t + 2) % 5]}) != 3:
continue
if A2[(i_t + 3) % 5] == A2[(i_t + 4) % 5]:
continue
chosen2 = (face2, i_t, boundary2, externals2, A2)
break
if chosen2 is not None:
break
if chosen2 is None:
# algorithm terminates at H_1
fig, ax = plt.subplots(figsize=(8, 8))
ax.text(0.5, 0.5,
"Algorithm terminates at $H_1$:\n"
"no pentagonal face of $H_1$ has all\n"
"ten incident edges outside $E$.",
ha='center', va='center', fontsize=18, color=DARK,
transform=ax.transAxes,
bbox=dict(boxstyle='round,pad=0.6', fc=HIGHLIGHT,
ec=DARK, lw=1.0))
ax.set_aspect('equal')
ax.axis('off')
fig.savefig(os.path.join(OUT_DIR, 'fig_alg_step2.png'),
dpi=170, bbox_inches='tight')
plt.close(fig)
print("Wrote fig_alg_step2.png (termination card)")
print(" Algorithm terminates at H_1: no safe pentagonal face.")
return
face2, i_t, boundary2, externals2, A2 = chosen2
print(f"Step 2: safe face found, i_t = {i_t}")
H2 = H1.copy()
for v in boundary2:
H2.delete_vertex(v)
# use a fresh int label for v_n^(2)
v_n_2 = max(H1.vertices(sort=False)) + 1
H2.add_vertex(v_n_2)
side_0_2 = (v_n_2, A2[i_t])
spike_2 = (v_n_2, A2[(i_t + 1) % 5])
side_1_2 = (v_n_2, A2[(i_t + 2) % 5])
merged_2 = (A2[(i_t + 3) % 5], A2[(i_t + 4) % 5])
H2.add_edges([side_0_2, spike_2, side_1_2, merged_2])
H2.is_planar(set_embedding=True)
coloring2 = {e: c for e, c in coloring1.items()
if not any(u in boundary2 for u in e)}
coloring2[frozenset(side_0_2)] = coloring1[externals2[i_t]]
coloring2[frozenset(spike_2)] = coloring1[externals2[(i_t + 1) % 5]]
coloring2[frozenset(side_1_2)] = coloring1[externals2[(i_t + 2) % 5]]
coloring2[frozenset(merged_2)] = coloring1[externals2[(i_t + 3) % 5]]
E |= {frozenset(side_0_2), frozenset(spike_2),
frozenset(side_1_2), frozenset(merged_2)}
H2_layout = tutte_layout(H2, avoid_verts={vn1_int, v_n_2})
fig, ax = plt.subplots(figsize=(8, 8))
draw_graph(ax, H2, H2_layout, coloring=coloring2, protected=E,
vn_labels={vn1_int: '$v_n^{(1)}$',
v_n_2: '$v_n^{(2)}$'})
fig.savefig(os.path.join(OUT_DIR, 'fig_alg_step2.png'),
dpi=170, bbox_inches='tight')
plt.close(fig)
print(f"Wrote fig_alg_step2.png: H_2 with |V|={H2.order()}, "
f"|E|={H2.size()}, |protected|={len(E)}")
# --- continue running to completion, checking Kempe condition each step --
print()
print("=" * 72)
print("Running algorithm to completion, checking chord-apex + Kempe at "
"each step.")
print("=" * 72)
# Step 1 status (by construction this is the matching coloring)
cond1 = check_step_conditions(H1, coloring1, named1)
print(f" step t = 1: |V|={H1.order():>3}, |E_graph|={H1.size():>3}, "
f"|E_prot|= 4 (initial)"
f" | chord-apex: {cond1['chord_apex']}, "
f"side_0-Kempe: {cond1['kc_side_0']}, "
f"side_1-Kempe: {cond1['kc_side_1']}")
run_to_completion_from(H2, coloring2, E,
{'spike': frozenset(spike_2),
'side_0': frozenset(side_0_2),
'side_1': frozenset(side_1_2),
'merged': frozenset(merged_2)},
start_t=2)
def check_step_conditions(H, coloring, named):
"""Given an H_t and the *just-added* spike/side_0/side_1/merged, check
whether chord-apex and the two Kempe-cycle conditions hold."""
edges = list(H.edges(labels=False))
edges_fs = [frozenset((u, v)) for (u, v) in edges]
col = [coloring[e] for e in edges_fs]
idx = {role: edges_fs.index(e) for role, e in named.items()}
c_spike = col[idx['spike']]
c_merged = col[idx['merged']]
chord_apex = (c_spike == c_merged)
if not chord_apex:
return {'chord_apex': False, 'kc_side_0': False, 'kc_side_1': False}
c_s0 = col[idx['side_0']]
c_s1 = col[idx['side_1']]
kc0 = kempe_cycle(edges, col, idx['spike'], (c_spike, c_s0))
kc1 = kempe_cycle(edges, col, idx['spike'], (c_spike, c_s1))
kc_side_0 = (idx['side_0'] in kc0 and idx['merged'] in kc0)
kc_side_1 = (idx['side_1'] in kc1 and idx['merged'] in kc1)
return {'chord_apex': True, 'kc_side_0': kc_side_0, 'kc_side_1': kc_side_1}
def find_safe_face(H, protected):
"""Return (face, externals, A) for some safe pentagonal face avoiding
`protected`, or None."""
for face in H.faces():
if len(face) != 5:
continue
boundary = [u for (u, v) in face]
boundary_edges = [frozenset([u, v]) for (u, v) in face]
externals = []
A = []
valid = True
for B_k in boundary:
outer = [w for w in H.neighbor_iterator(B_k) if w not in boundary]
if len(outer) != 1:
valid = False
break
externals.append(frozenset([B_k, outer[0]]))
A.append(outer[0])
if not valid:
continue
if any(e in protected for e in boundary_edges + externals):
continue
return face, boundary, externals, A
return None
def run_to_completion_from(H, coloring, E, last_named, start_t):
"""Continue iterating from H_{start_t}. The 'last_named' dict carries
the spike/side/merged of step `start_t` so we can report its Kempe
status. Print a row per step."""
t = start_t
print(f" step t = {t}: |V|={H.order():>3}, |E_graph|={H.size():>3}, "
f"|E_prot|={len(E):>3}", end='')
cond = check_step_conditions(H, coloring, last_named)
print(f" | chord-apex: {cond['chord_apex']}, "
f"side_0-Kempe: {cond['kc_side_0']}, "
f"side_1-Kempe: {cond['kc_side_1']}")
while True:
H.is_planar(set_embedding=True)
res = find_safe_face(H, E)
if res is None:
print(f" step t = {t + 1}: no safe pentagonal face --> "
f"algorithm terminates at H_{t}.")
return
face, boundary, externals, A = res
f_vec = [coloring[e] for e in externals]
i_t = None
for i in range(5):
if f_vec[(i + 3) % 5] != f_vec[(i + 4) % 5]:
continue
if len({f_vec[i], f_vec[(i + 1) % 5], f_vec[(i + 2) % 5]}) != 3:
continue
if A[(i + 3) % 5] == A[(i + 4) % 5]:
continue
i_t = i
break
if i_t is None:
print(f" step t = {t + 1}: f = {f_vec}, no valid index --> "
f"terminate (Lemma 2.4 violation? Probably a parallel-edge "
f"or other degenerate case).")
return
t += 1
v_n_new = max(H.vertices(sort=False)) + 1 if all(
isinstance(v, int) for v in H.vertex_iterator()) else f'vn{t}'
H_new = H.copy()
for v in boundary:
H_new.delete_vertex(v)
H_new.add_vertex(v_n_new)
side_0 = (v_n_new, A[i_t])
spike = (v_n_new, A[(i_t + 1) % 5])
side_1 = (v_n_new, A[(i_t + 2) % 5])
merged = (A[(i_t + 3) % 5], A[(i_t + 4) % 5])
H_new.add_edges([side_0, spike, side_1, merged])
H = H_new
coloring = {e: c for e, c in coloring.items()
if not any(u in boundary for u in e)}
coloring[frozenset(side_0)] = coloring[externals[i_t]] \
if frozenset(externals[i_t]) in coloring else f_vec[i_t]
# safer: directly use f_vec
coloring[frozenset(side_0)] = f_vec[i_t]
coloring[frozenset(spike)] = f_vec[(i_t + 1) % 5]
coloring[frozenset(side_1)] = f_vec[(i_t + 2) % 5]
coloring[frozenset(merged)] = f_vec[(i_t + 3) % 5]
named = {
'spike': frozenset(spike),
'side_0': frozenset(side_0),
'side_1': frozenset(side_1),
'merged': frozenset(merged),
}
E |= set(named.values())
cond = check_step_conditions(H, coloring, named)
print(f" step t = {t}: |V|={H.order():>3}, |E_graph|={H.size():>3}, "
f"|E_prot|={len(E):>3}, i_t = {i_t}", end='')
print(f" | chord-apex: {cond['chord_apex']}, "
f"side_0-Kempe: {cond['kc_side_0']}, "
f"side_1-Kempe: {cond['kc_side_1']}")
if __name__ == '__main__':
main()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 77 KiB

@@ -22,6 +22,7 @@
\newlabel{tocindent1}{17.77782pt}
\newlabel{tocindent2}{0pt}
\newlabel{tocindent3}{0pt}
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Algorithm\nonbreakingspace 3.1\hbox {} on $G' = $ dodecahedron (dual of the icosahedron). \emph {Left:} $G'$ (20 vertices, 30 edges), with $F_v$ (the inner pentagon) shaded as the face chosen for the first reduction. \emph {Centre:} $H_1$ (16 vertices, 24 edges) after step\nonbreakingspace (1) with $i_1 = 0$, $3$-edge-coloured by Sage; the four edges around $v_n^{(1)}$ in $E$ are drawn thicker. \emph {Right:} $H_2$ (12 vertices, 18 edges) after step\nonbreakingspace (3) with $i_t = 0$; the only safe pentagonal face in $H_1$ was the outer pentagon, whose deletion produces $v_n^{(2)}$ and a second chord, giving eight protected edges. No safe pentagonal face remains, so the algorithm terminates. The generating script is \texttt {experiments/draw\_iterated\_reduction.py}.}}{8}{}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Algorithm\nonbreakingspace 3.1\hbox {} on $G'=\mathrm {dual}(G)$, where $G$ is the first min-degree-$5$ plantri triangulation on $14$ vertices and $\varphi _1$ is a specific proper $3$-edge-colouring of $H_1$ that satisfies both the chord-apex condition (Lemma\nonbreakingspace 2.6\hbox {}) and the Kempe-cycle condition (Lemma\nonbreakingspace 2.7\hbox {}), found by \texttt {experiments/search\_kempe\_property.py}. \emph {Left:} $G'$ ($24$ vertices, $36$ edges) with the chosen pentagonal face shaded. \emph {Centre:} $H_1$ ($20$ vertices, $30$ edges) after step\nonbreakingspace (1) with $i_1 = 1$, $3$-edge-coloured by $\varphi _1$; the four edges around $v_n^{(1)}$ in $E$ are drawn thicker, and the spike and merged edges share the colour green. \emph {Right:} $H_2$ ($16$ vertices, $24$ edges) after step\nonbreakingspace (3) with $i_t = 3$; eight edges are protected, and the algorithm terminates one step later (no remaining safe pentagonal face in $H_2$). The generating script is \texttt {experiments/draw\_iterated\_reduction\_n14.py}; layouts are Tutte barycentric embeddings with the outer face picked to keep $v_n^{(1)}, v_n^{(2)}$ in the interior.}}{8}{}\protected@file@percent }
\newlabel{fig:iterated-reduction-trace}{{3}{8}}
\newlabel{conj:no-all-distinct-coloring}{{3.4}{8}}
\gdef \@abspage@last{8}
@@ -1,4 +1,4 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 23 MAY 2026 03:21
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 23 MAY 2026 13:19
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
@@ -144,26 +144,26 @@ File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX)
(./paper.aux)
\openout1 = `paper.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 26.
LaTeX Font Info: ... okay on input line 26.
LaTeX Font Info: Trying to load font information for U+msa on input line 26.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 27.
LaTeX Font Info: ... okay on input line 27.
LaTeX Font Info: Trying to load font information for U+msa on input line 27.
(/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 26.
LaTeX Font Info: Trying to load font information for U+msb on input line 27.
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsb.fd
@@ -192,7 +192,7 @@ File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv
e
))
[1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
Overfull \hbox (41.917pt too wide) in paragraph at lines 144--146
Overfull \hbox (41.917pt too wide) in paragraph at lines 145--147
[]\OT1/cmr/m/n/10 List the five degree-$2$ ver-tices in clock-wise or-der aroun
d $\OML/cmm/m/it/10 F$ \OT1/cmr/m/n/10 as $\OML/cmm/m/it/10 A \OT1/cmr/m/n/10 =
(\OML/cmm/m/it/10 A[]; A[]; A[]; A[]; A[]\OT1/cmr/m/n/10 )$.
@@ -201,22 +201,22 @@ d $\OML/cmm/m/it/10 F$ \OT1/cmr/m/n/10 as $\OML/cmm/m/it/10 A \OT1/cmr/m/n/10 =
<fig_reduced_dual_step1.png, id=17, 517.79329pt x 499.08812pt>
File: fig_reduced_dual_step1.png Graphic file (type png)
<use fig_reduced_dual_step1.png>
Package pdftex.def Info: fig_reduced_dual_step1.png used on input line 164.
Package pdftex.def Info: fig_reduced_dual_step1.png used on input line 165.
(pdftex.def) Requested size: 172.79846pt x 166.55775pt.
<fig_reduced_dual_step2.png, id=19, 490.16064pt x 483.35876pt>
File: fig_reduced_dual_step2.png Graphic file (type png)
<use fig_reduced_dual_step2.png>
Package pdftex.def Info: fig_reduced_dual_step2.png used on input line 165.
Package pdftex.def Info: fig_reduced_dual_step2.png used on input line 166.
(pdftex.def) Requested size: 172.79846pt x 170.39505pt.
<fig_reduced_dual_step3.png, id=20, 490.16064pt x 483.35876pt>
File: fig_reduced_dual_step3.png Graphic file (type png)
<use fig_reduced_dual_step3.png>
Package pdftex.def Info: fig_reduced_dual_step3.png used on input line 166.
Package pdftex.def Info: fig_reduced_dual_step3.png used on input line 167.
(pdftex.def) Requested size: 172.79846pt x 170.39505pt.
<fig_reduced_dual_step4.png, id=21, 490.16064pt x 486.3346pt>
File: fig_reduced_dual_step4.png Graphic file (type png)
<use fig_reduced_dual_step4.png>
Package pdftex.def Info: fig_reduced_dual_step4.png used on input line 167.
Package pdftex.def Info: fig_reduced_dual_step4.png used on input line 168.
(pdftex.def) Requested size: 172.79846pt x 171.44409pt.
LaTeX Warning: `h' float specifier changed to `ht'.
@@ -226,17 +226,17 @@ uced_dual_step3.png> <./fig_reduced_dual_step4.png>]
<fig_chord_apex_step1.png, id=34, 505.03976pt x 502.06393pt>
File: fig_chord_apex_step1.png Graphic file (type png)
<use fig_chord_apex_step1.png>
Package pdftex.def Info: fig_chord_apex_step1.png used on input line 288.
Package pdftex.def Info: fig_chord_apex_step1.png used on input line 289.
(pdftex.def) Requested size: 251.9989pt x 250.5104pt.
<fig_chord_apex_step2.png, id=35, 490.16064pt x 499.51323pt>
File: fig_chord_apex_step2.png Graphic file (type png)
<use fig_chord_apex_step2.png>
Package pdftex.def Info: fig_chord_apex_step2.png used on input line 289.
Package pdftex.def Info: fig_chord_apex_step2.png used on input line 290.
(pdftex.def) Requested size: 172.79846pt x 176.08986pt.
<fig_chord_apex_step3.png, id=36, 490.16064pt x 499.51323pt>
File: fig_chord_apex_step3.png Graphic file (type png)
<use fig_chord_apex_step3.png>
Package pdftex.def Info: fig_chord_apex_step3.png used on input line 290.
Package pdftex.def Info: fig_chord_apex_step3.png used on input line 291.
(pdftex.def) Requested size: 172.79846pt x 176.08986pt.
@@ -244,7 +244,7 @@ LaTeX Warning: `h' float specifier changed to `ht'.
[4] [5 <./fig_chord_apex_step1.png> <./fig_chord_apex_step2.png> <./fig_chord_a
pex_step3.png>] [6]
Overfull \hbox (4.76643pt too wide) in paragraph at lines 432--439
Overfull \hbox (4.76643pt too wide) in paragraph at lines 433--440
\OT1/cmr/m/n/10 which $\OML/cmm/m/it/10 '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[
]\OT1/cmr/m/n/10 ) = \OML/cmm/m/it/10 '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[]\
OT1/cmr/m/n/10 )$ and $\OML/cmm/m/it/10 '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[
@@ -253,31 +253,60 @@ T1/cmr/m/n/10 )\OML/cmm/m/it/10 ; '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[]\OT1/
cmr/m/n/10 )$
[]
<fig_alg_step0.png, id=49, 462.528pt x 440.42188pt>
<fig_alg_step0.png, id=49, 399.6106pt x 459.55217pt>
File: fig_alg_step0.png Graphic file (type png)
<use fig_alg_step0.png>
Package pdftex.def Info: fig_alg_step0.png used on input line 478.
(pdftex.def) Requested size: 115.20264pt x 109.69525pt.
<fig_alg_step1.png, id=50, 462.528pt x 440.42188pt>
Package pdftex.def Info: fig_alg_step0.png used on input line 479.
(pdftex.def) Requested size: 115.20264pt x 132.48134pt.
<fig_alg_step1.png, id=50, 399.6106pt x 459.55217pt>
File: fig_alg_step1.png Graphic file (type png)
<use fig_alg_step1.png>
Package pdftex.def Info: fig_alg_step1.png used on input line 479.
(pdftex.def) Requested size: 115.20264pt x 109.69525pt.
<fig_alg_step2.png, id=51, 480.80806pt x 371.1277pt>
Package pdftex.def Info: fig_alg_step1.png used on input line 480.
(pdftex.def) Requested size: 115.20264pt x 132.48134pt.
<fig_alg_step2.png, id=51, 399.6106pt x 459.55217pt>
File: fig_alg_step2.png Graphic file (type png)
<use fig_alg_step2.png>
Package pdftex.def Info: fig_alg_step2.png used on input line 480.
(pdftex.def) Requested size: 115.20264pt x 88.91956pt.
Package pdftex.def Info: fig_alg_step2.png used on input line 481.
(pdftex.def) Requested size: 115.20264pt x 132.48134pt.
Underfull \hbox (badness 4391) in paragraph at lines 498--498
\OT1/cmr/m/sc/10 Figure 3.\OT1/cmr/m/n/10 Algorithm 3.1[] on $\OML/cmm/m/it/10
G[] \OT1/cmr/m/n/10 = [](\OML/cmm/m/it/10 G\OT1/cmr/m/n/10 )$, where $\OML/cmm/
m/it/10 G$
[]
Underfull \hbox (badness 3623) in paragraph at lines 498--498
\OT1/cmr/m/n/10 is the first min-degree-$5$ plantri tri-an-gu-la-tion on $14$ v
er-
[]
Underfull \hbox (badness 3179) in paragraph at lines 498--498
\OT1/cmr/m/n/10 tices and $\OML/cmm/m/it/10 '[]$ \OT1/cmr/m/n/10 is a spe-cific
proper $3$-edge-colouring of $\OML/cmm/m/it/10 H[]$
[]
Underfull \hbox (badness 3209) in paragraph at lines 498--498
\OT1/cmr/m/n/10 that sat-is-fies both the chord-apex con-di-tion (Lemma 2.6[])
[]
Underfull \hbox (badness 6094) in paragraph at lines 498--498
\OT1/cmr/m/n/10 and the Kempe-cycle con-di-tion (Lemma 2.7[]), found by
[]
[7] [8 <./fig_alg_step0.png> <./fig_alg_step1.png> <./fig_alg_step2.png>]
(./paper.aux) )
Here is how much of TeX's memory you used:
3075 strings out of 478268
43781 string characters out of 5846347
343216 words of memory out of 5000000
21112 multiletter control sequences out of 15000+600000
3079 strings out of 478268
43848 string characters out of 5846347
344259 words of memory out of 5000000
21116 multiletter control sequences out of 15000+600000
476532 words of font info for 56 fonts, out of 8000000 for 9000
1302 hyphenation exceptions out of 8191
69i,8n,76p,918b,298s stack positions out of 10000i,1000n,20000p,200000b,200000s
69i,8n,76p,1306b,298s 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/c
m/cmcsc10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/c
@@ -289,15 +318,16 @@ mi7.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr1
fb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb>
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb></u
sr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb></us
r/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb></usr/
local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/l
ocal/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/loc
al/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb></usr/loca
l/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (8 pages, 983037 bytes).
r/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy5.pfb></usr/
local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb></usr/lo
cal/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/loc
al/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti8.pfb></usr/local
/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb></usr/local/
texlive/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (8 pages, 969703 bytes).
PDF statistics:
132 PDF objects out of 1000 (max. 8388607)
69 compressed objects within 1 object stream
137 PDF objects out of 1000 (max. 8388607)
72 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
51 words of extra memory for PDF output out of 10000 (max. 10000000)
@@ -12,6 +12,7 @@
\newtheorem{lemma}[theorem]{Lemma}
\newtheorem{corollary}[theorem]{Corollary}
\newtheorem{proposition}[theorem]{Proposition}
\newtheorem{conjecture}[theorem]{Conjecture}
\theoremstyle{definition}
\newtheorem{definition}[theorem]{Definition}
@@ -478,17 +479,36 @@ any further structure to $\varphi_t$ for $t \geq 2$ is left open.
\includegraphics[width=0.32\textwidth]{fig_alg_step0.png}\hfill
\includegraphics[width=0.32\textwidth]{fig_alg_step1.png}\hfill
\includegraphics[width=0.32\textwidth]{fig_alg_step2.png}
\caption{Algorithm~\ref{alg:iterated-reduction} on $G' = $ dodecahedron
(dual of the icosahedron). \emph{Left:} $G'$ (20 vertices, 30 edges), with
$F_v$ (the inner pentagon) shaded as the face chosen for the first reduction.
\emph{Centre:} $H_1$ (16 vertices, 24 edges) after step~(1) with $i_1 = 0$,
$3$-edge-coloured by Sage; the four edges around $v_n^{(1)}$ in $E$ are drawn
thicker. \emph{Right:} $H_2$ (12 vertices, 18 edges) after step~(3) with
$i_t = 0$; the only safe pentagonal face in $H_1$ was the outer pentagon,
whose deletion produces $v_n^{(2)}$ and a second chord, giving eight protected
edges. No safe pentagonal face remains, so the algorithm terminates. The
generating script is \texttt{experiments/draw\_iterated\_reduction.py}.}
\caption{Algorithm~\ref{alg:iterated-reduction} on $G'=\mathrm{dual}(G)$, where
$G$ is the first min-degree-$5$ plantri triangulation on $14$ vertices and
$\varphi_1$ is a specific proper $3$-edge-colouring of $H_1$ that satisfies
both the chord-apex condition (Lemma~\ref{lem:chord-apex}) and the Kempe-cycle
condition (Lemma~\ref{lem:kempe-spike}), found by
\texttt{experiments/search\_kempe\_property.py}. \emph{Left:} $G'$
($24$ vertices, $36$ edges) with the chosen pentagonal face shaded.
\emph{Centre:} $H_1$ ($20$ vertices, $30$ edges) after step~(1) with
$i_1 = 1$, $3$-edge-coloured by $\varphi_1$; the four edges around
$v_n^{(1)}$ in $E$ are drawn thicker, and the spike and merged edges share
the colour green. \emph{Right:} $H_2$ ($16$ vertices, $24$ edges) after
step~(3) with $i_t = 3$; eight edges are protected, and the algorithm
terminates one step later (no remaining safe pentagonal face in $H_2$).
The generating script is
\texttt{experiments/draw\_iterated\_reduction\_n14.py}; layouts are Tutte
barycentric embeddings with the outer face picked to keep $v_n^{(1)},
v_n^{(2)}$ in the interior.}
\label{fig:iterated-reduction-trace}
\end{figure}
\begin{conjecture}
\label{conj:no-all-distinct-coloring}
Let $G$ be a maximal planar graph of minimum degree $\geq 5$, and let
$H_{t^*}$ be the final reduced graph produced by some terminating execution
of Algorithm~\ref{alg:iterated-reduction} on $G$, with the corresponding
sequence of named pairs $(\mathrm{spike}_t, \mathrm{merged}_t)$ for
$t = 1, \dots, t^*$. Then $G$ is a minimal counterexample to the Four Colour
Theorem if and only if there is no proper $3$-edge-colouring of $H_{t^*}$
under which $\mathrm{spike}_t$ and $\mathrm{merged}_t$ receive different
colours for every $t \in \{1, \dots, t^*\}$.
\end{conjecture}
\end{document}