"""Check whether the dodecahedron's reduced dual admits a proper 3-edge-colouring with: (i) colour(spike) == colour(merged), AND (ii) the {c_spike, c_side0}-Kempe cycle through the spike contains both side-0 and the merged edge, AND (iii) the {c_spike, c_side1}-Kempe cycle through the spike contains both side-1 and the merged edge. Definitions follow Algorithm 3.1 in paper.tex with G = icosahedron, G' = dodecahedron, i_1 = 0; the reduced dual has 16 vertices and 24 edges. Lemmas 2.6 and 2.7 force these properties for ANY proper 3-edge-colouring of the reduced dual of a *minimal counterexample's* dual. The dodecahedron is the dual of the icosahedron --- which IS 4-colourable, so the lemmas do not apply and the question is non-trivial. """ from sage.all import Graph def build_reduced_dodecahedron(i_red=0): edges = [] for k in range(5): edges.append((('b', k), ('c', k))) edges.append((('b', k), ('c', (k - 1) % 5))) edges.append((('c', k), ('d', k))) edges.append((('d', k), ('d', (k + 1) % 5))) v_n = 'v_n' edges.append((v_n, ('b', i_red % 5))) edges.append((v_n, ('b', (i_red + 1) % 5))) edges.append((v_n, ('b', (i_red + 2) % 5))) edges.append((('b', (i_red + 3) % 5), ('b', (i_red + 4) % 5))) return Graph(edges, multiedges=False, loops=False) def enumerate_proper_3_edge_colorings(G): """Return (edge_list, list_of_colorings) where each coloring is a list of 3-edge-colours indexed by edge_list.""" 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(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(edges, coloring, start_edge_idx, color_pair): """Return the set of edge-indices in the Kempe cycle containing edges[start_edge_idx] in the subgraph of edges with colour in color_pair.""" a, b = color_pair in_sub = [i for i in range(len(edges)) if coloring[i] in (a, b)] if start_edge_idx not in in_sub: return None visited = {start_edge_idx} stack = [start_edge_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 main(): G = build_reduced_dodecahedron(i_red=0) print(f"|V| = {G.order()}, |E| = {G.size()}") edges, colorings = enumerate_proper_3_edge_colorings(G) print(f"Proper 3-edge-colorings total: {len(colorings)}") v_n = 'v_n' spike_set = frozenset({v_n, ('b', 1)}) side_0_set = frozenset({v_n, ('b', 0)}) side_1_set = frozenset({v_n, ('b', 2)}) merged_set = frozenset({('b', 3), ('b', 4)}) idx = {} for i, e in enumerate(edges): s = frozenset((e[0], e[1])) if s == spike_set: idx['spike'] = i if s == side_0_set: idx['side_0'] = i if s == side_1_set: idx['side_1'] = i if s == merged_set: idx['merged'] = i n_chord_apex = 0 # spike == merged n_kc0_ok = 0 # condition (ii) n_kc1_ok = 0 # condition (iii) n_all_ok = 0 # all three example = None for col in colorings: c_spike = col[idx['spike']] c_merged = col[idx['merged']] c_s0 = col[idx['side_0']] c_s1 = col[idx['side_1']] if c_spike != c_merged: continue n_chord_apex += 1 kc0 = kempe_cycle_edges(edges, col, idx['spike'], (c_spike, c_s0)) kc1 = kempe_cycle_edges(edges, col, idx['spike'], (c_spike, c_s1)) kc0_ok = kc0 is not None and idx['side_0'] in kc0 and idx['merged'] in kc0 kc1_ok = kc1 is not None and idx['side_1'] in kc1 and idx['merged'] in kc1 n_kc0_ok += int(kc0_ok) n_kc1_ok += int(kc1_ok) if kc0_ok and kc1_ok: n_all_ok += 1 if example is None: example = (col, kc0, kc1) print() print(f"Colorings with spike == merged (chord-apex condition): {n_chord_apex}") print(f" ... + {{c, c_0}}-Kempe cycle through spike/side-0/merged: {n_kc0_ok}") print(f" ... + {{c, c_1}}-Kempe cycle through spike/side-1/merged: {n_kc1_ok}") print(f" ... ALL THREE conditions: {n_all_ok}") if example is not None: col, kc0, kc1 = example c_spike = col[idx['spike']] print() print("Example coloring (one of {} matching):".format(n_all_ok)) for role in ('spike', 'side_0', 'side_1', 'merged'): print(f" {role:7s}: colour {col[idx[role]]}, edge {tuple(edges[idx[role]])}") print(f" spike colour = merged colour = {c_spike}") print(f" Kempe cycle {{c, c_0}} (through spike): {len(kc0)} edges") for i in sorted(kc0): print(f" [c={col[i]}] {edges[i]}") print(f" Kempe cycle {{c, c_1}} (through spike): {len(kc1)} edges") for i in sorted(kc1): print(f" [c={col[i]}] {edges[i]}") else: # Dissect a sample chord-apex coloring that fails the Kempe condition print() print("Sample chord-apex coloring (fails the Kempe-chain condition):") for col in colorings: if col[idx['spike']] != col[idx['merged']]: continue c_spike = col[idx['spike']] c_s0 = col[idx['side_0']] c_s1 = col[idx['side_1']] for role in ('spike', 'side_0', 'side_1', 'merged'): print(f" {role:7s}: colour {col[idx[role]]}, edge {tuple(edges[idx[role]])}") kc0 = kempe_cycle_edges(edges, col, idx['spike'], (c_spike, c_s0)) kc1 = kempe_cycle_edges(edges, col, idx['spike'], (c_spike, c_s1)) kc_m0 = kempe_cycle_edges(edges, col, idx['merged'], (c_spike, c_s0)) kc_m1 = kempe_cycle_edges(edges, col, idx['merged'], (c_spike, c_s1)) print(f" spike's {{c, c_0}}-Kempe cycle has {len(kc0)} edges; " f"side_0 in it = {idx['side_0'] in kc0}, " f"merged in it = {idx['merged'] in kc0}") print(f" merged's {{c, c_0}}-Kempe cycle has {len(kc_m0)} edges; " f"disjoint from spike's? {kc0.isdisjoint(kc_m0)}") print(f" spike's {{c, c_1}}-Kempe cycle has {len(kc1)} edges; " f"side_1 in it = {idx['side_1'] in kc1}, " f"merged in it = {idx['merged'] in kc1}") print(f" merged's {{c, c_1}}-Kempe cycle has {len(kc_m1)} edges; " f"disjoint from spike's? {kc1.isdisjoint(kc_m1)}") break if __name__ == '__main__': main()