diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.pdf b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.pdf new file mode 100644 index 0000000..c0d2ce8 Binary files /dev/null and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.pdf differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.png b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.png new file mode 100644 index 0000000..fc803fb Binary files /dev/null and b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/breaker_tile_n12.png differ diff --git a/papers/medial_tire_decompositions_of_plane_triangulations/experiments/plot_breaker_tile.py b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/plot_breaker_tile.py new file mode 100644 index 0000000..4b5c620 --- /dev/null +++ b/papers/medial_tire_decompositions_of_plane_triangulations/experiments/plot_breaker_tile.py @@ -0,0 +1,138 @@ +"""Plot the n=12 size-7 universal breaker tile: word=UUUDUDUDUDUD, bite=(3,11). + +Left panel: structure (7 up teeth = the size-7 outer rim, ringed; the bite (3,11); +the singleton down teeth). Right panel: a Kempe-balanced colouring, with the outer +rim necklace it presents. This tile's outer rim realises 9 of the 10 admissible +size-7 necklaces but never 0001112, which is why it alone empties the size-7 +universal at n=12. +""" + +from __future__ import annotations + +import math +import os + +from full_medial_tire_generator import FullMedialTireGraph +from kempe_valid_colorings import classify_colorings +from kempe_up_tooth_sequences import PALETTE, _positions, canonical_sequence, seq_str +from kempe_transfer_relation_probe import necklace + +HERE = os.path.dirname(os.path.abspath(__file__)) + +WORD = "UUUDUDUDUDUD" +BITE = frozenset({(3, 11)}) +N = 12 +TYPE_COLOR = {"a": "#999999", "u": "#e6550d", "d": "#31a354", "p": "#756bb1"} + + +def vtype(v): + return v[0] + + +def draw_structure(ax, g): + pos = _positions(g) + for u, v in g.edges(): + ax.plot([pos[u][0], pos[v][0]], [pos[u][1], pos[v][1]], + color="#cccccc", lw=0.6, zorder=1) + for k in range(g.n): + a, b = f"a{k}", f"a{(k + 1) % g.n}" + ax.plot([pos[a][0], pos[b][0]], [pos[a][1], pos[b][1]], + color="#555555", lw=1.2, zorder=2) + # highlight the bite spokes + for i, j in g.bites: + apex = f"p{i}_{j}" + for e in (i, j): + for av in (f"a{e}", f"a{(e + 1) % g.n}"): + ax.plot([pos[apex][0], pos[av][0]], [pos[apex][1], pos[av][1]], + color="#756bb1", lw=1.6, zorder=2.5) + for v, (x, y) in pos.items(): + t = vtype(v) + s = 90 if t in ("u", "p") else 60 + ax.scatter([x], [y], s=s, color=TYPE_COLOR[t], edgecolors="black", + linewidths=0.6, zorder=3) + # ring the up-tooth apexes (the size-7 outer rim / boundary) + ux = [pos[f"u{i}"][0] for i in g.up_edges] + uy = [pos[f"u{i}"][1] for i in g.up_edges] + ax.scatter(ux, uy, s=240, facecolors="none", edgecolors="#d62728", + linewidths=1.8, zorder=4) + # label up apexes in boundary order 0..6 + for k, i in enumerate(g.up_edges): + x, y = pos[f"u{i}"] + ax.annotate(str(k), (x, y), textcoords="offset points", xytext=(0, 9), + ha="center", fontsize=7, color="#d62728") + ax.set_xlim(-1.7, 1.7) + ax.set_ylim(-1.9, 1.7) + ax.set_aspect("equal") + ax.axis("off") + ax.set_title("structure: 7 up teeth (red-ringed = size-7 outer rim)\n" + "bite (3,11) in purple; singleton down teeth d5,d7,d9", + fontsize=8) + + +def draw_colored(ax, g, coloring): + pos = _positions(g) + for u, v in g.edges(): + ax.plot([pos[u][0], pos[v][0]], [pos[u][1], pos[v][1]], + color="#cccccc", lw=0.6, zorder=1) + for k in range(g.n): + a, b = f"a{k}", f"a{(k + 1) % g.n}" + ax.plot([pos[a][0], pos[b][0]], [pos[a][1], pos[b][1]], + color="#555555", lw=1.2, zorder=2) + for v, (x, y) in pos.items(): + is_bite = v.startswith("p") + ax.scatter([x], [y], s=80 if is_bite else 55, color=PALETTE[coloring[v]], + edgecolors="black", linewidths=0.5, zorder=3) + ux = [pos[f"u{i}"][0] for i in g.up_edges] + uy = [pos[f"u{i}"][1] for i in g.up_edges] + ax.scatter(ux, uy, s=240, facecolors="none", edgecolors="#222222", + linewidths=1.6, zorder=4) + raw = seq_str(tuple(coloring[f"u{i}"] for i in g.up_edges)) + neck = seq_str(necklace(tuple(coloring[f"u{i}"] for i in g.up_edges))) + ax.set_xlim(-1.7, 1.7) + ax.set_ylim(-1.9, 1.7) + ax.set_aspect("equal") + ax.axis("off") + ax.set_title(f"a Kempe-balanced colouring\nouter rim = {raw} → necklace {neck}", + fontsize=8) + + +def main(): + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + + g = FullMedialTireGraph(n=N, tooth_word=WORD, bites=BITE) + + # confirm: outer rim realises 9/10 admissible necklaces, never 0001112 + realized = set() + sample = None + for c, v in classify_colorings(g, dedup_colors=True): + if not v.valid: + continue + nk = necklace(tuple(c[f"u{i}"] for i in g.up_edges)) + realized.add(nk) + if sample is None: + sample = c + target = necklace(tuple(map(int, "0001112"))) + print(f"breaker {WORD} bite (3,11): outer rim realises {len(realized)} necklaces") + print(f" contains 0001112? {target in realized} (expect False)") + + fig, axes = plt.subplots(1, 2, figsize=(10, 5.4)) + draw_structure(axes[0], g) + draw_colored(axes[1], g, sample) + fig.suptitle( + "Size-7 universal breaker (n=12): word=UUUDUDUDUDUD, bite=(3,11)\n" + "outer rim realises 9 of 10 admissible size-7 necklaces — never 0001112", + fontsize=11, y=0.99, + ) + fig.tight_layout(rect=(0, 0, 1, 0.93)) + png = os.path.join(HERE, "breaker_tile_n12.png") + pdf = os.path.join(HERE, "breaker_tile_n12.pdf") + fig.savefig(png, dpi=200) + fig.savefig(pdf) + print(f"wrote {png}") + print(f"wrote {pdf}") + + +if __name__ == "__main__": + main()