dual_decomposition: 4-edge-face criterion, Conj 3.8, cubic contraction theorem

- Conjecture 3.6: add the 4-edge-face criterion as clause (3), with empirical
  table through n=21 (complete, 535,182/535,182 pass) plus partial n=22
  (641,700 colourings, timed out).
- Conjecture 3.8: strengthening with clause (4) on the b,c-Kempe cycle / 3-colour
  alternative on the new face f_n; existential at the witness level. Tested
  through n=18 (13,800/13,800 pass).
- Definition + figure for cubic-graph edge contraction (delete edge, smooth the
  resulting degree-2 endpoints; equivalent to simple contraction in the dual).
- Theorem: cubic contraction across a 4-face preserves 3-edge-colourability when
  the two opposite boundary edges have different colours. Constructive proof:
  the two smoothed-in edges inherit the colour of the w_i pair they absorb, and
  e_1 is recoloured to the third colour.
- Add 2-panel illustration of the theorem's recolouring.
- Trim Remark 3.7 and 3.9 tables to fit within \textwidth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 13:28:15 -04:00
parent 464c524fa1
commit 753af5ffae
11 changed files with 1366 additions and 59 deletions
@@ -0,0 +1,426 @@
"""Test Conjecture 3.8 (strengthening of 3.6 with the {b,c}-Kempe / 3-colour
constraint on the new 4-edge face) on all min-degree-5 triangulations up to
n = 18.
For each (G, F_v, i_red, varphi) with varphi a chord-apex + Kempe coloring,
we look for a witness (F, e_1, e_2) of Conjecture 3.6 (clauses 1-3 including
the 4-edge-face criterion). Then we:
- subdivide e_1, e_2 by X_1, X_2,
- add the new edge X_1 X_2,
- recolour the (subdivided) Kempe cycle alternately starting from merged
so propriety holds and the new edge takes the third colour,
- identify f_n (the 4-edge face containing the new edge), and
- test clause 4:
EITHER partial(f_n) uses all 3 colours,
OR the {b, c}-Kempe cycle through X_1 X_2 has only X_1 X_2 itself in
partial(f_n).
Aggregates per-n.
Run with: sage experiments/check_conj_3_8_scaled.py
"""
from sage.all import Graph
from sage.graphs.graph_generators import graphs
import sys
import time
def dual_of(G):
G.is_planar(set_embedding=True)
faces = G.faces()
edge_to_faces = {}
for fi, face in enumerate(faces):
for u, v in face:
edge_to_faces.setdefault(frozenset((u, v)), []).append(fi)
return Graph(
[(fs[0], fs[1]) for fs in edge_to_faces.values() if len(fs) == 2],
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 or 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])
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
return {'H': H, 'named': {'spike': frozenset(spike),
'side_0': frozenset(side_0), 'side_1': frozenset(side_1),
'merged': frozenset(merged)}}
def proper_3_edge_colorings(G):
edges = list(G.edges(labels=False))
n = len(edges)
adj = [[] for _ in range(n)]
for i in range(n):
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
results = []
def back(k):
if k == n:
results.append(tuple(coloring)); return
for c in range(3):
if all(coloring[j] != c for j in adj[k]):
coloring[k] = c
back(k + 1)
coloring[k] = -1
back(0)
return edges, results
def kempe_cycle_set(edges, coloring, start_idx, color_pair):
a, b = color_pair
if coloring[start_idx] not in (a, b): return set()
in_sub = set(i for i in range(len(edges)) if coloring[i] in (a, b))
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 edge_idx(edges, e_frozen):
for i, e in enumerate(edges):
if frozenset((e[0], e[1])) == e_frozen:
return i
return None
def trace_kempe_cycle(edges, col_list, start_idx, color_pair):
cycle_set = kempe_cycle_set(edges, col_list, start_idx, color_pair)
incident_at = {}
for ei in cycle_set:
u, v = edges[ei][0], edges[ei][1]
incident_at.setdefault(u, []).append(ei)
incident_at.setdefault(v, []).append(ei)
start_u, start_v = edges[start_idx][0], edges[start_idx][1]
walk = [(start_idx, start_v)]
cur_e = start_idx
cur_leave = start_v
while True:
nbrs = incident_at[cur_leave]
if len(nbrs) != 2: break
nxt = nbrs[0] if nbrs[1] == cur_e else nbrs[1]
u2, v2 = edges[nxt][0], edges[nxt][1]
leave_next = v2 if u2 == cur_leave else u2
if nxt == start_idx: break
walk.append((nxt, leave_next))
cur_e = nxt
cur_leave = leave_next
return walk
def matches_chord_apex_kempe(edges, col, named):
idx = {role: edge_idx(edges, ns) for role, ns in named.items()}
if any(v is None for v in idx.values()): 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_set(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_set(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_all_36_witnesses(H, edges, col_list, named):
"""Yield every (F, e_1, e_2) satisfying clauses 1-3 of Conjecture 3.6."""
merged_idx = edge_idx(edges, named['merged'])
c_merged = col_list[merged_idx]
kempe_cycles = []
for c_prime in range(3):
if c_prime == c_merged: continue
kc = kempe_cycle_set(edges, col_list, merged_idx, (c_merged, c_prime))
kempe_cycles.append((c_prime, kc))
H.is_planar(set_embedding=True)
out = []
for face in H.faces():
face_edge_indices = []
for u, v in face:
ei = edge_idx(edges, frozenset((u, v)))
if ei is not None:
face_edge_indices.append(ei)
n_face = len(face_edge_indices)
for i in range(n_face):
for j in range(i + 1, n_face):
e1, e2 = face_edge_indices[i], face_edge_indices[j]
if e1 == merged_idx or e2 == merged_idx: continue
if col_list[e1] != col_list[e2]: continue
gap_a = (j - i - 1)
gap_b = (n_face - 2 - gap_a)
if gap_a != 1 and gap_b != 1: continue
for c_prime, kc in kempe_cycles:
if e1 in kc and e2 in kc:
if gap_a == 1:
e_F = face_edge_indices[i + 1]
else:
e_F = face_edge_indices[(j + 1) % n_face]
out.append({
'face_edges': face_edge_indices,
'e1': e1, 'e2': e2, 'e_F': e_F,
'kc_color_pair': (c_merged, c_prime),
})
return out
def check_clause_4(H, edges, col_list, named, witness):
"""Construct the modified H + recoloring, identify f_n, check clause 4."""
e1, e2 = witness['e1'], witness['e2']
e_F = witness['e_F']
a = col_list[e1] # color of e_1 = e_2
merged_idx = edge_idx(edges, named['merged'])
cyc_a, cyc_b = witness['kc_color_pair'] # = (c_merged, c_other)
# a is the colour of e_1, e_2 on the Kempe cycle. By chord-apex,
# c_merged is the color of merged = the color of all "a"-edges on the
# cycle. The cycle alternates between c_merged = a and c_other = b.
b = cyc_b
c = ({0, 1, 2} - {a, b}).pop()
# Subdivided cycle K' alternates blue/green starting from merged = blue
walk = trace_kempe_cycle(edges, col_list, merged_idx, (cyc_a, cyc_b))
walk_edges = [w[0] for w in walk]
leave_at = [w[1] for w in walk]
# K' position counter. For each old cycle edge e in cyclic order, position
# increments by 1 normally; for e1/e2, increments by 2 (two halves).
# For e1, we record (entry_half_color, exit_half_color) where entry_half
# is the half adjacent to entry_vertex and exit_half adjacent to leaving.
e1_entry_color = e1_exit_color = None
e2_entry_color = e2_exit_color = None
e1_entry_vertex = e1_exit_vertex = None
e2_entry_vertex = e2_exit_vertex = None
other_new_colors = {} # ei -> new color (for cycle edges other than e1/e2)
pos = 0
for k, ei in enumerate(walk_edges):
leaving = leave_at[k]
u, v = edges[ei][0], edges[ei][1]
entry = v if leaving == u else u
if ei == e1:
c_entry = cyc_b if pos % 2 == 0 else cyc_a
pos += 1
c_exit = cyc_b if pos % 2 == 0 else cyc_a
pos += 1
e1_entry_color = c_entry; e1_exit_color = c_exit
e1_entry_vertex = entry; e1_exit_vertex = leaving
elif ei == e2:
c_entry = cyc_b if pos % 2 == 0 else cyc_a
pos += 1
c_exit = cyc_b if pos % 2 == 0 else cyc_a
pos += 1
e2_entry_color = c_entry; e2_exit_color = c_exit
e2_entry_vertex = entry; e2_exit_vertex = leaving
else:
nc = cyc_b if pos % 2 == 0 else cyc_a
other_new_colors[ei] = nc
pos += 1
# Identify which half of e1 is on f_n: it's the half adjacent to e_F.
# e_F has 2 endpoints; one is shared with e1, one with e2.
e_F_endpoints = set(edges[e_F])
e1_endpoints = set(edges[e1])
e2_endpoints = set(edges[e2])
shared_e1_eF = (e_F_endpoints & e1_endpoints).pop()
shared_e2_eF = (e_F_endpoints & e2_endpoints).pop()
# The half of e1 on f_n connects X_1 to shared_e1_eF. So if shared_e1_eF
# equals e1_entry_vertex, the half on f_n is the "entry half"; else the
# "exit half".
if shared_e1_eF == e1_entry_vertex:
e1_h_color = e1_entry_color
else:
e1_h_color = e1_exit_color
if shared_e2_eF == e2_entry_vertex:
e2_h_color = e2_entry_color
else:
e2_h_color = e2_exit_color
# Determine e_F's new colour: if e_F is on the Kempe cycle (other_new_colors)
# use that, else original.
if e_F in other_new_colors:
e_F_color = other_new_colors[e_F]
else:
e_F_color = col_list[e_F]
# f_n's 4 edge colors: [new edge X_1-X_2 = c, e1_h, e_F, e2_h]
fn_colors = [c, e1_h_color, e_F_color, e2_h_color]
fn_distinct = set(fn_colors)
uses_3_colors = (len(fn_distinct) == 3)
if uses_3_colors:
return True # clause 4(i) satisfied
# Otherwise, check clause 4(ii): the {b,c}-Kempe cycle through the new
# edge has only X_1-X_2 in f_n's boundary.
# We need to actually build the modified graph and trace this cycle.
return check_clause_4_kempe_part(H, edges, col_list, named, witness,
a, b, c, e1_h_color, e2_h_color,
other_new_colors,
e1_entry_vertex, e1_exit_vertex,
e2_entry_vertex, e2_exit_vertex,
e1_entry_color, e1_exit_color,
e2_entry_color, e2_exit_color)
def check_clause_4_kempe_part(H, edges, col_list, named, witness, a, b, c,
e1_h_color, e2_h_color, other_new_colors,
e1_ev, e1_xv, e2_ev, e2_xv,
e1_ec, e1_xc, e2_ec, e2_xc):
"""Build modified H' and check whether the {b,c}-Kempe cycle through X1-X2
has only that one edge in partial(f_n)."""
e1 = witness['e1']; e2 = witness['e2']; e_F = witness['e_F']
H2 = H.copy()
X1 = max(v for v in H.vertices(sort=False) if isinstance(v, int)) + 1
X2 = X1 + 1
H2.add_vertex(X1); H2.add_vertex(X2)
e1_uv = tuple(edges[e1]); e2_uv = tuple(edges[e2])
H2.delete_edge(e1_uv); H2.delete_edge(e2_uv)
H2.add_edges([(e1_uv[0], X1), (X1, e1_uv[1]),
(e2_uv[0], X2), (X2, e2_uv[1]),
(X1, X2)])
# Build coloring for H2
new_coloring = {}
# Copy non-modified edges: original color (unless in other_new_colors)
for ei, c0 in enumerate(col_list):
if ei == e1 or ei == e2: continue
e_fs = frozenset(edges[ei])
if ei in other_new_colors:
new_coloring[e_fs] = other_new_colors[ei]
else:
new_coloring[e_fs] = c0
# Halves of e1, e2
new_coloring[frozenset((e1_ev, X1))] = e1_ec
new_coloring[frozenset((X1, e1_xv))] = e1_xc
new_coloring[frozenset((e2_ev, X2))] = e2_ec
new_coloring[frozenset((X2, e2_xv))] = e2_xc
new_coloring[frozenset((X1, X2))] = c
# Determine f_n's edges (in H2): new edge X1-X2, the half of e1 adjacent
# to e_F's shared vertex with e1, e_F itself, and the half of e2 adjacent
# to e_F's shared vertex with e2.
e_F_endpoints = set(edges[e_F])
e1_endpoints = set(edges[e1])
e2_endpoints = set(edges[e2])
shared_e1_eF = (e_F_endpoints & e1_endpoints).pop()
shared_e2_eF = (e_F_endpoints & e2_endpoints).pop()
fn_edges = {
frozenset((X1, X2)),
frozenset((X1, shared_e1_eF)),
frozenset(edges[e_F]),
frozenset((X2, shared_e2_eF)),
}
# Build edge list of H2 + coloring list
H2_edges = list(H2.edges(labels=False))
H2_col_list = [new_coloring[frozenset(e)] for e in H2_edges]
# Sanity: verify phi' is a proper 3-edge-colouring of H2 (the conjecture
# asserts this; if the construction is wrong, abort).
for v in H2.vertex_iterator():
seen = []
for w in H2.neighbor_iterator(v):
seen.append(new_coloring[frozenset((v, w))])
if len(set(seen)) != len(seen):
raise RuntimeError(
f"phi' is not proper at vertex {v}: colors {seen}")
# Trace the {b,c}-Kempe cycle through X1-X2 in H2 using new_coloring
H2_X1X2_idx = None
for i, e in enumerate(H2_edges):
if frozenset(e) == frozenset((X1, X2)):
H2_X1X2_idx = i; break
if H2_X1X2_idx is None:
return False
kc_bc = kempe_cycle_set(H2_edges, H2_col_list, H2_X1X2_idx, (b, c))
# Count edges in kc_bc that are in fn_edges
count = 0
for ei in kc_bc:
if frozenset(H2_edges[ei]) in fn_edges:
count += 1
return count == 1
def main(max_n=18, time_budget_per_n=3600):
rows = []
for n in range(12, max_n + 1):
start = time.time()
try:
triangulations = list(graphs.triangulations(n, minimum_degree=5))
except Exception as ex:
rows.append((n, 0, 0, 0, f"cannot enumerate"))
continue
n_tri = len(triangulations)
total_col = 0
total_pass = 0
timed_out = False
for tri_idx, G in enumerate(triangulations, start=1):
if time.time() - start > time_budget_per_n:
timed_out = True; break
G.is_planar(set_embedding=True)
D = dual_of(G); D.is_planar(set_embedding=True)
for face in D.faces():
if len(face) != 5: continue
if time.time() - start > time_budget_per_n:
timed_out = True; break
for i_red in range(5):
res = apply_reduction(D, face, i_red, 9999)
if res is None: continue
H = res['H']; named = res['named']
H.is_planar(set_embedding=True)
edges, colorings = proper_3_edge_colorings(H)
cand = [c for c in colorings
if matches_chord_apex_kempe(edges, c, named)]
for col in cand:
witnesses = find_all_36_witnesses(H, edges, list(col),
named)
if not witnesses:
continue
total_col += 1
ok = False
for w in witnesses:
try:
if check_clause_4(H, edges, list(col), named,
w):
ok = True
break
except Exception:
pass
if ok:
total_pass += 1
elapsed = time.time() - start
status = (f"TIMEOUT ({elapsed:.0f}s)" if timed_out
else f"complete ({elapsed:.0f}s)")
rows.append((n, n_tri, total_col, total_pass, status))
print(f"n={n}: {n_tri} tri, {total_col} cand witnesses, "
f"{total_pass} pass clause 4, {status}")
sys.stdout.flush()
print()
print("=" * 70)
print(f"{'n':>3} {'#tri':>5} {'#witness':>10} {'#pass_cl4':>10} {'status':>25}")
print("-" * 70)
for n, n_tri, n_col, n_pass, status in rows:
print(f"{n:>3} {n_tri:>5} {n_col:>10} {n_pass:>10} {status:>25}")
if __name__ == '__main__':
main()
@@ -130,11 +130,12 @@ def matches_chord_apex_kempe(edges, col, named):
def conjecture_holds_for(H, edges, col, named): def conjecture_holds_for(H, edges, col, named):
"""Returns (F, e1, e2, kc) if some face F has two same-color edges (e1, e2) """Returns (F, e1, e2, kc) if some face F of H has two same-colour edges
and both, together with merged, lie on a Kempe cycle kc. Else None.""" (e1, e2), neither equal to merged, both on a Kempe cycle kc through
merged, AND exactly one edge of partial F lies between e1 and e2 along
one of the two arcs of partial F. Else None."""
merged_idx = edge_idx(edges, named['merged']) merged_idx = edge_idx(edges, named['merged'])
c_merged = col[merged_idx] c_merged = col[merged_idx]
# All Kempe cycles through merged (one per color pair (c_merged, c'))
kempe_cycles = [] kempe_cycles = []
for c_prime in range(3): for c_prime in range(3):
if c_prime == c_merged: continue if c_prime == c_merged: continue
@@ -146,10 +147,17 @@ def conjecture_holds_for(H, edges, col, named):
ei = edge_idx(edges, frozenset((u, v))) ei = edge_idx(edges, frozenset((u, v)))
if ei is not None: if ei is not None:
face_edge_indices.append(ei) face_edge_indices.append(ei)
for i in range(len(face_edge_indices)): n_face = len(face_edge_indices)
for j in range(i + 1, len(face_edge_indices)): for i in range(n_face):
for j in range(i + 1, n_face):
e1, e2 = face_edge_indices[i], face_edge_indices[j] e1, e2 = face_edge_indices[i], face_edge_indices[j]
if e1 == merged_idx or e2 == merged_idx: continue
if col[e1] != col[e2]: continue if col[e1] != col[e2]: continue
# exactly one edge between e1 and e2 in one arc
gap_a = (j - i - 1)
gap_b = (n_face - 2 - gap_a)
if gap_a != 1 and gap_b != 1:
continue
for kc in kempe_cycles: for kc in kempe_cycles:
if e1 in kc and e2 in kc: if e1 in kc and e2 in kc:
return face, e1, e2, kc return face, e1, e2, kc
@@ -0,0 +1,209 @@
"""Test Conjecture 3.6 (with the 4-edge-face criterion) across all
min-degree-5 triangulations up to n = 18. Aggregates per-n totals.
Run with: sage experiments/check_conj_face_kempe_scaled.py
"""
from sage.all import Graph
from sage.graphs.graph_generators import graphs
import sys
import time
def dual_of(G):
G.is_planar(set_embedding=True)
faces = G.faces()
edge_to_faces = {}
for fi, face in enumerate(faces):
for u, v in face:
edge_to_faces.setdefault(frozenset((u, v)), []).append(fi)
return Graph(
[(fs[0], fs[1]) for fs in edge_to_faces.values() if len(fs) == 2],
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 or 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])
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
return {
'H': H,
'named': {
'spike': frozenset(spike),
'side_0': frozenset(side_0),
'side_1': frozenset(side_1),
'merged': frozenset(merged),
},
}
def proper_3_edge_colorings(G):
edges = list(G.edges(labels=False))
n = len(edges)
adj = [[] for _ in range(n)]
for i in range(n):
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
results = []
def back(k):
if k == n:
results.append(tuple(coloring)); return
for c in range(3):
if all(coloring[j] != c for j in adj[k]):
coloring[k] = c
back(k + 1)
coloring[k] = -1
back(0)
return edges, results
def kempe_cycle(edges, coloring, start_idx, color_pair):
a, b = color_pair
if coloring[start_idx] not in (a, b):
return set()
in_sub = set(i for i in range(len(edges)) if coloring[i] in (a, b))
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 edge_idx(edges, e_frozen):
for i, e in enumerate(edges):
if frozenset((e[0], e[1])) == e_frozen:
return i
return None
def matches_chord_apex_kempe(edges, col, named):
idx = {role: edge_idx(edges, ns) for role, ns in named.items()}
if any(v is None for v in idx.values()): 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 conjecture_holds_for(H, edges, col, named):
"""Full Conjecture 3.6 check: face F with two same-colour edges e1, e2
(neither equal to merged), both on a common Kempe cycle through merged,
and exactly one edge of partial F lies between them in one arc."""
merged_idx = edge_idx(edges, named['merged'])
c_merged = col[merged_idx]
kempe_cycles = []
for c_prime in range(3):
if c_prime == c_merged: continue
kc = kempe_cycle(edges, col, merged_idx, (c_merged, c_prime))
kempe_cycles.append(kc)
for face in H.faces():
face_edge_indices = []
for u, v in face:
ei = edge_idx(edges, frozenset((u, v)))
if ei is not None:
face_edge_indices.append(ei)
n_face = len(face_edge_indices)
for i in range(n_face):
for j in range(i + 1, n_face):
e1, e2 = face_edge_indices[i], face_edge_indices[j]
if e1 == merged_idx or e2 == merged_idx: continue
if col[e1] != col[e2]: continue
gap_a = (j - i - 1)
gap_b = (n_face - 2 - gap_a)
if gap_a != 1 and gap_b != 1:
continue
for kc in kempe_cycles:
if e1 in kc and e2 in kc:
return True
return False
def main(max_n=22, time_budget_per_n=1800):
rows = []
for n in range(12, max_n + 1):
start = time.time()
try:
triangulations = list(graphs.triangulations(n, minimum_degree=5))
except Exception as ex:
print(f"n={n}: cannot enumerate: {ex}")
rows.append((n, None, None, None, 'cannot enumerate'))
continue
n_tri = len(triangulations)
total_col = 0
total_pass = 0
timed_out = False
for tri_idx, G in enumerate(triangulations, start=1):
if time.time() - start > time_budget_per_n:
timed_out = True
break
G.is_planar(set_embedding=True)
D = dual_of(G); D.is_planar(set_embedding=True)
for face in D.faces():
if len(face) != 5: continue
if time.time() - start > time_budget_per_n:
timed_out = True
break
for i_red in range(5):
res = apply_reduction(D, face, i_red, 9999)
if res is None: continue
H = res['H']; named = res['named']
H.is_planar(set_embedding=True)
edges, colorings = proper_3_edge_colorings(H)
cand = [c for c in colorings
if matches_chord_apex_kempe(edges, c, named)]
for col in cand:
total_col += 1
if conjecture_holds_for(H, edges, col, named):
total_pass += 1
elapsed = time.time() - start
status = (f"TIMEOUT after {elapsed:.0f}s" if timed_out
else f"complete ({elapsed:.0f}s)")
rows.append((n, n_tri, total_col, total_pass, status))
print(f"n={n}: {n_tri} tri, {total_col} colorings, "
f"{total_pass} pass, {status}")
sys.stdout.flush()
print()
print("=" * 60)
print(f"{'n':>3} {'#tri':>5} {'#col':>10} {'#pass':>10} {'status':>20}")
print("-" * 60)
for n, n_tri, n_col, n_pass, status in rows:
n_tri_s = str(n_tri) if n_tri is not None else '-'
n_col_s = str(n_col) if n_col is not None else '-'
n_pass_s = str(n_pass) if n_pass is not None else '-'
print(f"{n:>3} {n_tri_s:>5} {n_col_s:>10} {n_pass_s:>10} {status:>20}")
if __name__ == '__main__':
main()
@@ -0,0 +1,122 @@
"""Draw a 3-panel illustration of cubic-graph edge contraction:
(1) the original cubic graph fragment with edge e = uv highlighted;
(2) after deleting e (u, v are degree-2);
(3) after smoothing u, v (gone, replaced by single edges).
Produces fig_cubic_edge_contraction.png.
"""
import os
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch
OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DARK = '#374151'
GRAY = '#9ca3af'
HIGHLIGHT = '#dc2626' # the edge being contracted (panel 1)
GHOST = '#fca5a5' # removed edges (panel 2)
DEG2 = '#f59e0b' # degree-2 vertices (panel 2)
NEW = '#2563eb' # smoothed-in new edges (panel 3)
# Positions: u centered at (-1, 0), v at (1, 0); their outer neighbors angled.
pos = {
'u': (-1.0, 0.0),
'v': ( 1.0, 0.0),
'a': (-2.2, 1.0),
'b': (-2.2, -1.0),
'c': ( 2.2, 1.0),
'd': ( 2.2, -1.0),
}
def draw_vertex(ax, p, color, size=110, label=None, label_offset=(0, 0.22)):
ax.scatter([p[0]], [p[1]], s=size, color=color, zorder=4)
if label is not None:
ax.text(p[0] + label_offset[0], p[1] + label_offset[1], label,
ha='center', va='center', fontsize=12, zorder=5,
color=DARK)
def draw_edge(ax, p, q, color, lw=2.0, ls='-', zorder=2):
ax.plot([p[0], q[0]], [p[1], q[1]], color=color, lw=lw, ls=ls,
zorder=zorder, solid_capstyle='round')
def panel_before(ax):
# Outer edges (gray)
for (x, y) in [('a', 'u'), ('b', 'u'), ('c', 'v'), ('d', 'v')]:
draw_edge(ax, pos[x], pos[y], DARK, lw=2.0)
# The highlighted edge e = uv
draw_edge(ax, pos['u'], pos['v'], HIGHLIGHT, lw=3.2)
# Vertices
for v in ('a', 'b', 'c', 'd'):
draw_vertex(ax, pos[v], DARK, size=60)
draw_vertex(ax, pos['u'], DARK, size=120, label='$u$',
label_offset=(-0.05, 0.28))
draw_vertex(ax, pos['v'], DARK, size=120, label='$v$',
label_offset=(0.05, 0.28))
# Label on the edge
mid = ((pos['u'][0] + pos['v'][0]) / 2,
(pos['u'][1] + pos['v'][1]) / 2)
ax.text(mid[0], mid[1] + 0.25, '$e$', ha='center', va='center',
fontsize=13, color=HIGHLIGHT, zorder=5)
ax.set_title('(1) cubic plane graph with edge $e = uv$',
fontsize=11, color=DARK, pad=8)
def panel_after_delete(ax):
# Outer edges (gray)
for (x, y) in [('a', 'u'), ('b', 'u'), ('c', 'v'), ('d', 'v')]:
draw_edge(ax, pos[x], pos[y], DARK, lw=2.0)
# Ghost the deleted edge
draw_edge(ax, pos['u'], pos['v'], GHOST, lw=2.0, ls=':')
# Vertices: u, v are now degree-2 (highlighted color)
for v in ('a', 'b', 'c', 'd'):
draw_vertex(ax, pos[v], DARK, size=60)
draw_vertex(ax, pos['u'], DEG2, size=140, label='$u$',
label_offset=(-0.05, 0.32))
draw_vertex(ax, pos['v'], DEG2, size=140, label='$v$',
label_offset=(0.05, 0.32))
ax.set_title('(2) delete $e$: $u, v$ now have degree $2$',
fontsize=11, color=DARK, pad=8)
def panel_after_smooth(ax):
# The smoothed-in new edges
draw_edge(ax, pos['a'], pos['b'], NEW, lw=3.0)
draw_edge(ax, pos['c'], pos['d'], NEW, lw=3.0)
# Outer vertices remain
for v in ('a', 'b', 'c', 'd'):
draw_vertex(ax, pos[v], DARK, size=60)
# u, v are gone — show their former positions as faint markers
ax.scatter([pos['u'][0], pos['v'][0]], [pos['u'][1], pos['v'][1]],
s=140, facecolors='none', edgecolors=GRAY, lw=1.0,
linestyles='--', zorder=3)
ax.text(pos['u'][0], pos['u'][1] + 0.32, '$u$ gone',
ha='center', va='center', fontsize=9, color=GRAY)
ax.text(pos['v'][0], pos['v'][1] + 0.32, '$v$ gone',
ha='center', va='center', fontsize=9, color=GRAY)
ax.set_title('(3) smooth $u, v$: their incident edges merge',
fontsize=11, color=DARK, pad=8)
def main():
fig, axes = plt.subplots(1, 3, figsize=(13.5, 4.2))
for ax in axes:
ax.set_xlim(-3.0, 3.0)
ax.set_ylim(-1.7, 1.7)
ax.set_aspect('equal')
ax.axis('off')
panel_before(axes[0])
panel_after_delete(axes[1])
panel_after_smooth(axes[2])
plt.subplots_adjust(left=0.02, right=0.98, top=0.92, bottom=0.04,
wspace=0.05)
out = os.path.join(OUT_DIR, 'fig_cubic_edge_contraction.png')
plt.savefig(out, dpi=180, bbox_inches='tight')
print(f"wrote {out}")
if __name__ == '__main__':
main()
@@ -0,0 +1,205 @@
"""Two-panel illustration of Theorem (cubic contraction across a 4-face).
Left: H near the 4-face, with the forced 3-edge-colouring
e_0=a, e_1=b, e_2=e_3=c, w_0=w_1=b, w_2=w_3=a.
Right: H' after cubic-graph edge contraction on e_0, with the new colouring
(e_2', e_3' both b; e_1 recoloured to c; everything else unchanged).
Produces fig_thm_cubic_contraction_4face.png.
"""
import os
import matplotlib.pyplot as plt
OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DARK = '#374151'
GRAY = '#9ca3af'
GHOST = '#fca5a5'
DEG2 = '#f59e0b'
# Colour code: a=orange-ish, b=blue, c=green. Chosen colourblind-friendly.
COL_A = '#ea580c' # 'a'
COL_B = '#2563eb' # 'b'
COL_C = '#16a34a' # 'c'
# Positions of the 4-face vertices and their external neighbours.
pos = {
'v0': (0.0, 0.0),
'v1': (2.4, 0.0),
'v2': (2.4, 2.4),
'v3': (0.0, 2.4),
'u0': (-1.5, -0.8),
'u1': ( 3.9, -0.8),
'u2': ( 3.9, 3.2),
'u3': (-1.5, 3.2),
}
def draw_edge(ax, p, q, color, lw=2.4, ls='-', zorder=2):
ax.plot([p[0], q[0]], [p[1], q[1]], color=color, lw=lw, ls=ls,
zorder=zorder, solid_capstyle='round')
def draw_vertex(ax, p, color=DARK, size=70, zorder=4):
ax.scatter([p[0]], [p[1]], s=size, color=color, zorder=zorder)
def label_vertex(ax, p, text, offset=(0.0, 0.28), fontsize=12, color=DARK):
ax.text(p[0] + offset[0], p[1] + offset[1], text,
ha='center', va='center', fontsize=fontsize, color=color,
zorder=5)
def label_edge(ax, p, q, text, offset=(0.0, 0.0), color=DARK, fontsize=11):
mid = ((p[0] + q[0]) / 2 + offset[0], (p[1] + q[1]) / 2 + offset[1])
ax.text(mid[0], mid[1], text, ha='center', va='center',
fontsize=fontsize, color=color, zorder=5,
bbox=dict(boxstyle='round,pad=0.15', facecolor='white',
edgecolor='none', alpha=0.85))
def shade_face(ax, vs, color='#fef9c3', alpha=0.7):
xs = [p[0] for p in vs] + [vs[0][0]]
ys = [p[1] for p in vs] + [vs[0][1]]
ax.fill(xs, ys, color=color, alpha=alpha, zorder=1)
def panel_before(ax):
# 4-face shading
shade_face(ax, [pos['v0'], pos['v1'], pos['v2'], pos['v3']])
# Face label
ax.text(1.2, 1.2, '$f$', ha='center', va='center', fontsize=14,
color=GRAY, style='italic', zorder=2)
# Face edges
draw_edge(ax, pos['v0'], pos['v1'], COL_A) # e_0 = a
draw_edge(ax, pos['v2'], pos['v3'], COL_B) # e_1 = b
draw_edge(ax, pos['v1'], pos['v2'], COL_C) # e_2 = c
draw_edge(ax, pos['v3'], pos['v0'], COL_C) # e_3 = c
# External edges
draw_edge(ax, pos['v0'], pos['u0'], COL_B) # w_0 = b
draw_edge(ax, pos['v1'], pos['u1'], COL_B) # w_1 = b
draw_edge(ax, pos['v2'], pos['u2'], COL_A) # w_2 = a
draw_edge(ax, pos['v3'], pos['u3'], COL_A) # w_3 = a
# Vertices
for v in ('v0', 'v1', 'v2', 'v3'):
draw_vertex(ax, pos[v], DARK, size=90)
for u in ('u0', 'u1', 'u2', 'u3'):
draw_vertex(ax, pos[u], DARK, size=60)
# Labels
label_vertex(ax, pos['v0'], '$v_0$', offset=(-0.20, -0.25))
label_vertex(ax, pos['v1'], '$v_1$', offset=( 0.20, -0.25))
label_vertex(ax, pos['v2'], '$v_2$', offset=( 0.20, 0.25))
label_vertex(ax, pos['v3'], '$v_3$', offset=(-0.20, 0.25))
label_vertex(ax, pos['u0'], '$u_0$', offset=(-0.25, 0.00))
label_vertex(ax, pos['u1'], '$u_1$', offset=( 0.25, 0.00))
label_vertex(ax, pos['u2'], '$u_2$', offset=( 0.25, 0.00))
label_vertex(ax, pos['u3'], '$u_3$', offset=(-0.25, 0.00))
# Edge labels with colour
label_edge(ax, pos['v0'], pos['v1'], r'$e_0\!=\!a$', offset=(0, -0.18),
color=COL_A)
label_edge(ax, pos['v2'], pos['v3'], r'$e_1\!=\!b$', offset=(0, 0.18),
color=COL_B)
label_edge(ax, pos['v1'], pos['v2'], r'$e_2\!=\!c$', offset=(0.30, 0.0),
color=COL_C)
label_edge(ax, pos['v3'], pos['v0'], r'$e_3\!=\!c$', offset=(-0.30, 0.0),
color=COL_C)
label_edge(ax, pos['v0'], pos['u0'], r'$w_0\!=\!b$',
offset=(-0.05, -0.05), color=COL_B, fontsize=10)
label_edge(ax, pos['v1'], pos['u1'], r'$w_1\!=\!b$',
offset=( 0.05, -0.05), color=COL_B, fontsize=10)
label_edge(ax, pos['v2'], pos['u2'], r'$w_2\!=\!a$',
offset=( 0.05, 0.05), color=COL_A, fontsize=10)
label_edge(ax, pos['v3'], pos['u3'], r'$w_3\!=\!a$',
offset=(-0.05, 0.05), color=COL_A, fontsize=10)
ax.set_title('$H$ with proper $3$-edge-colouring $\\varphi$:\n'
'$\\varphi(e_0)=a$, $\\varphi(e_1)=b$ (opposite, different)',
fontsize=11, color=DARK, pad=10)
def panel_after(ax):
# New 'face' shading: now (v_2, v_3) connected via e_1, plus new edges
# form a hexagonal-ish region. Just shade the area lightly.
shade_face(ax,
[pos['u0'], pos['v3'], pos['v2'], pos['u1'],
(pos['u1'][0] + 0.7, pos['u1'][1] - 0.7),
(pos['u0'][0] - 0.7, pos['u0'][1] - 0.7)],
color='#fef9c3', alpha=0.0) # invisible, just spacing
# Ghost the deleted edges (e_0, e_2, e_3, w_0, w_1) and former vertices
draw_edge(ax, pos['v0'], pos['v1'], GHOST, lw=1.5, ls=':') # e_0
draw_edge(ax, pos['v1'], pos['v2'], GHOST, lw=1.5, ls=':') # e_2
draw_edge(ax, pos['v3'], pos['v0'], GHOST, lw=1.5, ls=':') # e_3
draw_edge(ax, pos['v0'], pos['u0'], GHOST, lw=1.5, ls=':') # w_0
draw_edge(ax, pos['v1'], pos['u1'], GHOST, lw=1.5, ls=':') # w_1
# Surviving / recoloured edges
draw_edge(ax, pos['v2'], pos['v3'], COL_C, lw=3.0) # e_1 recoloured to c
draw_edge(ax, pos['v2'], pos['u2'], COL_A) # w_2 = a
draw_edge(ax, pos['v3'], pos['u3'], COL_A) # w_3 = a
# Smoothed-in new edges: e_2' from v_2 to u_1, e_3' from v_3 to u_0
draw_edge(ax, pos['v2'], pos['u1'], COL_B, lw=3.0)
draw_edge(ax, pos['v3'], pos['u0'], COL_B, lw=3.0)
# Vertices: v_0, v_1 removed; show their former positions faintly
for ghost in ('v0', 'v1'):
ax.scatter([pos[ghost][0]], [pos[ghost][1]], s=120,
facecolors='none', edgecolors=GRAY, lw=1.0,
linestyles='--', zorder=3)
for v in ('v2', 'v3'):
draw_vertex(ax, pos[v], DARK, size=90)
for u in ('u0', 'u1', 'u2', 'u3'):
draw_vertex(ax, pos[u], DARK, size=60)
# Labels
ax.text(pos['v0'][0], pos['v0'][1] - 0.30, '$v_0$ gone',
ha='center', va='center', fontsize=9, color=GRAY)
ax.text(pos['v1'][0], pos['v1'][1] - 0.30, '$v_1$ gone',
ha='center', va='center', fontsize=9, color=GRAY)
label_vertex(ax, pos['v2'], '$v_2$', offset=( 0.20, 0.25))
label_vertex(ax, pos['v3'], '$v_3$', offset=(-0.20, 0.25))
label_vertex(ax, pos['u0'], '$u_0$', offset=(-0.25, 0.00))
label_vertex(ax, pos['u1'], '$u_1$', offset=( 0.25, 0.00))
label_vertex(ax, pos['u2'], '$u_2$', offset=( 0.25, 0.00))
label_vertex(ax, pos['u3'], '$u_3$', offset=(-0.25, 0.00))
# Edge labels for the new/recoloured edges
label_edge(ax, pos['v2'], pos['v3'], r'$e_1\!=\!c$', offset=(0, 0.18),
color=COL_C)
label_edge(ax, pos['v2'], pos['u1'], r"$e_2'\!=\!b$",
offset=(0.20, 0.10), color=COL_B)
label_edge(ax, pos['v3'], pos['u0'], r"$e_3'\!=\!b$",
offset=(-0.20, 0.10), color=COL_B)
label_edge(ax, pos['v2'], pos['u2'], r'$w_2\!=\!a$',
offset=( 0.05, 0.05), color=COL_A, fontsize=10)
label_edge(ax, pos['v3'], pos['u3'], r'$w_3\!=\!a$',
offset=(-0.05, 0.05), color=COL_A, fontsize=10)
ax.set_title("$H'$ after cubic contraction of $e_0$:\n"
r"$e_2', e_3'$ get colour $b$; $e_1$ recoloured to $c$",
fontsize=11, color=DARK, pad=10)
def main():
fig, axes = plt.subplots(1, 2, figsize=(13, 5.8))
for ax in axes:
ax.set_xlim(-2.6, 4.8)
ax.set_ylim(-2.0, 4.2)
ax.set_aspect('equal')
ax.axis('off')
panel_before(axes[0])
panel_after(axes[1])
plt.subplots_adjust(left=0.02, right=0.98, top=0.92, bottom=0.04,
wspace=0.05)
out = os.path.join(OUT_DIR, 'fig_thm_cubic_contraction_4face.png')
plt.savefig(out, dpi=180, bbox_inches='tight')
print(f"wrote {out}")
if __name__ == '__main__':
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

@@ -20,11 +20,20 @@
\@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 } \@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{fig:iterated-reduction-trace}{{3}{8}}
\newlabel{lem:exactly-one-match}{{3.4}{8}} \newlabel{lem:exactly-one-match}{{3.4}{8}}
\newlabel{lem:all-distinct-exists}{{3.5}{9}}
\newlabel{conj:face-monochromatic-pair-on-merged-kempe-cycle}{{3.6}{9}}
\newlabel{rem:conj-3-6-empirical}{{3.7}{10}}
\newlabel{conj:face-monochromatic-pair-strengthened}{{3.8}{10}}
\newlabel{rem:conj-3-8-empirical}{{3.9}{11}}
\newlabel{def:cubic-edge-contraction}{{3.10}{11}}
\newlabel{thm:cubic-contraction-4face}{{3.11}{11}}
\newlabel{tocindent-1}{0pt} \newlabel{tocindent-1}{0pt}
\newlabel{tocindent0}{0pt} \newlabel{tocindent0}{0pt}
\newlabel{tocindent1}{17.77782pt} \newlabel{tocindent1}{17.77782pt}
\newlabel{tocindent2}{0pt} \newlabel{tocindent2}{0pt}
\newlabel{tocindent3}{0pt} \newlabel{tocindent3}{0pt}
\newlabel{lem:all-distinct-exists}{{3.5}{9}} \@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces Cubic-graph edge contraction (Definition\nonbreakingspace 3.10\hbox {}). Left: a fragment of a cubic plane graph with the contracted edge $e = uv$ highlighted in red. Middle: deleting $e$ leaves $u$ and $v$ of degree\nonbreakingspace $2$. Right: smoothing $u$ and $v$ replaces each pair of incident edges by a single new edge, removing $u, v$ and giving a cubic plane graph again.}}{12}{}\protected@file@percent }
\newlabel{conj:face-monochromatic-pair-on-merged-kempe-cycle}{{3.6}{9}} \newlabel{fig:cubic-edge-contraction}{{4}{12}}
\gdef \@abspage@last{9} \@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces The recolouring used in the proof of Theorem\nonbreakingspace 3.11\hbox {}. Left: the $4$-face $f$ of $H$ under $\varphi $, with the forced colours $\varphi (e_0) = a$, $\varphi (e_1) = b$, $\varphi (e_2) = \varphi (e_3) = c$, $\varphi (w_0) = \varphi (w_1) = b$, and $\varphi (w_2) = \varphi (w_3) = a$. Right: the contracted graph $H'$ under $\varphi '$. The smoothed-in edges $e_2', e_3'$ inherit the colour $b$ from $w_0, w_1$, and $e_1$ is recoloured from $b$ to $c$; every edge outside the face neighbourhood keeps its $\varphi $-colour (dotted in red: the five edges of $H$ removed by the contraction).}}{13}{}\protected@file@percent }
\newlabel{fig:thm-cubic-contraction-4face}{{5}{13}}
\gdef \@abspage@last{13}
@@ -1,12 +1,12 @@
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 24 MAY 2026 11:25 This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 24 MAY 2026 13:27
entering extended mode entering extended mode
restricted \write18 enabled. restricted \write18 enabled.
file:line:error style messages enabled.
%&-line parsing enabled. %&-line parsing enabled.
**/Users/didericis/Code/math-research/papers/dual_decomposition_minimal_counterexamples/paper.tex **paper.tex
(/Users/didericis/Code/math-research/papers/dual_decomposition_minimal_counterexamples/paper.tex (./paper.tex
LaTeX2e <2021-11-15> patch level 1 LaTeX2e <2021-11-15> patch level 1
L3 programming layer <2022-02-24> (/usr/local/texlive/2022/texmf-dist/tex/latex/amscls/amsart.cls L3 programming layer <2022-02-24>
(/usr/local/texlive/2022/texmf-dist/tex/latex/amscls/amsart.cls
Document Class: amsart 2020/05/29 v2.20.6 Document Class: amsart 2020/05/29 v2.20.6
\linespacing=\dimen138 \linespacing=\dimen138
\normalparindent=\dimen139 \normalparindent=\dimen139
@@ -18,14 +18,17 @@ Package: amsmath 2021/10/15 v2.17l AMS math features
For additional information on amsmath, use the `?' option. For additional information on amsmath, use the `?' option.
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amstext.sty (/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amstext.sty
Package: amstext 2021/08/26 v2.01 AMS text Package: amstext 2021/08/26 v2.01 AMS text
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsgen.sty
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsgen.sty
File: amsgen.sty 1999/11/30 v2.0 generic functions File: amsgen.sty 1999/11/30 v2.0 generic functions
\@emptytoks=\toks16 \@emptytoks=\toks16
\ex@=\dimen140 \ex@=\dimen140
)) (/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsbsy.sty ))
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsbsy.sty
Package: amsbsy 1999/11/29 v1.2d Bold Symbols Package: amsbsy 1999/11/29 v1.2d Bold Symbols
\pmbraise@=\dimen141 \pmbraise@=\dimen141
) (/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsopn.sty )
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amsopn.sty
Package: amsopn 2021/08/26 v2.02 operator names Package: amsopn 2021/08/26 v2.02 operator names
) )
\inf@bad=\count185 \inf@bad=\count185
@@ -66,10 +69,13 @@ LaTeX Font Info: Redeclaring font encoding OMS on input line 744.
LaTeX Info: Redefining \[ on input line 2938. LaTeX Info: Redefining \[ on input line 2938.
LaTeX Info: Redefining \] on input line 2939. LaTeX Info: Redefining \] on input line 2939.
) )
LaTeX Font Info: Trying to load font information for U+msa on input line 397. LaTeX Font Info: Trying to load font information for U+msa on input line 397
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd .
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd
File: umsa.fd 2013/01/14 v3.01 AMS symbols A File: umsa.fd 2013/01/14 v3.01 AMS symbols A
) (/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amsfonts.sty )
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amsfonts.sty
Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support
\symAMSa=\mathgroup4 \symAMSa=\mathgroup4
\symAMSb=\mathgroup5 \symAMSb=\mathgroup5
@@ -100,33 +106,42 @@ LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold'
\thm@postskip=\skip55 \thm@postskip=\skip55
\thm@headsep=\skip56 \thm@headsep=\skip56
\dth@everypar=\toks26 \dth@everypar=\toks26
) (/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amssymb.sty )
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/amssymb.sty
Package: amssymb 2013/01/14 v3.01 AMS font symbols Package: amssymb 2013/01/14 v3.01 AMS font symbols
) (/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphicx.sty )
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphicx.sty
Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR)
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/keyval.sty
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/keyval.sty
Package: keyval 2014/10/28 v1.15 key=value parser (DPC) Package: keyval 2014/10/28 v1.15 key=value parser (DPC)
\KV@toks@=\toks27 \KV@toks@=\toks27
) (/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphics.sty )
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/graphics.sty
Package: graphics 2021/03/04 v1.4d Standard LaTeX Graphics (DPC,SPQR) Package: graphics 2021/03/04 v1.4d Standard LaTeX Graphics (DPC,SPQR)
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/trig.sty
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics/trig.sty
Package: trig 2021/08/11 v1.11 sin cos tan (DPC) Package: trig 2021/08/11 v1.11 sin cos tan (DPC)
) (/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-cfg/graphics.cfg )
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
) )
Package graphics Info: Driver file: pdftex.def on input line 107. Package graphics Info: Driver file: pdftex.def on input line 107.
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-def/pdftex.def
(/usr/local/texlive/2022/texmf-dist/tex/latex/graphics-def/pdftex.def
File: pdftex.def 2020/10/05 v1.2a Graphics/color driver for pdftex File: pdftex.def 2020/10/05 v1.2a Graphics/color driver for pdftex
)) ))
\Gin@req@height=\dimen150 \Gin@req@height=\dimen150
\Gin@req@width=\dimen151 \Gin@req@width=\dimen151
) )
\c@theorem=\count272 \c@theorem=\count272
(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX) File: l3backend-pdftex.def 2022-02-07 L3 backend support: PDF output (pdfTeX)
\l__color_backend_stack_int=\count273 \l__color_backend_stack_int=\count273
\l__pdf_internal_box=\box53 \l__pdf_internal_box=\box53
) (./paper.aux) )
(./paper.aux)
\openout1 = `paper.aux'. \openout1 = `paper.aux'.
LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 27. LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 27.
@@ -144,13 +159,17 @@ 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: Checking defaults for U/cmr/m/n on input line 27.
LaTeX Font Info: ... okay 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. 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 (/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsa.fd
File: umsa.fd 2013/01/14 v3.01 AMS symbols A 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 27. 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
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsfonts/umsb.fd
File: umsb.fd 2013/01/14 v3.01 AMS symbols B File: umsb.fd 2013/01/14 v3.01 AMS symbols B
) (/usr/local/texlive/2022/texmf-dist/tex/context/base/mkii/supp-pdf.mkii )
(/usr/local/texlive/2022/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
[Loading MPS to PDF converter (version 2006.09.02).] [Loading MPS to PDF converter (version 2006.09.02).]
\scratchcounter=\count274 \scratchcounter=\count274
\scratchdimen=\dimen152 \scratchdimen=\dimen152
@@ -165,12 +184,18 @@ File: umsb.fd 2013/01/14 v3.01 AMS symbols B
\everyMPtoPDFconversion=\toks29 \everyMPtoPDFconversion=\toks29
) (/usr/local/texlive/2022/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty ) (/usr/local/texlive/2022/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 485. Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
(/usr/local/texlive/2022/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg 85.
File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Live
)) [1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] (/usr/local/texlive/2022/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
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 145--147 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 around $\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 )$. []\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 )$.
[] []
<fig_reduced_dual_step1.png, id=17, 517.79329pt x 499.08812pt> <fig_reduced_dual_step1.png, id=17, 517.79329pt x 499.08812pt>
@@ -196,7 +221,8 @@ Package pdftex.def Info: fig_reduced_dual_step4.png used on input line 168.
LaTeX Warning: `h' float specifier changed to `ht'. LaTeX Warning: `h' float specifier changed to `ht'.
[2] [3 <./fig_reduced_dual_step1.png> <./fig_reduced_dual_step2.png> <./fig_reduced_dual_step3.png> <./fig_reduced_dual_step4.png>] [2] [3 <./fig_reduced_dual_step1.png> <./fig_reduced_dual_step2.png> <./fig_red
uced_dual_step3.png> <./fig_reduced_dual_step4.png>]
<fig_chord_apex_step1.png, id=34, 505.03976pt x 502.06393pt> <fig_chord_apex_step1.png, id=34, 505.03976pt x 502.06393pt>
File: fig_chord_apex_step1.png Graphic file (type png) File: fig_chord_apex_step1.png Graphic file (type png)
<use fig_chord_apex_step1.png> <use fig_chord_apex_step1.png>
@@ -216,9 +242,15 @@ Package pdftex.def Info: fig_chord_apex_step3.png used on input line 291.
LaTeX Warning: `h' float specifier changed to `ht'. LaTeX Warning: `h' float specifier changed to `ht'.
[4] [5 <./fig_chord_apex_step1.png> <./fig_chord_apex_step2.png> <./fig_chord_apex_step3.png>] [6] [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 433--440 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[]\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 )\OML/cmm/m/it/10 ; '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[]\OT1/cmr/m/n/10 )$ \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[
]\OT1/cmr/m/n/10 )\OML/cmm/m/it/10 ; '[]\OT1/cmr/m/n/10 (\OML/cmm/m/it/10 f[]\O
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, 399.6106pt x 459.55217pt> <fig_alg_step0.png, id=49, 399.6106pt x 459.55217pt>
@@ -238,17 +270,21 @@ Package pdftex.def Info: fig_alg_step2.png used on input line 481.
(pdftex.def) Requested size: 115.20264pt x 132.48134pt. (pdftex.def) Requested size: 115.20264pt x 132.48134pt.
Underfull \hbox (badness 4391) in paragraph at lines 498--498 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$ \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 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$ ver- \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 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[]$ \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[]$
[] []
@@ -261,20 +297,73 @@ 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 \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>] [9] (./paper.aux) ) [7] [8 <./fig_alg_step0.png> <./fig_alg_step1.png> <./fig_alg_step2.png>]
Here is how much of TeX's memory you used: [9] [10]
3082 strings out of 478268 Underfull \hbox (badness 1648) in paragraph at lines 705--710
44173 string characters out of 5846347 \OT1/cmr/m/it/10 Remark \OT1/cmr/m/n/10 3.9\OT1/cmr/m/it/10 . \OT1/cmr/m/n/10 T
344279 words of memory out of 5000000 he strength-ened con-jec-ture was tested on the same chord-
21118 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,1392b,298s stack positions out of 10000i,1000n,20000p,200000b,200000s Underfull \hbox (badness 1014) in paragraph at lines 705--710
</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></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi5.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr5.pfb></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></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb></usr/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/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/local/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> \OT1/cmr/m/n/10 apex+Kempe colour-ings as Re-mark 3.7[]; for each colour-ing we
Output written on paper.pdf (9 pages, 977618 bytes). sought any
PDF statistics: []
140 PDF objects out of 1000 (max. 8388607)
74 compressed objects within 1 object stream <fig_cubic_edge_contraction.png, id=75, 950.752pt x 203.159pt>
0 named destinations out of 1000 (max. 500000) File: fig_cubic_edge_contraction.png Graphic file (type png)
51 words of extra memory for PDF output out of 10000 (max. 10000000) <use fig_cubic_edge_contraction.png>
Package pdftex.def Info: fig_cubic_edge_contraction.png used on input line 769
.
(pdftex.def) Requested size: 341.9989pt x 73.08138pt.
LaTeX Warning: `h' float specifier changed to `ht'.
[11]
<fig_thm_cubic_contraction_4face.png, id=79, 916.223pt x 417.56pt>
File: fig_thm_cubic_contraction_4face.png Graphic file (type png)
<use fig_thm_cubic_contraction_4face.png>
Package pdftex.def Info: fig_thm_cubic_contraction_4face.png used on input lin
e 844.
(pdftex.def) Requested size: 352.79846pt x 160.78339pt.
LaTeX Warning: `h' float specifier changed to `ht'.
[12 <./fig_cubic_edge_contraction.png>] [13 <./fig_thm_cubic_contraction_4face.
png>] (./paper.aux) )
Here is how much of TeX's memory you used:
3112 strings out of 478268
44739 string characters out of 5846347
347356 words of memory out of 5000000
21143 multiletter control sequences out of 15000+600000
478077 words of font info for 62 fonts, out of 8000000 for 9000
1302 hyphenation exceptions out of 8191
69i,9n,76p,1306b,398s stack positions out of 10000i,1000n,20000p,200000b,200000s
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/ams
fonts/cm/cmbx10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsf
onts/cm/cmcsc10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsf
onts/cm/cmex10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfo
nts/cm/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfon
ts/cm/cmmi5.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts
/cm/cmmi7.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/c
m/cmmi9.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/
cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cm
r5.pfb></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
></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmr9.pfb></
usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb></u
sr/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/l
ocal/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy9.pfb></usr/loc
al/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb></usr/loca
l/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/t
exlive/2022/texmf-dist/fonts/type1/public/amsfonts/symbols/msam10.pfb>
Output written on paper.pdf (13 pages, 1144897 bytes).
PDF statistics:
172 PDF objects out of 1000 (max. 8388607)
92 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
61 words of extra memory for PDF output out of 10000 (max. 10000000)
@@ -610,10 +610,249 @@ a face $F$ of $\widehat{G}'_{v,i}$ and two distinct edges
$e_1, e_2 \in \partial F$, with neither $e_1$ nor $e_2$ equal to the merged $e_1, e_2 \in \partial F$, with neither $e_1$ nor $e_2$ equal to the merged
edge, such that edge, such that
\begin{enumerate} \begin{enumerate}
\item $\varphi(e_1) = \varphi(e_2)$, and \item $\varphi(e_1) = \varphi(e_2)$,
\item $e_1$, $e_2$, and the merged edge all lie on a common \item $e_1$, $e_2$, and the merged edge all lie on a common
$\{a, b\}$-Kempe cycle of $\varphi$. $\{a, b\}$-Kempe cycle of $\varphi$, and
\item exactly one edge of $\partial F$ lies between $e_1$ and $e_2$ along
one of the two arcs of $\partial F$; equivalently, subdividing $e_1$
and $e_2$ by new vertices $X_1, X_2$ and joining them by a new edge
$X_1 X_2$ inside $F$ creates a new face bounded by exactly $4$
edges (the new edge, the two subdivision halves adjacent to it, and
the single $\partial F$-edge between $e_1$ and $e_2$).
\end{enumerate} \end{enumerate}
\end{conjecture} \end{conjecture}
\begin{remark}
\label{rem:conj-3-6-empirical}
The conjecture cannot be tested on actual minimal counterexamples (none exist
by the Four Colour Theorem), but its conclusion is checkable on the
structural surrogates: proper $3$-edge-colourings of reduced duals that
satisfy both the chord-apex condition (Lemma~\ref{lem:chord-apex}) and the
Kempe-cycle conditions (Lemma~\ref{lem:kempe-spike}), since a counterexample's
reduced dual is forced to admit such colourings under any proper colouring.
For every min-degree-$5$ triangulation $G$ with $|V(G)| \leq 21$, every
pentagonal face $F$ of $G'$, and every reduction index
$i \in \{0,\dots,4\}$, we enumerated all such colourings and tested the
three clauses of Conjecture~\ref{conj:face-monochromatic-pair-on-merged-kempe-cycle}
(see \texttt{experiments/check\_conj\_face\_kempe\_scaled.py}); $n = 22$
ran past a $1800$\,s budget after $641{,}700$ colourings (all pass), but
did not finish the full set of $651$ triangulations:
\begin{center}
\small
\renewcommand{\arraystretch}{1.15}
\begin{tabular}{r|r|r|r|l}
$n$ & \#tri & \#col.\ tested & \#sat.\ & status \\
\hline
$12$ & $1$ & $0$ & --- & vacuous (icosahedron) \\
$13$ & $0$ & --- & --- & no min-deg-$5$ tri \\
$14$ & $1$ & $216$ & $216$ & all pass \\
$15$ & $1$ & $0$ & --- & vacuous \\
$16$ & $3$ & $864$ & $864$ & all pass \\
$17$ & $4$ & $4{,}650$ & $4{,}650$ & all pass \\
$18$ & $12$ & $8{,}070$ & $8{,}070$ & all pass \\
$19$ & $23$ & $21{,}138$ & $21{,}138$ & all pass \\
$20$ & $73$ & $107{,}874$ & $107{,}874$ & all pass \\
$21$ & $192$ & $392{,}370$ & $392{,}370$ & all pass \\
$22$ (part.) & $651$ & $641{,}700$ & $641{,}700$ & timeout \\
\hline
total ($n \le 21$) & $311$ & $535{,}182$ & $535{,}182$ & \\
\end{tabular}
\end{center}
\noindent The vacuous rows ($n = 12, 15$) are those where the relevant
reduced duals admit no proper $3$-edge-colouring satisfying chord-apex +
both Kempe-cycle conditions, so the conjecture has no content there. On
every $(G, F, i, \varphi)$ with content, all three clauses of the
conjecture hold simultaneously.
\end{remark}
\begin{conjecture}[Strengthening of Conjecture~\ref{conj:face-monochromatic-pair-on-merged-kempe-cycle}]
\label{conj:face-monochromatic-pair-strengthened}
Let $G$, $\widehat{G}'_{v,i}$, $\varphi$ be as in
Conjecture~\ref{conj:face-monochromatic-pair-on-merged-kempe-cycle}. Then
there exist $F$, $e_1$, $e_2$ satisfying clauses (1)--(3) of that
conjecture, and the following additional clause holds.
Let $X_1, X_2$ be the new vertices subdividing $e_1, e_2$, joined by a new
edge $X_1 X_2$ inside $F$; write $\widehat{G}'^{+}$ for the resulting
modified graph (which has $|V(\widehat{G}'_{v,i})|+2$ vertices and
$|E(\widehat{G}'_{v,i})|+3$ edges, is again cubic and plane, and admits a
proper $3$-edge-colouring). Let $\varphi'$ be the proper
$3$-edge-colouring of $\widehat{G}'^{+}$ obtained from $\varphi$ by
swapping the two colours along the (subdivided) $\{a, b\}$-Kempe cycle of
clause~(2) and assigning the new edge $X_1 X_2$ the remaining (third)
colour. In particular $\varphi'$ agrees with $\varphi$ on every edge of
$\widehat{G}'_{v,i}$ outside that Kempe cycle, and at $X_1$ and $X_2$ the
two subdivision halves take the colours $\{a, b\}$ in the order forced by
propriety. Write $a := \varphi(e_1) = \varphi(e_2)$,
$c := \varphi'(X_1 X_2)$, and let $b$ be the third colour. Let $f_n$ be
the new $4$-edge face of $\widehat{G}'^{+}$ incident to $X_1 X_2$. Then:
\begin{enumerate}
\setcounter{enumi}{3}
\item either
\begin{enumerate}
\item[(i)] $\partial f_n$ uses all three colours under
$\varphi'$, or
\item[(ii)] the $\{b, c\}$-Kempe cycle of $\varphi'$ through
$X_1 X_2$ is incident to exactly one edge of
$\partial f_n$ (namely $X_1 X_2$ itself).
\end{enumerate}
\end{enumerate}
\end{conjecture}
\begin{remark}
\label{rem:conj-3-8-empirical}
\sloppy
The strengthened conjecture was tested on the same chord-apex+Kempe
colourings as Remark~\ref{rem:conj-3-6-empirical}; for each colouring we
sought any Conjecture-3.6-witness $(F, e_1, e_2)$ whose accompanying
$f_n$ satisfies clause~(4) (see
\texttt{experiments/check\_conj\_3\_8\_scaled.py}):
\begin{center}
\small
\renewcommand{\arraystretch}{1.15}
\begin{tabular}{r|r|r|r|l}
$n$ & \#tri & \#col.\ tested & \#sat.\ & status \\
\hline
$12$ & $1$ & $0$ & --- & vacuous \\
$13$ & $0$ & --- & --- & no min-deg-$5$ tri \\
$14$ & $1$ & $216$ & $216$ & all pass \\
$15$ & $1$ & $0$ & --- & vacuous \\
$16$ & $3$ & $864$ & $864$ & all pass \\
$17$ & $4$ & $4{,}650$ & $4{,}650$ & all pass \\
$18$ & $12$ & $8{,}070$ & $8{,}070$ & all pass \\
\hline
total & $23$ & $13{,}800$ & $13{,}800$ & \\
\end{tabular}
\end{center}
\noindent A subtlety: only about half of the Conjecture-3.6-witnesses
individually satisfy clause (4) on each colouring, but in every case some
witness does. The conjecture is therefore an existential statement at the
witness level, not a property of every witness.
\end{remark}
\medskip
The next definition records a cubic-preserving analogue of edge contraction
which turns out --- under planar duality --- to coincide with simple-graph
contraction on the dual side. It will be useful when reasoning about the
modified graph $\widehat{G}'^{+}$ of Conjecture~\ref{conj:face-monochromatic-pair-strengthened}
and its further reductions.
\begin{definition}[Cubic-graph edge contraction]
\label{def:cubic-edge-contraction}
Let $H$ be a cubic plane graph and $e = uv$ an edge of $H$ with $u \neq v$ and
no edge of $H$ parallel to $e$. The \emph{cubic-graph edge contraction} of $H$
along $e$ is the graph $H'$ obtained in two steps:
\begin{enumerate}
\item \emph{Delete} the edge $e$; the endpoints $u$ and $v$ each drop to
degree $2$.
\item \emph{Smooth} each of $u$ and $v$: at $u$, replace $u$ and its two
remaining incident edges $ua, ub$ by a single new edge $ab$; do the
same at $v$. Both vertices $u$ and $v$ are removed, and two new edges
are added in their place.
\end{enumerate}
Provided the smoothings do not introduce a loop or parallel edge, $H'$ is
again a cubic plane graph, with $|V(H')| = |V(H)| - 2$ and
$|E(H')| = |E(H)| - 3$.
Equivalently, $H'$ is the planar dual of $\mathrm{dual}(H) / e^{*}$, where
$e^{*}$ is the edge of $\mathrm{dual}(H)$ crossing $e$ and the contraction
on the right-hand side is simple-graph contraction (loops removed, parallel
edges absorbed). Under planar duality, contracting $e^{*}$ in
$\mathrm{dual}(H)$ merges the two triangular faces of $\mathrm{dual}(H)$
incident to $e^{*}$, and the parallel-edge cleanup corresponds exactly to
the smoothing step on the primal side.
\end{definition}
\begin{figure}[h]
\centering
\includegraphics[width=0.95\textwidth]{fig_cubic_edge_contraction.png}
\caption{Cubic-graph edge contraction
(Definition~\ref{def:cubic-edge-contraction}). Left: a fragment of a cubic
plane graph with the contracted edge $e = uv$ highlighted in red. Middle:
deleting $e$ leaves $u$ and $v$ of degree~$2$. Right: smoothing $u$ and $v$
replaces each pair of incident edges by a single new edge, removing $u, v$
and giving a cubic plane graph again.}
\label{fig:cubic-edge-contraction}
\end{figure}
\begin{theorem}[Cubic contraction across a 4-face preserves 3-edge-colourability]
\label{thm:cubic-contraction-4face}
Let $H$ be a cubic plane graph with a proper $3$-edge-colouring $\varphi$,
let $f$ be a face of $H$ with $|\partial f| = 4$, and let $e_0, e_1$ be the
two edges of $\partial f$ sharing no endpoint (the opposite pair on the
$4$-cycle $\partial f$). If $\varphi(e_0) \neq \varphi(e_1)$ and the
cubic-graph edge contraction of $H$ along $e_0$
(Definition~\ref{def:cubic-edge-contraction}) is well-defined (no loops or
parallel edges are created), then the contracted graph admits a proper
$3$-edge-colouring.
\end{theorem}
\begin{proof}
Write $\partial f$ as the $4$-cycle $v_0 v_1 v_2 v_3$ with $e_0 = v_0 v_1$
and $e_1 = v_2 v_3$ (so $e_0, e_1$ are opposite); the remaining two
boundary edges of $f$ are $e_2 := v_1 v_2$ and $e_3 := v_3 v_0$. Since $H$
is cubic, each $v_i$ has exactly one edge not on $\partial f$: write $w_i$
for that edge and $u_i$ for its other endpoint, so $w_i = v_i u_i$ with
$u_i \notin \{v_0, v_1, v_2, v_3\}$, for each $i \in \{0, 1, 2, 3\}$. Put
$a := \varphi(e_0)$, $b := \varphi(e_1)$, and let $c$ be the third colour.
\emph{Forced colours on the face.} Propriety at $v_1$ and $v_2$ forces
$\varphi(e_2) \notin \{a, b\}$, so $\varphi(e_2) = c$; then
$\varphi(w_1) = b$ and $\varphi(w_2) = a$. Symmetrically $\varphi(e_3) = c$,
$\varphi(w_0) = b$, and $\varphi(w_3) = a$. In particular
$\varphi(w_0) = \varphi(w_1) = b$.
\emph{Construction of $\varphi'$.} Let $H'$ denote the cubic-graph edge
contraction of $H$ along $e_0$; its new edges are $e_3' := v_3 u_0$
(replacing $e_3$ and $w_0$ via the smoothing at $v_0$) and
$e_2' := v_2 u_1$ (replacing $e_2$ and $w_1$ via the smoothing at $v_1$).
Define $\varphi' \colon E(H') \to \{1, 2, 3\}$ by
\[
\varphi'(e) :=
\begin{cases}
c & \text{if } e = e_1, \\
b & \text{if } e \in \{e_2', e_3'\}, \\
\varphi(e) & \text{otherwise.}
\end{cases}
\]
That is: give each smoothed-in edge the colour $b$ (the colour of the two
$w_i$ it absorbs), recolour $e_1$ to $c$, and leave every other edge of
$H'$ with its $\varphi$-colour.
\emph{Propriety.} Every vertex of $H'$ other than $v_2, v_3, u_0, u_1$ has
the same incident edges and the same $\varphi'$-colours as it did under
$\varphi$, so propriety is inherited there. At the four affected vertices,
\[
\begin{array}{r|lll}
\text{vertex} & \text{edges in } H' & \text{colours under }\varphi' \\
\hline
v_2 & e_1,\; w_2,\; e_2' & c,\; a,\; b \\
v_3 & e_1,\; w_3,\; e_3' & c,\; a,\; b \\
u_0 & e_3',\; \alpha_0,\; \beta_0 & b,\; a,\; c \\
u_1 & e_2',\; \alpha_1,\; \beta_1 & b,\; a,\; c
\end{array}
\]
where $\alpha_i, \beta_i$ are the two edges of $H$ at $u_i$ other than
$w_i$, whose $\varphi$-colours are forced to $\{a, c\}$ by propriety at
$u_i$ (since $\varphi(w_i) = b$). Each row lists three distinct colours, so
$\varphi'$ is proper.
\end{proof}
\begin{figure}[h]
\centering
\includegraphics[width=0.98\textwidth]{fig_thm_cubic_contraction_4face.png}
\caption{The recolouring used in the proof of
Theorem~\ref{thm:cubic-contraction-4face}. Left: the $4$-face $f$ of $H$
under $\varphi$, with the forced colours $\varphi(e_0) = a$,
$\varphi(e_1) = b$, $\varphi(e_2) = \varphi(e_3) = c$,
$\varphi(w_0) = \varphi(w_1) = b$, and $\varphi(w_2) = \varphi(w_3) = a$.
Right: the contracted graph $H'$ under $\varphi'$. The smoothed-in edges
$e_2', e_3'$ inherit the colour $b$ from $w_0, w_1$, and $e_1$ is
recoloured from $b$ to $c$; every edge outside the face neighbourhood
keeps its $\varphi$-colour (dotted in red: the five edges of $H$ removed
by the contraction).}
\label{fig:thm-cubic-contraction-4face}
\end{figure}
\end{document} \end{document}