Skip to content

Commit 0a3562d

Browse files
author
gabe-rbo
committed
new useful complexes methods
1 parent c3f58d3 commit 0a3562d

5 files changed

Lines changed: 94 additions & 4 deletions

File tree

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ authors:
55
given-names: "Gabriel"
66
email: "gabriel.ribeiro@dcc.ufmg.br"
77
title: "pySurgery: Computational Surgery Theory in Python"
8-
version: 2.1.2
8+
version: 2.1.3
99
date-released: 01-05-2026
1010
url: "https://github.com/gabe-rbo/pysurgery"
1111
repository-code: "https://github.com/gabe-rbo/pysurgery"

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "pysurgery"
7-
version = "2.1.2"
7+
version = "2.1.3"
88
description = "A high-performance Python library for Computational Surgery Theory."
99
dependencies = [
1010
"numpy>=2.4.4",
@@ -83,7 +83,7 @@ python_files = ["test_*.py"]
8383
"*" = ["*.jl", "*.yaml", "*.json"]
8484

8585
[tool.bumpversion]
86-
current_version = "2.1.2"
86+
current_version = "2.1.3"
8787
commit = true
8888
tag = true
8989

pysurgery/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@
315315

316316
from . import integrations
317317

318-
__version__ = "2.1.2"
318+
__version__ = "2.1.3"
319319

320320
def __getattr__(name):
321321
if name == "JuliaBridge":

pysurgery/topology/complexes.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3896,6 +3896,68 @@ def link(self, simplex: Iterable[int]) -> "SimplicialComplex":
38963896
# if we include all faces. from_simplices will organize them.
38973897
return SimplicialComplex.from_simplices(link_simplices, coefficient_ring=self.coefficient_ring, close_under_faces=True)
38983898

3899+
def get_subfaces(self, simplex: Iterable[int], dimension: Optional[int] = None) -> Set[Tuple[int, ...]]:
3900+
"""Return all subfaces of a simplex present in the complex, optionally filtered by dimension.
3901+
3902+
A subface of sigma is any simplex tau in the complex that is a subset of sigma.
3903+
3904+
Args:
3905+
simplex: The simplex to find subfaces of.
3906+
dimension: Optional dimension to filter by.
3907+
3908+
Returns:
3909+
A set of tuples representing the subfaces.
3910+
"""
3911+
sigma = _normalize_simplex(simplex)
3912+
subfaces = set()
3913+
3914+
for r in range(1, len(sigma) + 1):
3915+
d = r - 1
3916+
if dimension is not None and d != dimension:
3917+
continue
3918+
if d not in self._simplices_table:
3919+
continue
3920+
for combo in itertools.combinations(sigma, r):
3921+
f = tuple(sorted(combo))
3922+
if f in self._simplices_table[d]:
3923+
subfaces.add(f)
3924+
return subfaces
3925+
3926+
def get_cofaces(self, simplex: Iterable[int], dimension: Optional[int] = None) -> Set[Tuple[int, ...]]:
3927+
"""Return all cofaces of a simplex in the complex, optionally filtered by dimension.
3928+
3929+
A coface of sigma is any simplex tau in the complex that contains sigma.
3930+
3931+
Args:
3932+
simplex: The simplex to find cofaces of.
3933+
dimension: Optional dimension to filter by.
3934+
3935+
Returns:
3936+
A set of tuples representing the cofaces.
3937+
"""
3938+
cofaces = self.star(simplex)
3939+
if dimension is not None:
3940+
cofaces = {c for c in cofaces if len(c) - 1 == dimension}
3941+
return cofaces
3942+
3943+
def to_dynamic_complex(self) -> "DynamicComplex":
3944+
"""Convert this static SimplicialComplex into a DynamicComplex.
3945+
3946+
Preserves the simplices, coefficient ring, filtration, and coordinates.
3947+
3948+
Returns:
3949+
DynamicComplex: The dynamic complex representation.
3950+
"""
3951+
dc = DynamicComplex(
3952+
simplices=self.simplices_field,
3953+
coefficient_ring=self.coefficient_ring,
3954+
filtration=self.filtration,
3955+
)
3956+
if hasattr(self, "_coordinates") and self._coordinates is not None:
3957+
dc._coordinates = self._coordinates.copy()
3958+
dc._generate_point_cloud_mappings(dc._coordinates)
3959+
return dc
3960+
38993961
def simplex_to_index(self, d: int) -> Dict[Tuple[int, ...], int]:
39003962
"""Map each d-simplex to its index in the ordered simplex list.
39013963

tests/test_dynamic_complexes.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,31 @@ def test_dynamic_complex_skeletal_closure_removal():
4040
assert dc.count_simplices(1) == 0
4141
assert dc.count_simplices(0) == 1
4242
assert dc.n_simplices(0) == [(1,)]
43+
44+
45+
def test_new_simplicial_complex_methods():
46+
sc = SimplicialComplex.from_simplices([(0, 1, 2)], close_under_faces=True)
47+
48+
# Test get_subfaces
49+
subfaces_all = sc.get_subfaces((0, 1, 2))
50+
assert (0,) in subfaces_all
51+
assert (0, 1) in subfaces_all
52+
assert (0, 1, 2) in subfaces_all
53+
54+
subfaces_dim1 = sc.get_subfaces((0, 1, 2), dimension=1)
55+
assert subfaces_dim1 == {(0, 1), (1, 2), (0, 2)}
56+
57+
# Test get_cofaces
58+
cofaces_all = sc.get_cofaces((0,))
59+
assert (0,) in cofaces_all
60+
assert (0, 1) in cofaces_all
61+
assert (0, 1, 2) in cofaces_all
62+
63+
cofaces_dim2 = sc.get_cofaces((0,), dimension=2)
64+
assert cofaces_dim2 == {(0, 1, 2)}
65+
66+
# Test to_dynamic_complex
67+
dc = sc.to_dynamic_complex()
68+
assert isinstance(dc, DynamicComplex)
69+
assert dc.betti_number(0) == 1
70+
assert dc.betti_number(1) == 0

0 commit comments

Comments
 (0)