Add medial tire cut experiment and chaining section
New experiments/run_medial_tire_cut_experiment.py: generates a random maximal planar graph (stacked seed + random diagonal flips), builds the medial graph, takes the tire decomposition at a random vertex level source, walk-depth labels and cuts each full medial tire graph chained down the tire tree, and assembles one final cut graph of M(G) with a global label map (data only; graphics go in a separate script). Fix label_and_cut: the root face is None, which collided with the next(..., None) sentinel, leaving teeth unlabelled when the entry up tooth lay inside a bite gap; use a distinct sentinel so the ascent to the root face runs. Add a "Chaining across the tire tree" section to the paper, clarifying that the candidate parent down teeth are the boundary (singleton) down teeth only -- bite teeth are interior to the parent and shared with no child, so a lower-walk bite is skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -179,7 +179,9 @@ def label_and_cut(graph: FullMedialTireGraph, entry_edge: int,
|
||||
# Steps 1-3: the entry face.
|
||||
traverse(innermost_bite(entry_edge, graph.bites), entry_edge, is_entry=True)
|
||||
|
||||
# Steps 4-6: descend through bites, deepest first.
|
||||
# Steps 4-6: descend (or ascend) through bites, deepest first. The root
|
||||
# face is ``None``, so we use a distinct sentinel for "no unlabelled face".
|
||||
_MISSING = object()
|
||||
while len(depth) < graph.n:
|
||||
labelled_bite_teeth = sorted(
|
||||
(e for e in depth if door_bite(graph, e) is not None),
|
||||
@@ -188,8 +190,8 @@ def label_and_cut(graph: FullMedialTireGraph, entry_edge: int,
|
||||
for t in labelled_bite_teeth:
|
||||
target = next((F for F in faces_bordered(graph, t)
|
||||
if any(e not in depth for e in face_boundary(graph, F))),
|
||||
None)
|
||||
if target is not None:
|
||||
_MISSING)
|
||||
if target is not _MISSING:
|
||||
traverse(target, t, is_entry=False)
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
"""Medial tire cut experiment.
|
||||
|
||||
End-to-end experiment for the *Medial Tire Cuts* paper:
|
||||
|
||||
1. Generate a random maximal planar graph G on n vertices (stacked seed plus
|
||||
random diagonal flips; ``random_maximal_planar`` from the medial tire
|
||||
decompositions experiments).
|
||||
2. Build its medial graph M(G).
|
||||
3. Take the nested tire decomposition at one random vertex level source: the
|
||||
BFS-level treads, each realized as a FullMedialTireGraph.
|
||||
4. Walk-depth label and cut each full medial tire graph, chaining the labels
|
||||
down the tire tree, and assemble one final cut graph of M(G) with a global
|
||||
label map.
|
||||
|
||||
This script produces *data* (the final cut graph and its labels); it draws
|
||||
nothing. Anything for the paper (figures) lives in a separate script that
|
||||
imports ``run_experiment`` from here.
|
||||
|
||||
Chaining rule (walk depths across the tire tree).
|
||||
* The root tread (no recognised parent) is entered at an arbitrary up tooth
|
||||
with walk depth 0.
|
||||
* A child tread is entered at the up tooth whose apex is the *same medial
|
||||
vertex* as the parent's down tooth of lowest walk depth -- a parent down
|
||||
tooth and the child up tooth glued to it across the shared level cycle are
|
||||
the same medial vertex of M(G). The entry up tooth's walk depth is that
|
||||
parent down tooth's depth + 1, and the walk increments locally from there.
|
||||
|
||||
Run with the repo venv (networkx + scipy): ``.venv/bin/python``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
|
||||
import networkx as nx
|
||||
|
||||
# Reuse the realization pipeline from the medial tire decompositions paper, and
|
||||
# the walk-depth labelling/cut from this paper's companion script.
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
_MTD = os.path.normpath(os.path.join(
|
||||
_HERE, "..", "..",
|
||||
"medial_tire_decompositions_of_plane_triangulations", "experiments"))
|
||||
sys.path.insert(0, _MTD)
|
||||
sys.path.insert(0, _HERE)
|
||||
|
||||
from tire_realization_analysis import ( # noqa: E402
|
||||
extract_tread, medial_graph, medial_tire_facemodel, random_maximal_planar,
|
||||
recognise, triangular_faces,
|
||||
)
|
||||
from medial_tire_cut_labelling import door_bite, label_and_cut # noqa: E402
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 4. Walk-depth labelling and cut, chained down the tire tree.
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def _apex_vertex(g, bij, edge):
|
||||
"""The medial vertex that is the apex of the tooth on annular ``edge``."""
|
||||
return bij[g.apex_of_edge(edge)]
|
||||
|
||||
|
||||
def _label_treads(treads, results):
|
||||
"""Fill ``results[d]`` with the walk-depth labelling and cuts for each
|
||||
recognised tread ``d``, chaining child entries to parent down teeth."""
|
||||
for d in sorted(treads):
|
||||
g, bij = treads[d]
|
||||
parent = treads.get(d - 1)
|
||||
if parent is None:
|
||||
entry_edge, start_depth = g.up_edges[0], 0 # arbitrary root entry
|
||||
else:
|
||||
pg, pbij = parent
|
||||
pdepth = results[d - 1]["depth"]
|
||||
# parent down teeth, lowest walk depth first
|
||||
down = sorted((pdepth[k], _apex_vertex(pg, pbij, k))
|
||||
for k in pg.down_edges)
|
||||
child_up_apex = {bij[f"u{m}"]: m for m in g.up_edges}
|
||||
entry_edge = start_depth = None
|
||||
for value, apex in down:
|
||||
if apex in child_up_apex:
|
||||
entry_edge, start_depth = child_up_apex[apex], value + 1
|
||||
break
|
||||
if entry_edge is None: # no shared apex (degenerate); root-style
|
||||
entry_edge, start_depth = g.up_edges[0], 0
|
||||
depth, cuts = label_and_cut(g, entry_edge, start_depth=start_depth)
|
||||
results[d] = {"g": g, "bij": bij, "entry_edge": entry_edge,
|
||||
"start_depth": start_depth, "depth": depth, "cuts": cuts}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Assemble one final cut graph of M(G) with a global label map.
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def _assemble_cut_graph(M, results):
|
||||
"""Apply every tread's cuts to M(G).
|
||||
|
||||
Each cut duplicates an annular medial vertex, splitting its four incident
|
||||
medial edges along the slit between the two teeth meeting there: the tooth
|
||||
on the previous annular edge (with that edge's far annular vertex) goes to
|
||||
one copy, the tooth on the next annular edge to the other.
|
||||
|
||||
Returns ``(H, label_records, warnings)`` where ``H`` is the cut graph (a
|
||||
networkx graph whose split vertices are keyed ``(medial_vertex, "A"/"B",
|
||||
tread)``) and ``label_records`` lists every tooth's walk depth.
|
||||
"""
|
||||
# Per cut annular vertex: map each original neighbour -> which copy keeps it.
|
||||
split = {} # medial_vertex -> {neighbour_medial_vertex: copy_node}
|
||||
warnings = []
|
||||
for d in sorted(results):
|
||||
g, bij = results[d]["g"], results[d]["bij"]
|
||||
n = g.n
|
||||
for c in results[d]["cuts"]:
|
||||
kk = c.vertex
|
||||
if kk is None:
|
||||
continue
|
||||
mv = bij[f"a{kk}"]
|
||||
if mv in split:
|
||||
warnings.append(f"annular vertex a{kk} of tread {d} cut twice; "
|
||||
f"second cut not applied")
|
||||
continue
|
||||
e_prev, e_next = (kk - 1) % n, kk
|
||||
copy_a = (mv, "A", d)
|
||||
copy_b = (mv, "B", d)
|
||||
split[mv] = {
|
||||
bij[f"a{(kk - 1) % n}"]: copy_a,
|
||||
_apex_vertex(g, bij, e_prev): copy_a,
|
||||
bij[f"a{(kk + 1) % n}"]: copy_b,
|
||||
_apex_vertex(g, bij, e_next): copy_b,
|
||||
}
|
||||
|
||||
def resolve(node, other):
|
||||
return split[node][other] if node in split else node
|
||||
|
||||
H = nx.Graph()
|
||||
H.add_nodes_from(v for v in M.nodes() if v not in split)
|
||||
for v, copies in split.items():
|
||||
H.add_nodes_from(set(copies.values()))
|
||||
for u, v in M.edges():
|
||||
H.add_edge(resolve(u, v), resolve(v, u))
|
||||
|
||||
label_records = []
|
||||
for d in sorted(results):
|
||||
g, bij, depth = results[d]["g"], results[d]["bij"], results[d]["depth"]
|
||||
for k in range(g.n):
|
||||
role = ("up" if g.tooth_word[k] == "U"
|
||||
else "bite" if door_bite(g, k) is not None else "down")
|
||||
label_records.append({
|
||||
"tread": d, "edge": k, "role": role,
|
||||
"apex": _apex_vertex(g, bij, k), "walk": depth[k],
|
||||
})
|
||||
return H, label_records, warnings
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Driver.
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def run_experiment(n: int = 12, seed: int = 0, flips: int = 400) -> dict:
|
||||
"""Run the full pipeline and return a structured result.
|
||||
|
||||
Result keys: ``n, seed, G, M, source, treads`` (dict depth -> (g, bij)),
|
||||
``results`` (dict depth -> labelling/cut record), ``skipped`` (list of
|
||||
(depth, reason)), ``cut_graph`` (networkx graph), ``labels`` (list of tooth
|
||||
records), ``warnings``.
|
||||
"""
|
||||
G = random_maximal_planar(n, seed, flips=flips)
|
||||
faces, _ = triangular_faces(G)
|
||||
M = medial_graph(G)
|
||||
source = random.Random(f"source-{seed}").choice(sorted(G.nodes()))
|
||||
levels = nx.single_source_shortest_path_length(G, source)
|
||||
|
||||
treads, skipped = {}, []
|
||||
for d in range(max(levels.values())):
|
||||
tread = extract_tread(faces, levels, d)
|
||||
if tread is None:
|
||||
skipped.append((d, "no tread faces"))
|
||||
continue
|
||||
if len(tread["up"]) < 3:
|
||||
skipped.append((d, f"only {len(tread['up'])} up teeth"))
|
||||
continue
|
||||
rec = recognise(medial_tire_facemodel(tread["tread_faces"]), tread)
|
||||
if rec is None:
|
||||
skipped.append((d, "not a valid full medial tire graph"))
|
||||
continue
|
||||
treads[d] = rec
|
||||
|
||||
results = {}
|
||||
_label_treads(treads, results)
|
||||
cut_graph, labels, warnings = _assemble_cut_graph(M, results)
|
||||
return {
|
||||
"n": n, "seed": seed, "G": G, "M": M, "source": source,
|
||||
"treads": treads, "results": results, "skipped": skipped,
|
||||
"cut_graph": cut_graph, "labels": labels, "warnings": warnings,
|
||||
}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Serialization / reporting.
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def _vname(v) -> str:
|
||||
"""Stable string name for a medial vertex (an edge key) or a split node."""
|
||||
if isinstance(v, tuple) and len(v) == 3 and v[1] in ("A", "B"):
|
||||
mv, side, d = v
|
||||
return f"{mv[0]}-{mv[1]}#{side}@T{d}"
|
||||
return f"{v[0]}-{v[1]}"
|
||||
|
||||
|
||||
def to_json(result: dict) -> dict:
|
||||
res = result["results"]
|
||||
treads_out = []
|
||||
for d in sorted(res):
|
||||
g, bij = res[d]["g"], res[d]["bij"]
|
||||
depth, cuts = res[d]["depth"], res[d]["cuts"]
|
||||
teeth = [{
|
||||
"edge": k,
|
||||
"role": ("up" if g.tooth_word[k] == "U"
|
||||
else "bite" if door_bite(g, k) is not None else "down"),
|
||||
"apex": _vname(_apex_vertex(g, bij, k)),
|
||||
"walk": depth[k],
|
||||
} for k in range(g.n)]
|
||||
treads_out.append({
|
||||
"depth": d, "n": g.n, "tooth_word": g.tooth_word,
|
||||
"bites": sorted(list(b) for b in g.bites),
|
||||
"entry_edge": res[d]["entry_edge"], "start_depth": res[d]["start_depth"],
|
||||
"teeth": teeth,
|
||||
"cuts": [{
|
||||
"annular_index": c.vertex,
|
||||
"annular_vertex": _vname(bij[f"a{c.vertex}"]),
|
||||
"last_edge": c.last_tooth, "closing_edge": c.closing_tooth,
|
||||
} for c in cuts],
|
||||
})
|
||||
H = result["cut_graph"]
|
||||
return {
|
||||
"n": result["n"], "seed": result["seed"], "source": result["source"],
|
||||
"graph_edges": sorted([int(u), int(v)] for u, v in result["G"].edges()),
|
||||
"medial_vertices": result["M"].number_of_nodes(),
|
||||
"skipped": [[d, why] for d, why in result["skipped"]],
|
||||
"treads": treads_out,
|
||||
"cut_graph": {
|
||||
"nodes": sorted(_vname(v) for v in H.nodes()),
|
||||
"edges": sorted([_vname(u), _vname(v)] for u, v in H.edges()),
|
||||
},
|
||||
"labels": [{
|
||||
"tread": r["tread"], "edge": r["edge"], "role": r["role"],
|
||||
"apex": _vname(r["apex"]), "walk": r["walk"],
|
||||
} for r in result["labels"]],
|
||||
"warnings": result["warnings"],
|
||||
}
|
||||
|
||||
|
||||
def summary(result: dict) -> str:
|
||||
H, res = result["cut_graph"], result["results"]
|
||||
lines = [
|
||||
f"random maximal planar graph: n={result['n']} seed={result['seed']} "
|
||||
f"({result['G'].number_of_edges()} edges)",
|
||||
f"medial graph M(G): {result['M'].number_of_nodes()} vertices",
|
||||
f"level source: vertex {result['source']}",
|
||||
f"recognised treads: {sorted(res)}",
|
||||
f"skipped treads: {result['skipped']}",
|
||||
]
|
||||
for d in sorted(res):
|
||||
g = res[d]["g"]
|
||||
ncuts = len(res[d]["cuts"])
|
||||
lines.append(
|
||||
f" tread {d}: |A(T)|={g.n} word={g.tooth_word} "
|
||||
f"bites={sorted(g.bites)} entry=e{res[d]['entry_edge']} "
|
||||
f"start_depth={res[d]['start_depth']} cuts={ncuts}")
|
||||
lines.append(
|
||||
f"final cut graph: {H.number_of_nodes()} vertices, "
|
||||
f"{H.number_of_edges()} edges, "
|
||||
f"{sum(len(r['cuts']) for r in res.values())} cuts total")
|
||||
if result["warnings"]:
|
||||
lines.append("warnings: " + "; ".join(result["warnings"]))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("-n", type=int, default=12, help="number of vertices")
|
||||
parser.add_argument("--seed", type=int, default=0)
|
||||
parser.add_argument("--flips", type=int, default=400,
|
||||
help="number of random diagonal flips when building G")
|
||||
parser.add_argument("--json", metavar="PATH",
|
||||
help="write the full result as JSON to PATH")
|
||||
args = parser.parse_args()
|
||||
|
||||
result = run_experiment(n=args.n, seed=args.seed, flips=args.flips)
|
||||
print(summary(result))
|
||||
if args.json:
|
||||
with open(args.json, "w") as fh:
|
||||
json.dump(to_json(result), fh, indent=2)
|
||||
print(f"wrote {args.json}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -6,15 +6,19 @@
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{2}{Cutting a full medial tire graph}}{1}{}\protected@file@percent }
|
||||
\newlabel{def:walk-depth-cut}{{2.1}{1}}
|
||||
\citation{bauerfeld-medial-tire}
|
||||
\citation{bauerfeld-medial-tire}
|
||||
\newlabel{rem:closing-tooth}{{2.2}{2}}
|
||||
\newlabel{ex:worked-cut}{{2.3}{2}}
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{3}{Chaining across the tire tree}}{2}{}\protected@file@percent }
|
||||
\citation{bauerfeld-medial-tire}
|
||||
\bibcite{bauerfeld-medial-tire}{1}
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces A full medial tire graph (left) and its walk-depth labelling and cut (right), from Example\nonbreakingspace 2.3\hbox {}. Black vertices are the annular medial vertices of the cycle $A(T)$; blue vertices are up-tooth apexes, red vertices are down-tooth apexes, and the larger red vertex is the shared apex of the bite on annular edges $0$ and $4$. On the right, each tooth carries its walk depth, and the two red slits mark the cuts: \emph {cut\nonbreakingspace 1} duplicates $a_5$ as the root-face traversal closes, and \emph {cut\nonbreakingspace 2} duplicates $a_1$ as the bite's inner-gap face closes. After the cuts the only bounded faces are the eight teeth.}}{3}{}\protected@file@percent }
|
||||
\newlabel{fig:worked-cut}{{1}{3}}
|
||||
\newlabel{rem:chaining-candidates}{{3.1}{3}}
|
||||
\newlabel{tocindent-1}{0pt}
|
||||
\newlabel{tocindent0}{12.7778pt}
|
||||
\newlabel{tocindent1}{17.77782pt}
|
||||
\newlabel{tocindent2}{0pt}
|
||||
\newlabel{tocindent3}{0pt}
|
||||
\newlabel{rem:closing-tooth}{{2.2}{2}}
|
||||
\newlabel{ex:worked-cut}{{2.3}{2}}
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{2}{}\protected@file@percent }
|
||||
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces A full medial tire graph (left) and its walk-depth labelling and cut (right), from Example\nonbreakingspace 2.3\hbox {}. Black vertices are the annular medial vertices of the cycle $A(T)$; blue vertices are up-tooth apexes, red vertices are down-tooth apexes, and the larger red vertex is the shared apex of the bite on annular edges $0$ and $4$. On the right, each tooth carries its walk depth, and the two red slits mark the cuts: \emph {cut\nonbreakingspace 1} duplicates $a_5$ as the root-face traversal closes, and \emph {cut\nonbreakingspace 2} duplicates $a_1$ as the bite's inner-gap face closes. After the cuts the only bounded faces are the eight teeth.}}{3}{}\protected@file@percent }
|
||||
\newlabel{fig:worked-cut}{{1}{3}}
|
||||
\gdef \@abspage@last{3}
|
||||
\@writefile{toc}{\contentsline {section}{\tocsection {}{}{References}}{4}{}\protected@file@percent }
|
||||
\gdef \@abspage@last{4}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 14 JUN 2026 21:56
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.10.5) 14 JUN 2026 23:46
|
||||
entering extended mode
|
||||
restricted \write18 enabled.
|
||||
%&-line parsing enabled.
|
||||
@@ -498,33 +498,33 @@ e
|
||||
|
||||
LaTeX Warning: `h' float specifier changed to `ht'.
|
||||
|
||||
[2] [3] (./paper.aux) )
|
||||
[2] [3] [4] (./paper.aux) )
|
||||
Here is how much of TeX's memory you used:
|
||||
13647 strings out of 478268
|
||||
272595 string characters out of 5846347
|
||||
554433 words of memory out of 5000000
|
||||
31476 multiletter control sequences out of 15000+600000
|
||||
13648 strings out of 478268
|
||||
272620 string characters out of 5846347
|
||||
558283 words of memory out of 5000000
|
||||
31477 multiletter control sequences out of 15000+600000
|
||||
477049 words of font info for 58 fonts, out of 8000000 for 9000
|
||||
1302 hyphenation exceptions out of 8191
|
||||
84i,9n,89p,936b,704s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/local/texlive/2022/texmf-dist/fonts/type1/public/a
|
||||
msfonts/cm/cmbx10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/am
|
||||
sfonts/cm/cmbx7.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/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfo
|
||||
nts/cm/cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfont
|
||||
s/cm/cmr6.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/c
|
||||
m/cmr7.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/c
|
||||
mr8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmss
|
||||
10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy1
|
||||
0.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.p
|
||||
fb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pf
|
||||
b>
|
||||
Output written on paper.pdf (3 pages, 172798 bytes).
|
||||
84i,9n,89p,801b,704s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/local/texlive/2022/texmf-dist/fonts/type1/publ
|
||||
ic/amsfonts/cm/cmbx10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/publi
|
||||
c/amsfonts/cm/cmbx7.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/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/a
|
||||
msfonts/cm/cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/ams
|
||||
fonts/cm/cmr6.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfon
|
||||
ts/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/
|
||||
cmss10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/c
|
||||
msy10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cm
|
||||
ti10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmt
|
||||
i8.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt1
|
||||
0.pfb>
|
||||
Output written on paper.pdf (4 pages, 176366 bytes).
|
||||
PDF statistics:
|
||||
82 PDF objects out of 1000 (max. 8388607)
|
||||
50 compressed objects within 1 object stream
|
||||
85 PDF objects out of 1000 (max. 8388607)
|
||||
52 compressed objects within 1 object stream
|
||||
0 named destinations out of 1000 (max. 500000)
|
||||
13 words of extra memory for PDF output out of 10000 (max. 10000000)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -261,6 +261,54 @@ the cuts the only bounded faces are the eight teeth.}
|
||||
\label{fig:worked-cut}
|
||||
\end{figure}
|
||||
|
||||
\section{Chaining across the tire tree}
|
||||
|
||||
Definition~\ref{def:walk-depth-cut} labels and cuts a single full medial
|
||||
tire graph. We extend it to the whole medial graph $M(G)$ through the
|
||||
medial tire decomposition of~\cite{bauerfeld-medial-tire}: the tire tree
|
||||
decomposes $M(G)$ into full medial tire graphs $\mathsf{M}(T)$, one per
|
||||
tread $T$, glued along their boundary medial vertices. A parent tread's
|
||||
inner level cycle is a child tread's outer level cycle, and the boundary
|
||||
medial vertices on that shared cycle belong to both treads.
|
||||
|
||||
The key incidence is this. A \emph{boundary} (singleton) down tooth of a
|
||||
parent tread and the up tooth of the child tread glued to it across the
|
||||
shared level cycle have the \emph{same apex}: both apexes are the same
|
||||
medial vertex of $M(G)$, namely the medial vertex of an edge with both
|
||||
endpoints on the shared level cycle. We use this to carry the walk depth
|
||||
from a parent into its children.
|
||||
|
||||
We label tread by tread, outward from the root:
|
||||
\begin{itemize}
|
||||
\item a tread with no parent in the decomposition---in particular the
|
||||
innermost recognised tread---is treated as a \emph{root} and entered at
|
||||
an arbitrary up tooth with walk depth $0$;
|
||||
\item a child tread is entered at the up tooth whose apex is the parent's
|
||||
boundary down tooth of lowest walk depth; that entry up tooth's walk depth
|
||||
is one more than that down tooth's, and the walk then increments locally
|
||||
within the child as in Definition~\ref{def:walk-depth-cut}.
|
||||
\end{itemize}
|
||||
|
||||
\begin{remark}[Candidate down teeth for chaining]
|
||||
\label{rem:chaining-candidates}
|
||||
The down teeth eligible to fix a child's entry are exactly the
|
||||
\emph{boundary} (singleton) down teeth of the parent: those lying in a
|
||||
single tread face, whose apex is the shared boundary medial vertex glued to
|
||||
a child up tooth. A bite's two down teeth are \emph{not} eligible. By the
|
||||
definition of a bite in~\cite{bauerfeld-medial-tire} its annular edge borders
|
||||
two tread faces, so a bite tooth is interior to the parent tread and its
|
||||
apex is a boundary medial vertex of no child. Hence ``the down tooth of
|
||||
lowest walk depth'' is read among the boundary down teeth only; a bite of
|
||||
even lower walk depth is skipped.
|
||||
\end{remark}
|
||||
|
||||
Applying every tread's cuts to $M(G)$ assembles the per-tread labellings
|
||||
and cuts into a single cut graph of $M(G)$ together with a global
|
||||
walk-depth label map. This pipeline---random maximal planar graph, medial
|
||||
graph, tire decomposition at a vertex level source, and chained walk-depth
|
||||
labelling and cut---is carried out by the experiment script
|
||||
\texttt{experiments/run\_medial\_tire\_cut\_experiment.py}.
|
||||
|
||||
\begin{thebibliography}{9}
|
||||
|
||||
\bibitem{bauerfeld-medial-tire}
|
||||
|
||||
Reference in New Issue
Block a user