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:
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user