Cut up-tooth apexes (except entry teeth) in the dual-cut experiment

Duplicate the apex medial vertex of every singleton up tooth across all
recognised treads -- except each tread's entry tooth, whose apex is left
intact -- in addition to the closing annular-vertex cuts.

For seed59 (source 5) this removes 19 = n-1 source-dual edges and the
remaining dual is a tree (verified for every source/entry choice). The
tree property holds exactly when n-1 distinct edges are cut; some graphs
(e.g. seed7, cutting 17) fall short and retain cycles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 10:50:56 -04:00
parent 0a3d7b2615
commit 4e92dde36e
@@ -113,9 +113,9 @@ def source_dual(G, faces):
return D
def removed_dual_edges(results, cap_cuts):
"""The set of primal edges whose dual edge a cut removes (cap cut plus every
tread cut)."""
def annular_cut_edges(results, cap_cuts):
"""Primal edges whose dual edge a *closing* cut removes: the cap cut plus
each tread's annular-vertex duplications."""
removed = set()
for c in cap_cuts or []:
removed.add(c["medial_vertex"])
@@ -127,6 +127,27 @@ def removed_dual_edges(results, cap_cuts):
return removed
def up_apex_cut_edges(results):
"""Primal edges whose dual edge the apex duplications remove: the apex
medial vertex of every (singleton) up tooth across all treads, except the
entry tooth of each tread (its apex is not duplicated)."""
removed = set()
for d in sorted(results):
g, bij = results[d]["g"], results[d]["bij"]
entry = results[d]["entry_edge"]
for i in g.up_edges:
if i == entry:
continue
removed.add(bij[f"u{i}"])
return removed
def removed_dual_edges(results, cap_cuts):
"""All primal edges whose dual edge the cut removes: the closing annular
cuts together with the up-tooth apex duplications."""
return annular_cut_edges(results, cap_cuts) | up_apex_cut_edges(results)
def dual_face_missing(G, removed):
"""For each dual face (vertex ``v`` of ``G``), the number of bounding dual
edges removed by the cut."""
@@ -164,7 +185,9 @@ def medial_tire_dual_cut(G, source, entry_edge):
cut_graph, labels, warnings = _assemble_cut_graph(M, results, cap_cuts=cap_cuts)
dual = source_dual(G, faces)
removed = removed_dual_edges(results, cap_cuts)
annular = annular_cut_edges(results, cap_cuts)
apex = up_apex_cut_edges(results)
removed = annular | apex
missing = dual_face_missing(G, removed)
return {
@@ -173,7 +196,9 @@ def medial_tire_dual_cut(G, source, entry_edge):
"levels": levels, "treads": treads, "skipped": skipped,
"results": results, "cap_cuts": cap_cuts, "cut_graph": cut_graph,
"labels": labels, "warnings": warnings,
"dual": dual, "removed_dual_edges": removed, "dual_face_missing": missing,
"dual": dual, "removed_dual_edges": removed,
"annular_cut_edges": annular, "apex_cut_edges": apex,
"dual_face_missing": missing,
"max_missing": max(missing.values()) if missing else 0,
}
@@ -237,7 +262,10 @@ def summary(result):
f"recognised treads: {sorted(result['treads'])} "
f"skipped: {result['skipped']}",
f"removed source-dual edges ({len(removed)}): "
f"{sorted(removed)}",
f"{len(result['annular_cut_edges'])} annular/cap + "
f"{len(result['apex_cut_edges'])} up-tooth apex",
f" annular/cap: {sorted(result['annular_cut_edges'])}",
f" up apexes: {sorted(result['apex_cut_edges'])}",
f"dual-face missing-edge histogram (count by #removed around the dual "
f"face): {dict(sorted(hist.items()))} max={result['max_missing']}",
]
@@ -378,7 +406,7 @@ def _draw_tread(ax, g, depth, cuts, entry_edge, title):
lx, ly = ax_ + 0.5 * (mx - ax_), ay + 0.5 * (my - ay)
ax.text(lx, ly, str(depth[edge]), fontsize=7, fontweight="bold",
ha="center", va="center", zorder=4)
# cut slits
# annular-vertex cut slits (radial)
for c in cuts or []:
if c.vertex is None:
continue
@@ -390,6 +418,17 @@ def _draw_tread(ax, g, depth, cuts, entry_edge, title):
ax.text(vx + 0.34 * math.cos(rad), vy + 0.34 * math.sin(rad),
f"cut {c.order + 1}", fontsize=6, color="#cc2020",
ha="center", va="center", zorder=5)
# up-tooth apex duplications (slit tangential, across the apex marker);
# the entry tooth's apex is not duplicated
for i in g.up_edges:
if i == entry_edge:
continue
vx, vy = pos[f"u{i}"]
rad = math.atan2(vy, vx)
tx, ty = -math.sin(rad), math.cos(rad) # tangential
ax.plot([vx - 0.12 * tx, vx + 0.12 * tx],
[vy - 0.12 * ty, vy + 0.12 * ty],
color="#cc2020", lw=2.0, zorder=6)
# entry marker
if entry_edge is not None:
ex, ey = pos[g.apex_of_edge(entry_edge)]