Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions cereeberus/cereeberus/reeb/reebgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,17 +705,30 @@ def slice(self, a, b, type="open", verbose=False):
Returns:
ReebGraph: The subgraph of the Reeb graph with image in (a,b).
"""

if type == "open" and a == b:
# (a, a) is an empty interval for open type
return ReebGraph()
if type == "open":
v_list = [v for v in self.nodes() if self.f[v] > a and self.f[v] < b]
elif type == "closed":
v_list = [v for v in self.nodes() if self.f[v] >= a and self.f[v] <= b]

# Keep the edges where either endpoint (or both) is in (a,b)
e_list = [e for e in self.edges() if e[0] in v_list or e[1] in v_list]
# Include the edges that cover the entire slice.
# Include the edges that cover the entire slice, , including edges whose
# endpoint sits exactly on an excluded open bound. Skip edges already
# selected through an included endpoint so they aren't counted twice.
# Note this assumes that all edges are ordered twoards teh upper function value
e_list.extend(
[e for e in self.edges() if self.f[e[0]] < a and self.f[e[1]] > b]
[
e
for e in self.edges()
if e[0] not in v_list
and e[1] not in v_list
and self.f[e[0]] <= a
and self.f[e[1]] >= b
]
)

# Make a dictionary of counts to deal with multiedges
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "cereeberus"
version = "0.1.21"
version = "0.1.22"
authors = [
{ name="Liz Munch", email="muncheli@msu.edu" },
]
Expand Down
65 changes: 65 additions & 0 deletions tests/test_reeb_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,71 @@ def test_add_edge_collapse_respects_reset_pos_false(self):
R.set_pos_from_f()
self.check_reeb(R)


def _edge(self, multiplicity=1, high_first=False):
# One interval from f=0 to f=2, optionally with parallel copies.
R = ReebGraph()
if high_first:
R.add_node('high', 2, reset_pos=False)
R.add_node('low', 0, reset_pos=False)
else:
R.add_node('low', 0, reset_pos=False)
R.add_node('high', 2, reset_pos=False)
for _ in range(multiplicity):
if high_first:
R.add_edge('high', 'low', reset_pos=False)
else:
R.add_edge('low', 'high', reset_pos=False)
R.set_pos_from_f()
return R

def _shape(self, H):
return (len(H.nodes), len(H.edges), H.number_connected_components())

def test_slice_open_endpoint_on_bound(self):
# An edge whose endpoint sits exactly on an open bound still has its
# interior inside the slice, so it must not be dropped.
for high_first in [False, True]:
R = self._edge(high_first=high_first)
for a, b in [(0, 2), (0, 1), (1, 2)]:
H = R.slice(a, b)
self.assertEqual(self._shape(H), (2, 1, 1), f'open slice ({a}, {b})')
self.assertEqual(sorted(H.f.values()), [a, b])
self.check_reeb(H)

def test_slice_open_parallel_edges_on_bounds(self):
# Removing the shared endpoints leaves the parallel edges disconnected.
R = self._edge(multiplicity=3)
H = R.slice(0, 2)
self.assertEqual(self._shape(H), (6, 3, 3))
self.check_reeb(H)

# Torus example: vertices at 0, 1, 4, 5 with a double edge from 1 to 4.
T = ex_rg.torus()
H = T.slice(1, 4)
self.assertEqual(self._shape(H), (4, 2, 2))
self.check_reeb(H)

def test_slice_open_zero_width_is_empty(self):
# (a, a) is empty, even when edges cross height a.
T = ex_rg.torus()
for a in [0, 1, 2, 4, 5]:
H = T.slice(a, a)
self.assertEqual(len(H.nodes), 0, f'open slice ({a}, {a})')
self.assertEqual(len(H.edges), 0)

def test_slice_closed_unchanged(self):
# Closed slices keep boundary vertices and don't double-count edges.
R = self._edge(multiplicity=3)
self.assertEqual(self._shape(R.slice(0, 2, type='closed')), (2, 3, 1))
self.assertEqual(self._shape(R.slice(0, 1, type='closed')), (4, 3, 1))
self.assertEqual(self._shape(R.slice(1, 1, type='closed')), (3, 0, 3))

T = ex_rg.torus()
self.assertEqual(self._shape(T.slice(1, 4, type='closed')), (2, 2, 1))
self.assertEqual(self._shape(T.slice(2, 2, type='closed')), (2, 0, 2))





Expand Down
Loading