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:
+426
@@ -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()
|
||||
+13
-5
@@ -130,11 +130,12 @@ def matches_chord_apex_kempe(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)
|
||||
and both, together with merged, lie on a Kempe cycle kc. Else None."""
|
||||
"""Returns (F, e1, e2, kc) if some face F of H has two same-colour edges
|
||||
(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'])
|
||||
c_merged = col[merged_idx]
|
||||
# All Kempe cycles through merged (one per color pair (c_merged, c'))
|
||||
kempe_cycles = []
|
||||
for c_prime in range(3):
|
||||
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)))
|
||||
if ei is not None:
|
||||
face_edge_indices.append(ei)
|
||||
for i in range(len(face_edge_indices)):
|
||||
for j in range(i + 1, len(face_edge_indices)):
|
||||
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
|
||||
# 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:
|
||||
if e1 in kc and e2 in kc:
|
||||
return face, e1, e2, kc
|
||||
|
||||
+209
@@ -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()
|
||||
+122
@@ -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()
|
||||
+205
@@ -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 }
|
||||
\newlabel{fig:iterated-reduction-trace}{{3}{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{tocindent0}{0pt}
|
||||
\newlabel{tocindent1}{17.77782pt}
|
||||
\newlabel{tocindent2}{0pt}
|
||||
\newlabel{tocindent3}{0pt}
|
||||
\newlabel{lem:all-distinct-exists}{{3.5}{9}}
|
||||
\newlabel{conj:face-monochromatic-pair-on-merged-kempe-cycle}{{3.6}{9}}
|
||||
\gdef \@abspage@last{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{fig:cubic-edge-contraction}{{4}{12}}
|
||||
\@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
|
||||
restricted \write18 enabled.
|
||||
file:line:error style messages enabled.
|
||||
%&-line parsing enabled.
|
||||
**/Users/didericis/Code/math-research/papers/dual_decomposition_minimal_counterexamples/paper.tex
|
||||
(/Users/didericis/Code/math-research/papers/dual_decomposition_minimal_counterexamples/paper.tex
|
||||
**paper.tex
|
||||
(./paper.tex
|
||||
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
|
||||
\linespacing=\dimen138
|
||||
\normalparindent=\dimen139
|
||||
@@ -18,14 +18,17 @@ Package: amsmath 2021/10/15 v2.17l AMS math features
|
||||
For additional information on amsmath, use the `?' option.
|
||||
(/usr/local/texlive/2022/texmf-dist/tex/latex/amsmath/amstext.sty
|
||||
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
|
||||
\@emptytoks=\toks16
|
||||
\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
|
||||
\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
|
||||
)
|
||||
\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 2939.
|
||||
)
|
||||
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
|
||||
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
|
||||
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
|
||||
\symAMSa=\mathgroup4
|
||||
\symAMSb=\mathgroup5
|
||||
@@ -100,33 +106,42 @@ LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold'
|
||||
\thm@postskip=\skip55
|
||||
\thm@headsep=\skip56
|
||||
\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
|
||||
) (/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)
|
||||
(/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)
|
||||
\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)
|
||||
(/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)
|
||||
) (/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
|
||||
)
|
||||
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
|
||||
))
|
||||
\Gin@req@height=\dimen150
|
||||
\Gin@req@width=\dimen151
|
||||
)
|
||||
\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)
|
||||
\l__color_backend_stack_int=\count273
|
||||
\l__pdf_internal_box=\box53
|
||||
) (./paper.aux)
|
||||
)
|
||||
(./paper.aux)
|
||||
\openout1 = `paper.aux'.
|
||||
|
||||
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: ... 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 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
|
||||
) (/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).]
|
||||
\scratchcounter=\count274
|
||||
\scratchdimen=\dimen152
|
||||
@@ -165,12 +184,18 @@ File: umsb.fd 2013/01/14 v3.01 AMS symbols B
|
||||
\everyMPtoPDFconversion=\toks29
|
||||
) (/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 Info: Redefining graphics rule for `.eps' on input line 485.
|
||||
(/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 Live
|
||||
)) [1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
|
||||
Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4
|
||||
85.
|
||||
|
||||
(/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
|
||||
[]\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>
|
||||
@@ -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'.
|
||||
|
||||
[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>
|
||||
File: fig_chord_apex_step1.png Graphic file (type 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'.
|
||||
|
||||
[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
|
||||
\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>
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
\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
|
||||
\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
|
||||
[]
|
||||
|
||||
[7] [8 <./fig_alg_step0.png> <./fig_alg_step1.png> <./fig_alg_step2.png>] [9] (./paper.aux) )
|
||||
Here is how much of TeX's memory you used:
|
||||
3082 strings out of 478268
|
||||
44173 string characters out of 5846347
|
||||
344279 words of memory out of 5000000
|
||||
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
|
||||
</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>
|
||||
Output written on paper.pdf (9 pages, 977618 bytes).
|
||||
PDF statistics:
|
||||
140 PDF objects out of 1000 (max. 8388607)
|
||||
74 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)
|
||||
[7] [8 <./fig_alg_step0.png> <./fig_alg_step1.png> <./fig_alg_step2.png>]
|
||||
[9] [10]
|
||||
Underfull \hbox (badness 1648) in paragraph at lines 705--710
|
||||
\OT1/cmr/m/it/10 Remark \OT1/cmr/m/n/10 3.9\OT1/cmr/m/it/10 . \OT1/cmr/m/n/10 T
|
||||
he strength-ened con-jec-ture was tested on the same chord-
|
||||
[]
|
||||
|
||||
|
||||
Underfull \hbox (badness 1014) in paragraph at lines 705--710
|
||||
\OT1/cmr/m/n/10 apex+Kempe colour-ings as Re-mark 3.7[]; for each colour-ing we
|
||||
sought any
|
||||
[]
|
||||
|
||||
<fig_cubic_edge_contraction.png, id=75, 950.752pt x 203.159pt>
|
||||
File: fig_cubic_edge_contraction.png Graphic file (type png)
|
||||
<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)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
edge, such that
|
||||
\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
|
||||
$\{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{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}
|
||||
|
||||
Reference in New Issue
Block a user