pysimplicial 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysimplicial-0.2.0/PKG-INFO +297 -0
- pysimplicial-0.2.0/README.md +271 -0
- pysimplicial-0.2.0/pyproject.toml +35 -0
- pysimplicial-0.2.0/setup.cfg +4 -0
- pysimplicial-0.2.0/src/pysimplicial/__init__.py +27 -0
- pysimplicial-0.2.0/src/pysimplicial/_errors.py +13 -0
- pysimplicial-0.2.0/src/pysimplicial/_solvers/__init__.py +0 -0
- pysimplicial-0.2.0/src/pysimplicial/_solvers/cuda_restarts.py +507 -0
- pysimplicial-0.2.0/src/pysimplicial/_solvers/exact.py +1375 -0
- pysimplicial-0.2.0/src/pysimplicial/_solvers/set_cover.py +136 -0
- pysimplicial-0.2.0/src/pysimplicial/category.py +290 -0
- pysimplicial-0.2.0/src/pysimplicial/collapse.py +52 -0
- pysimplicial-0.2.0/src/pysimplicial/complex.py +80 -0
- pysimplicial-0.2.0/src/pysimplicial/contiguity.py +97 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/__init__.py +19 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/graphs.py +48 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/random_complexes.py +29 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/rips.py +185 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/triangulations.json +1 -0
- pysimplicial-0.2.0/src/pysimplicial/datasets/triangulations.py +44 -0
- pysimplicial-0.2.0/src/pysimplicial/maps.py +81 -0
- pysimplicial-0.2.0/src/pysimplicial/operations.py +105 -0
- pysimplicial-0.2.0/src/pysimplicial/visualization/__init__.py +36 -0
- pysimplicial-0.2.0/src/pysimplicial/visualization/_layout.py +824 -0
- pysimplicial-0.2.0/src/pysimplicial/visualization/_points.py +46 -0
- pysimplicial-0.2.0/src/pysimplicial/visualization/_surface.py +1459 -0
- pysimplicial-0.2.0/src/pysimplicial/visualization/_two_d.py +603 -0
- pysimplicial-0.2.0/src/pysimplicial.egg-info/PKG-INFO +297 -0
- pysimplicial-0.2.0/src/pysimplicial.egg-info/SOURCES.txt +33 -0
- pysimplicial-0.2.0/src/pysimplicial.egg-info/dependency_links.txt +1 -0
- pysimplicial-0.2.0/src/pysimplicial.egg-info/requires.txt +17 -0
- pysimplicial-0.2.0/src/pysimplicial.egg-info/top_level.txt +1 -0
- pysimplicial-0.2.0/tests/test_datasets_and_visualization.py +56 -0
- pysimplicial-0.2.0/tests/test_gscat.py +119 -0
- pysimplicial-0.2.0/tests/test_topology.py +120 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysimplicial
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Finite simplicial complexes, contiguity and simplicial LS category
|
|
5
|
+
Project-URL: Homepage, https://github.com/zgenng/PySimplicial
|
|
6
|
+
Project-URL: Repository, https://github.com/zgenng/PySimplicial
|
|
7
|
+
Project-URL: Issues, https://github.com/zgenng/PySimplicial/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: numpy>=1.24
|
|
15
|
+
Provides-Extra: plot
|
|
16
|
+
Requires-Dist: matplotlib>=3.6; extra == "plot"
|
|
17
|
+
Provides-Extra: plot3d
|
|
18
|
+
Requires-Dist: plotly>=5; extra == "plot3d"
|
|
19
|
+
Provides-Extra: viz
|
|
20
|
+
Requires-Dist: matplotlib>=3.6; extra == "viz"
|
|
21
|
+
Requires-Dist: plotly>=5; extra == "viz"
|
|
22
|
+
Provides-Extra: cuda12
|
|
23
|
+
Requires-Dist: numba-cuda[cu12]; extra == "cuda12"
|
|
24
|
+
Provides-Extra: cuda13
|
|
25
|
+
Requires-Dist: numba-cuda[cu13]; extra == "cuda13"
|
|
26
|
+
|
|
27
|
+
# PySimplicial
|
|
28
|
+
|
|
29
|
+
**A Python library for computational simplicial topology, simplicial maps, contiguity, and simplicial Lusternik–Schnirelmann category.**
|
|
30
|
+
|
|
31
|
+
PySimplicial provides tools for constructing and analyzing finite abstract simplicial complexes, studying strong homotopy properties, and computing simplicial topological invariants. It includes exact algorithms for simplicial category and contiguity distance, along with CPU and optional CUDA-assisted methods for the simplicial geometric category.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **Simplicial complexes:** Immutable finite simplicial complexes, faces, facets, dimensions, and skeletons.
|
|
36
|
+
- **Operations:** Joins, wedges, disjoint unions, links, categorical products, and connected components.
|
|
37
|
+
- **Strong homotopy:** Dominated vertices, strong collapsibility, and strong cores.
|
|
38
|
+
- **Simplicial maps:** Construction, restriction, composition, and validation.
|
|
39
|
+
- **Contiguity:** Direct contiguity, contiguity classes, chains, and distance.
|
|
40
|
+
- **Simplicial LS category:** Computation of `scat` and `gscat`.
|
|
41
|
+
- **Exact computation:** Categorical covers, certificates, and rigorous bounds.
|
|
42
|
+
- **Optional CUDA acceleration:** GPU-assisted geometric category computations.
|
|
43
|
+
- **Datasets and visualization:** Graph families, triangulated manifolds, random and Vietoris–Rips complexes, 2D and interactive 3D plots.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
Requires **Python 3.10+** and **NumPy 1.24+**.
|
|
48
|
+
|
|
49
|
+
Install directly from GitHub:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pip install git+https://github.com/zgenng/PySimplicial.git
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or clone the repository and install in editable mode:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/zgenng/PySimplicial.git
|
|
59
|
+
cd PySimplicial
|
|
60
|
+
python -m pip install -e .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Optional visualization and CUDA dependencies (run from the project root):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python -m pip install -e ".[viz]" # Matplotlib + Plotly
|
|
67
|
+
python -m pip install -e ".[plot]" # Matplotlib only
|
|
68
|
+
python -m pip install -e ".[plot3d]" # Plotly only
|
|
69
|
+
python -m pip install -e ".[cuda12]" # CUDA 12
|
|
70
|
+
python -m pip install -e ".[cuda13]" # CUDA 13
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
CUDA requires a compatible NVIDIA GPU and driver. Once the package is published to PyPI, the intended command is:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install pysimplicial
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
### Finite simplicial complexes
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from pysimplicial import SimplicialComplex
|
|
85
|
+
|
|
86
|
+
# Boundary of a triangle, a simplicial model of S¹
|
|
87
|
+
K = SimplicialComplex([(0, 1), (1, 2), (2, 0)])
|
|
88
|
+
|
|
89
|
+
print(K.dimension()) # 1
|
|
90
|
+
print(K.vertices)
|
|
91
|
+
print(K.facets)
|
|
92
|
+
print(K.faces())
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`SimplicialComplex` is immutable. Its facets are the inclusion-maximal simplices; nonempty faces are generated on demand. Use `K.faces(d)`, `K.skeleton(d)`, or `K.induced_subcomplex(vertices)` for common operations.
|
|
96
|
+
|
|
97
|
+
### Strong collapsibility
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from pysimplicial import is_strongly_collapsible, strong_core
|
|
101
|
+
from pysimplicial.datasets import path_graph
|
|
102
|
+
|
|
103
|
+
K = path_graph(5)
|
|
104
|
+
print(is_strongly_collapsible(K)) # True
|
|
105
|
+
result = strong_core(K)
|
|
106
|
+
print(result.core)
|
|
107
|
+
print(result.steps)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
A finite complex is *strongly collapsible* when dominated vertices can be removed iteratively until one vertex remains.
|
|
111
|
+
|
|
112
|
+
### Simplicial maps and contiguity
|
|
113
|
+
|
|
114
|
+
Two simplicial maps `f, g: K → L` are contiguous if `f(σ) ∪ g(σ)` is a simplex of `L` for every simplex `σ` of `K`.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pysimplicial import (
|
|
118
|
+
SimplicialComplex, SimplicialMap,
|
|
119
|
+
is_contiguous, same_contiguity_class, contiguity_chain,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
K = SimplicialComplex([(0, 1)])
|
|
123
|
+
f = SimplicialMap.identity(K)
|
|
124
|
+
g = SimplicialMap.constant(K, K, vertex=0)
|
|
125
|
+
|
|
126
|
+
print(is_contiguous(f, g))
|
|
127
|
+
print(same_contiguity_class(f, g))
|
|
128
|
+
print(contiguity_chain(f, g))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Simplicial Lusternik–Schnirelmann category
|
|
132
|
+
|
|
133
|
+
PySimplicial computes two related invariants:
|
|
134
|
+
|
|
135
|
+
- `scat(K)`: reduced simplicial LS category, using covers by categorical subcomplexes.
|
|
136
|
+
- `gscat(K)`: reduced simplicial geometric category, using covers by strongly collapsible subcomplexes.
|
|
137
|
+
|
|
138
|
+
A cover by `r` subcomplexes yields the **reduced value `r - 1`**.
|
|
139
|
+
|
|
140
|
+
### Simplicial category
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pysimplicial import scat
|
|
144
|
+
from pysimplicial.datasets import cycle_graph
|
|
145
|
+
|
|
146
|
+
K = cycle_graph(3)
|
|
147
|
+
result = scat(K)
|
|
148
|
+
print(result.value) # 1
|
|
149
|
+
print(result.cover)
|
|
150
|
+
print(result.chains)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Geometric category
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from pysimplicial import gscat, verify_gscat_cover
|
|
157
|
+
from pysimplicial.datasets import projective_plane
|
|
158
|
+
|
|
159
|
+
K = projective_plane()
|
|
160
|
+
result = gscat(K)
|
|
161
|
+
|
|
162
|
+
print(result.value) # 2
|
|
163
|
+
print(result.exact)
|
|
164
|
+
print(result.lower_bound, result.upper_bound)
|
|
165
|
+
assert verify_gscat_cover(K, result.cover)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Mathematically, in the reduced convention,
|
|
169
|
+
|
|
170
|
+
\[
|
|
171
|
+
\operatorname{gscat}(K) = \min\{r \geq 0 : K = K_0 \cup \cdots \cup K_r,\; K_i \text{ strongly collapsible}\}.
|
|
172
|
+
\]
|
|
173
|
+
|
|
174
|
+
When the result's lower and upper bounds agree, the answer is exact. Otherwise, `value` is `None` and the verified cover supplies an upper bound. Search limits can be configured with `max_candidate_states` and `max_search_states`.
|
|
175
|
+
|
|
176
|
+
## Optional CUDA acceleration
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from pysimplicial import cuda_status, gscat
|
|
180
|
+
from pysimplicial.datasets import projective_plane
|
|
181
|
+
|
|
182
|
+
print(cuda_status())
|
|
183
|
+
K = projective_plane()
|
|
184
|
+
result = gscat(K, backend="cuda", restarts=1000, seed=42)
|
|
185
|
+
print(result.value, result.gpu_used, result.exact)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Set `backend="auto"` to use CUDA when available, or `backend="cpu"` for CPU-only computation. Set `restarts=0` to skip randomized restarts and proceed to the exact search. CPU verification is used to check returned covers. The randomized CUDA restart kernel supports up to 64 vertices and 512 facets; larger inputs use the exact-search path.
|
|
189
|
+
|
|
190
|
+
## Datasets
|
|
191
|
+
|
|
192
|
+
### Standard graph families
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from pysimplicial.datasets import (
|
|
196
|
+
complete_graph, complete_bipartite_graph, cycle_graph, path_graph,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
K1 = complete_graph(5)
|
|
200
|
+
K2 = complete_bipartite_graph(3, 4)
|
|
201
|
+
K3 = cycle_graph(6)
|
|
202
|
+
K4 = path_graph(5)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Named triangulations
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from pysimplicial.datasets import (
|
|
209
|
+
torus, projective_plane, tetrahedron_boundary,
|
|
210
|
+
triangulation, triangulation_names,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
T = torus()
|
|
214
|
+
RP2 = projective_plane()
|
|
215
|
+
S2 = tetrahedron_boundary()
|
|
216
|
+
CP2 = triangulation("CP2")
|
|
217
|
+
print(CP2.dimension()) # 4
|
|
218
|
+
print(triangulation_names())
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Random and Vietoris–Rips complexes
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from pysimplicial.datasets import random_d_complex, generate_rips_data
|
|
225
|
+
|
|
226
|
+
K = random_d_complex(n=10, dim=2, seed=42)
|
|
227
|
+
points, R, epsilon = generate_rips_data(n_points=20, shape="circle", seed=42)
|
|
228
|
+
print(R.dimension(), epsilon)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Visualization
|
|
232
|
+
|
|
233
|
+
Install `.[viz]` first.
|
|
234
|
+
|
|
235
|
+
### 2D
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from pysimplicial import gscat
|
|
239
|
+
from pysimplicial.datasets import complete_graph
|
|
240
|
+
from pysimplicial.visualization import draw_complex_and_cover
|
|
241
|
+
|
|
242
|
+
K = complete_graph(5)
|
|
243
|
+
result = gscat(K)
|
|
244
|
+
draw_complex_and_cover(K, result, legend=True)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Interactive 3D
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from pysimplicial import gscat
|
|
251
|
+
from pysimplicial.datasets import projective_plane
|
|
252
|
+
from pysimplicial.visualization import draw_surface_3d
|
|
253
|
+
|
|
254
|
+
K = projective_plane()
|
|
255
|
+
result = gscat(K)
|
|
256
|
+
draw_surface_3d(K, cover=result.cover, show=False, save_html="rp2-cover.html")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Tests and development
|
|
260
|
+
|
|
261
|
+
After installing the package:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
python -m unittest discover -s tests -v
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Build and validate distributions:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
python -m pip install build twine
|
|
271
|
+
python -m build
|
|
272
|
+
python -m twine check dist/*
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The test suite covers topological operations, contiguity, geometric category, datasets, and visualization. CUDA-specific tests require compatible hardware.
|
|
276
|
+
|
|
277
|
+
## Mathematical background and references
|
|
278
|
+
|
|
279
|
+
This project is motivated by computational problems in finite simplicial complexes, strong homotopy theory, contiguity, and simplicial Lusternik–Schnirelmann category.
|
|
280
|
+
|
|
281
|
+
- J. A. Barmak and E. G. Minian, *Strong homotopy types, nerves and collapses*, Discrete & Computational Geometry (2012).
|
|
282
|
+
- D. Fernández-Ternero, E. Macías-Virgós, E. Minuz, and J. A. Vilches, *Simplicial Lusternik–Schnirelmann category*, Publicacions Matemàtiques (2019).
|
|
283
|
+
- [Frank Lutz's triangulated surface catalog](https://www3.math.tu-berlin.de/IfM/Nachrufe/Frank_Lutz/stellar/surfaces.html).
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
|
|
287
|
+
Bug reports, suggestions, and contributions are welcome. Please open an [issue](https://github.com/zgenng/PySimplicial/issues) to discuss problems or improvements.
|
|
288
|
+
|
|
289
|
+
## Project status
|
|
290
|
+
|
|
291
|
+
PySimplicial is an evolving research-oriented project. Public APIs and algorithms may change as development continues.
|
|
292
|
+
|
|
293
|
+
**Current source version:** `0.2.0`
|
|
294
|
+
|
|
295
|
+
## Author
|
|
296
|
+
|
|
297
|
+
**Islam Yeginbay** — [@zgenng](https://github.com/zgenng/PySimplicial)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# PySimplicial
|
|
2
|
+
|
|
3
|
+
**A Python library for computational simplicial topology, simplicial maps, contiguity, and simplicial Lusternik–Schnirelmann category.**
|
|
4
|
+
|
|
5
|
+
PySimplicial provides tools for constructing and analyzing finite abstract simplicial complexes, studying strong homotopy properties, and computing simplicial topological invariants. It includes exact algorithms for simplicial category and contiguity distance, along with CPU and optional CUDA-assisted methods for the simplicial geometric category.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Simplicial complexes:** Immutable finite simplicial complexes, faces, facets, dimensions, and skeletons.
|
|
10
|
+
- **Operations:** Joins, wedges, disjoint unions, links, categorical products, and connected components.
|
|
11
|
+
- **Strong homotopy:** Dominated vertices, strong collapsibility, and strong cores.
|
|
12
|
+
- **Simplicial maps:** Construction, restriction, composition, and validation.
|
|
13
|
+
- **Contiguity:** Direct contiguity, contiguity classes, chains, and distance.
|
|
14
|
+
- **Simplicial LS category:** Computation of `scat` and `gscat`.
|
|
15
|
+
- **Exact computation:** Categorical covers, certificates, and rigorous bounds.
|
|
16
|
+
- **Optional CUDA acceleration:** GPU-assisted geometric category computations.
|
|
17
|
+
- **Datasets and visualization:** Graph families, triangulated manifolds, random and Vietoris–Rips complexes, 2D and interactive 3D plots.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Requires **Python 3.10+** and **NumPy 1.24+**.
|
|
22
|
+
|
|
23
|
+
Install directly from GitHub:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python -m pip install git+https://github.com/zgenng/PySimplicial.git
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or clone the repository and install in editable mode:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/zgenng/PySimplicial.git
|
|
33
|
+
cd PySimplicial
|
|
34
|
+
python -m pip install -e .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Optional visualization and CUDA dependencies (run from the project root):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python -m pip install -e ".[viz]" # Matplotlib + Plotly
|
|
41
|
+
python -m pip install -e ".[plot]" # Matplotlib only
|
|
42
|
+
python -m pip install -e ".[plot3d]" # Plotly only
|
|
43
|
+
python -m pip install -e ".[cuda12]" # CUDA 12
|
|
44
|
+
python -m pip install -e ".[cuda13]" # CUDA 13
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
CUDA requires a compatible NVIDIA GPU and driver. Once the package is published to PyPI, the intended command is:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install pysimplicial
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
### Finite simplicial complexes
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from pysimplicial import SimplicialComplex
|
|
59
|
+
|
|
60
|
+
# Boundary of a triangle, a simplicial model of S¹
|
|
61
|
+
K = SimplicialComplex([(0, 1), (1, 2), (2, 0)])
|
|
62
|
+
|
|
63
|
+
print(K.dimension()) # 1
|
|
64
|
+
print(K.vertices)
|
|
65
|
+
print(K.facets)
|
|
66
|
+
print(K.faces())
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`SimplicialComplex` is immutable. Its facets are the inclusion-maximal simplices; nonempty faces are generated on demand. Use `K.faces(d)`, `K.skeleton(d)`, or `K.induced_subcomplex(vertices)` for common operations.
|
|
70
|
+
|
|
71
|
+
### Strong collapsibility
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pysimplicial import is_strongly_collapsible, strong_core
|
|
75
|
+
from pysimplicial.datasets import path_graph
|
|
76
|
+
|
|
77
|
+
K = path_graph(5)
|
|
78
|
+
print(is_strongly_collapsible(K)) # True
|
|
79
|
+
result = strong_core(K)
|
|
80
|
+
print(result.core)
|
|
81
|
+
print(result.steps)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A finite complex is *strongly collapsible* when dominated vertices can be removed iteratively until one vertex remains.
|
|
85
|
+
|
|
86
|
+
### Simplicial maps and contiguity
|
|
87
|
+
|
|
88
|
+
Two simplicial maps `f, g: K → L` are contiguous if `f(σ) ∪ g(σ)` is a simplex of `L` for every simplex `σ` of `K`.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from pysimplicial import (
|
|
92
|
+
SimplicialComplex, SimplicialMap,
|
|
93
|
+
is_contiguous, same_contiguity_class, contiguity_chain,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
K = SimplicialComplex([(0, 1)])
|
|
97
|
+
f = SimplicialMap.identity(K)
|
|
98
|
+
g = SimplicialMap.constant(K, K, vertex=0)
|
|
99
|
+
|
|
100
|
+
print(is_contiguous(f, g))
|
|
101
|
+
print(same_contiguity_class(f, g))
|
|
102
|
+
print(contiguity_chain(f, g))
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Simplicial Lusternik–Schnirelmann category
|
|
106
|
+
|
|
107
|
+
PySimplicial computes two related invariants:
|
|
108
|
+
|
|
109
|
+
- `scat(K)`: reduced simplicial LS category, using covers by categorical subcomplexes.
|
|
110
|
+
- `gscat(K)`: reduced simplicial geometric category, using covers by strongly collapsible subcomplexes.
|
|
111
|
+
|
|
112
|
+
A cover by `r` subcomplexes yields the **reduced value `r - 1`**.
|
|
113
|
+
|
|
114
|
+
### Simplicial category
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from pysimplicial import scat
|
|
118
|
+
from pysimplicial.datasets import cycle_graph
|
|
119
|
+
|
|
120
|
+
K = cycle_graph(3)
|
|
121
|
+
result = scat(K)
|
|
122
|
+
print(result.value) # 1
|
|
123
|
+
print(result.cover)
|
|
124
|
+
print(result.chains)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Geometric category
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pysimplicial import gscat, verify_gscat_cover
|
|
131
|
+
from pysimplicial.datasets import projective_plane
|
|
132
|
+
|
|
133
|
+
K = projective_plane()
|
|
134
|
+
result = gscat(K)
|
|
135
|
+
|
|
136
|
+
print(result.value) # 2
|
|
137
|
+
print(result.exact)
|
|
138
|
+
print(result.lower_bound, result.upper_bound)
|
|
139
|
+
assert verify_gscat_cover(K, result.cover)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Mathematically, in the reduced convention,
|
|
143
|
+
|
|
144
|
+
\[
|
|
145
|
+
\operatorname{gscat}(K) = \min\{r \geq 0 : K = K_0 \cup \cdots \cup K_r,\; K_i \text{ strongly collapsible}\}.
|
|
146
|
+
\]
|
|
147
|
+
|
|
148
|
+
When the result's lower and upper bounds agree, the answer is exact. Otherwise, `value` is `None` and the verified cover supplies an upper bound. Search limits can be configured with `max_candidate_states` and `max_search_states`.
|
|
149
|
+
|
|
150
|
+
## Optional CUDA acceleration
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from pysimplicial import cuda_status, gscat
|
|
154
|
+
from pysimplicial.datasets import projective_plane
|
|
155
|
+
|
|
156
|
+
print(cuda_status())
|
|
157
|
+
K = projective_plane()
|
|
158
|
+
result = gscat(K, backend="cuda", restarts=1000, seed=42)
|
|
159
|
+
print(result.value, result.gpu_used, result.exact)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Set `backend="auto"` to use CUDA when available, or `backend="cpu"` for CPU-only computation. Set `restarts=0` to skip randomized restarts and proceed to the exact search. CPU verification is used to check returned covers. The randomized CUDA restart kernel supports up to 64 vertices and 512 facets; larger inputs use the exact-search path.
|
|
163
|
+
|
|
164
|
+
## Datasets
|
|
165
|
+
|
|
166
|
+
### Standard graph families
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from pysimplicial.datasets import (
|
|
170
|
+
complete_graph, complete_bipartite_graph, cycle_graph, path_graph,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
K1 = complete_graph(5)
|
|
174
|
+
K2 = complete_bipartite_graph(3, 4)
|
|
175
|
+
K3 = cycle_graph(6)
|
|
176
|
+
K4 = path_graph(5)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Named triangulations
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from pysimplicial.datasets import (
|
|
183
|
+
torus, projective_plane, tetrahedron_boundary,
|
|
184
|
+
triangulation, triangulation_names,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
T = torus()
|
|
188
|
+
RP2 = projective_plane()
|
|
189
|
+
S2 = tetrahedron_boundary()
|
|
190
|
+
CP2 = triangulation("CP2")
|
|
191
|
+
print(CP2.dimension()) # 4
|
|
192
|
+
print(triangulation_names())
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Random and Vietoris–Rips complexes
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from pysimplicial.datasets import random_d_complex, generate_rips_data
|
|
199
|
+
|
|
200
|
+
K = random_d_complex(n=10, dim=2, seed=42)
|
|
201
|
+
points, R, epsilon = generate_rips_data(n_points=20, shape="circle", seed=42)
|
|
202
|
+
print(R.dimension(), epsilon)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Visualization
|
|
206
|
+
|
|
207
|
+
Install `.[viz]` first.
|
|
208
|
+
|
|
209
|
+
### 2D
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from pysimplicial import gscat
|
|
213
|
+
from pysimplicial.datasets import complete_graph
|
|
214
|
+
from pysimplicial.visualization import draw_complex_and_cover
|
|
215
|
+
|
|
216
|
+
K = complete_graph(5)
|
|
217
|
+
result = gscat(K)
|
|
218
|
+
draw_complex_and_cover(K, result, legend=True)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Interactive 3D
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from pysimplicial import gscat
|
|
225
|
+
from pysimplicial.datasets import projective_plane
|
|
226
|
+
from pysimplicial.visualization import draw_surface_3d
|
|
227
|
+
|
|
228
|
+
K = projective_plane()
|
|
229
|
+
result = gscat(K)
|
|
230
|
+
draw_surface_3d(K, cover=result.cover, show=False, save_html="rp2-cover.html")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Tests and development
|
|
234
|
+
|
|
235
|
+
After installing the package:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
python -m unittest discover -s tests -v
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Build and validate distributions:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
python -m pip install build twine
|
|
245
|
+
python -m build
|
|
246
|
+
python -m twine check dist/*
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The test suite covers topological operations, contiguity, geometric category, datasets, and visualization. CUDA-specific tests require compatible hardware.
|
|
250
|
+
|
|
251
|
+
## Mathematical background and references
|
|
252
|
+
|
|
253
|
+
This project is motivated by computational problems in finite simplicial complexes, strong homotopy theory, contiguity, and simplicial Lusternik–Schnirelmann category.
|
|
254
|
+
|
|
255
|
+
- J. A. Barmak and E. G. Minian, *Strong homotopy types, nerves and collapses*, Discrete & Computational Geometry (2012).
|
|
256
|
+
- D. Fernández-Ternero, E. Macías-Virgós, E. Minuz, and J. A. Vilches, *Simplicial Lusternik–Schnirelmann category*, Publicacions Matemàtiques (2019).
|
|
257
|
+
- [Frank Lutz's triangulated surface catalog](https://www3.math.tu-berlin.de/IfM/Nachrufe/Frank_Lutz/stellar/surfaces.html).
|
|
258
|
+
|
|
259
|
+
## Contributing
|
|
260
|
+
|
|
261
|
+
Bug reports, suggestions, and contributions are welcome. Please open an [issue](https://github.com/zgenng/PySimplicial/issues) to discuss problems or improvements.
|
|
262
|
+
|
|
263
|
+
## Project status
|
|
264
|
+
|
|
265
|
+
PySimplicial is an evolving research-oriented project. Public APIs and algorithms may change as development continues.
|
|
266
|
+
|
|
267
|
+
**Current source version:** `0.2.0`
|
|
268
|
+
|
|
269
|
+
## Author
|
|
270
|
+
|
|
271
|
+
**Islam Yeginbay** — [@zgenng](https://github.com/zgenng/PySimplicial)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project.urls]
|
|
2
|
+
Homepage = "https://github.com/zgenng/PySimplicial"
|
|
3
|
+
Repository = "https://github.com/zgenng/PySimplicial"
|
|
4
|
+
Issues = "https://github.com/zgenng/PySimplicial/issues"
|
|
5
|
+
|
|
6
|
+
[build-system]
|
|
7
|
+
requires = ["setuptools>=61", "wheel"]
|
|
8
|
+
build-backend = "setuptools.build_meta"
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "pysimplicial"
|
|
12
|
+
version = "0.2.0"
|
|
13
|
+
description = "Finite simplicial complexes, contiguity and simplicial LS category"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
|
+
dependencies = ["numpy>=1.24"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
plot = ["matplotlib>=3.6"]
|
|
26
|
+
plot3d = ["plotly>=5"]
|
|
27
|
+
viz = ["matplotlib>=3.6", "plotly>=5"]
|
|
28
|
+
cuda12 = ["numba-cuda[cu12]"]
|
|
29
|
+
cuda13 = ["numba-cuda[cu13]"]
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
"pysimplicial.datasets" = ["*.json"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Finite simplicial complexes, contiguity and simplicial category."""
|
|
2
|
+
|
|
3
|
+
from ._errors import CudaUnavailableError, SearchLimitExceeded
|
|
4
|
+
from .category import (
|
|
5
|
+
CategoryResult,
|
|
6
|
+
GSCatResult,
|
|
7
|
+
contiguity_distance,
|
|
8
|
+
cuda_status,
|
|
9
|
+
gscat,
|
|
10
|
+
is_categorical,
|
|
11
|
+
scat,
|
|
12
|
+
verify_gscat_cover,
|
|
13
|
+
)
|
|
14
|
+
from .collapse import core, dominators, is_strongly_collapsible, strong_core
|
|
15
|
+
from .complex import SimplicialComplex
|
|
16
|
+
from .contiguity import contiguity_chain, contiguity_class, is_contiguous, same_contiguity_class
|
|
17
|
+
from .maps import SimplicialMap
|
|
18
|
+
from .operations import categorical_product, connected_components, disjoint_union, graph_join, join, link, relabel, wedge
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"CategoryResult", "CudaUnavailableError", "GSCatResult", "SearchLimitExceeded",
|
|
22
|
+
"SimplicialComplex", "SimplicialMap", "categorical_product", "connected_components",
|
|
23
|
+
"contiguity_chain", "contiguity_class", "contiguity_distance", "core", "cuda_status",
|
|
24
|
+
"disjoint_union", "dominators", "graph_join", "gscat", "is_categorical",
|
|
25
|
+
"is_contiguous", "is_strongly_collapsible", "join", "link", "relabel",
|
|
26
|
+
"same_contiguity_class", "scat", "strong_core", "verify_gscat_cover", "wedge",
|
|
27
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class SearchLimitExceeded(RuntimeError):
|
|
2
|
+
"""A search budget expired before minimality or reachability was decided."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, message, *, gpu_used=False, cover_lower_bound=None, cover_masks=()):
|
|
5
|
+
super().__init__(message)
|
|
6
|
+
self.gpu_used = gpu_used
|
|
7
|
+
self.cover_lower_bound = cover_lower_bound
|
|
8
|
+
self.cover_masks = cover_masks
|
|
9
|
+
self.cover_witnesses = ()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CudaUnavailableError(RuntimeError):
|
|
13
|
+
"""The explicitly requested CUDA backend is unavailable."""
|
|
File without changes
|