vineyards 1.0.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.
Files changed (89) hide show
  1. vineyards-1.0.0/CHANGELOG.md +42 -0
  2. vineyards-1.0.0/CITATION.cff +22 -0
  3. vineyards-1.0.0/LICENSE +21 -0
  4. vineyards-1.0.0/MANIFEST.in +16 -0
  5. vineyards-1.0.0/PKG-INFO +293 -0
  6. vineyards-1.0.0/README.md +243 -0
  7. vineyards-1.0.0/cpp/exact_poly.cpp +425 -0
  8. vineyards-1.0.0/cpp/kinetic_arith.hpp +300 -0
  9. vineyards-1.0.0/cpp/kinetic_delaunay_2d.cpp +478 -0
  10. vineyards-1.0.0/cpp/kinetic_delaunay_3d.cpp +618 -0
  11. vineyards-1.0.0/cpp/sparse_ru.cpp +552 -0
  12. vineyards-1.0.0/docs/Makefile +12 -0
  13. vineyards-1.0.0/docs/api.rst +89 -0
  14. vineyards-1.0.0/docs/conf.py +48 -0
  15. vineyards-1.0.0/docs/cpp_kernels.rst +30 -0
  16. vineyards-1.0.0/docs/engines.rst +48 -0
  17. vineyards-1.0.0/docs/index.rst +44 -0
  18. vineyards-1.0.0/docs/install.rst +38 -0
  19. vineyards-1.0.0/pyproject.toml +110 -0
  20. vineyards-1.0.0/scripts/smoke_readme.py +106 -0
  21. vineyards-1.0.0/scripts/wheel_smoke.py +52 -0
  22. vineyards-1.0.0/setup.cfg +4 -0
  23. vineyards-1.0.0/setup.py +102 -0
  24. vineyards-1.0.0/tests/test_3d.py +600 -0
  25. vineyards-1.0.0/tests/test_backend_equiv.py +292 -0
  26. vineyards-1.0.0/tests/test_comparison.py +104 -0
  27. vineyards-1.0.0/tests/test_counters.py +82 -0
  28. vineyards-1.0.0/tests/test_cpp_exact_poly.py +129 -0
  29. vineyards-1.0.0/tests/test_d1_validation.py +690 -0
  30. vineyards-1.0.0/tests/test_dionysus_comparison.py +334 -0
  31. vineyards-1.0.0/tests/test_feature_tracking.py +115 -0
  32. vineyards-1.0.0/tests/test_frame_vs_kinetic.py +79 -0
  33. vineyards-1.0.0/tests/test_hollow_triangle.py +150 -0
  34. vineyards-1.0.0/tests/test_kinetic_cpp_2d.py +106 -0
  35. vineyards-1.0.0/tests/test_kinetic_cpp_3d.py +127 -0
  36. vineyards-1.0.0/tests/test_kinetic_delaunay.py +285 -0
  37. vineyards-1.0.0/tests/test_kinetic_delaunay_3d.py +202 -0
  38. vineyards-1.0.0/tests/test_kinetic_exact.py +140 -0
  39. vineyards-1.0.0/tests/test_kinetic_exact_degeneracies.py +105 -0
  40. vineyards-1.0.0/tests/test_kinetic_filtration.py +62 -0
  41. vineyards-1.0.0/tests/test_kinetic_filtration_3d.py +64 -0
  42. vineyards-1.0.0/tests/test_kinetic_filtration_dynamic.py +60 -0
  43. vineyards-1.0.0/tests/test_kinetic_predicates.py +151 -0
  44. vineyards-1.0.0/tests/test_kinetic_vineyard.py +262 -0
  45. vineyards-1.0.0/tests/test_kinetic_vineyard_3d.py +131 -0
  46. vineyards-1.0.0/tests/test_knee_times.py +94 -0
  47. vineyards-1.0.0/tests/test_local_ops.py +237 -0
  48. vineyards-1.0.0/tests/test_moving_vineyard.py +778 -0
  49. vineyards-1.0.0/tests/test_oracle.py +157 -0
  50. vineyards-1.0.0/tests/test_persistence.py +203 -0
  51. vineyards-1.0.0/tests/test_polar_plotting.py +288 -0
  52. vineyards-1.0.0/tests/test_retriangulation.py +418 -0
  53. vineyards-1.0.0/tests/test_sos.py +325 -0
  54. vineyards-1.0.0/tests/test_transposition.py +374 -0
  55. vineyards-1.0.0/tests/test_vineyard.py +904 -0
  56. vineyards-1.0.0/vineyards/__init__.py +149 -0
  57. vineyards-1.0.0/vineyards/_exact_poly.pyi +31 -0
  58. vineyards-1.0.0/vineyards/_kinetic_2d.pyi +23 -0
  59. vineyards-1.0.0/vineyards/_kinetic_3d.pyi +23 -0
  60. vineyards-1.0.0/vineyards/_sparse.pyi +26 -0
  61. vineyards-1.0.0/vineyards/alpha_values.py +154 -0
  62. vineyards-1.0.0/vineyards/backend.py +223 -0
  63. vineyards-1.0.0/vineyards/comparison.py +194 -0
  64. vineyards-1.0.0/vineyards/counters.py +379 -0
  65. vineyards-1.0.0/vineyards/cpp_backend.py +272 -0
  66. vineyards-1.0.0/vineyards/dionysus_bridge.py +306 -0
  67. vineyards-1.0.0/vineyards/feature_tracking.py +248 -0
  68. vineyards-1.0.0/vineyards/kinetic_delaunay.py +632 -0
  69. vineyards-1.0.0/vineyards/kinetic_delaunay_3d.py +620 -0
  70. vineyards-1.0.0/vineyards/kinetic_exact.py +708 -0
  71. vineyards-1.0.0/vineyards/kinetic_filtration.py +466 -0
  72. vineyards-1.0.0/vineyards/kinetic_vineyard.py +343 -0
  73. vineyards-1.0.0/vineyards/kinetic_vineyard_3d.py +136 -0
  74. vineyards-1.0.0/vineyards/oracle.py +153 -0
  75. vineyards-1.0.0/vineyards/persistence.py +972 -0
  76. vineyards-1.0.0/vineyards/plotting.py +257 -0
  77. vineyards-1.0.0/vineyards/polar_plotting.py +562 -0
  78. vineyards-1.0.0/vineyards/predicates.py +188 -0
  79. vineyards-1.0.0/vineyards/py.typed +0 -0
  80. vineyards-1.0.0/vineyards/regular.py +80 -0
  81. vineyards-1.0.0/vineyards/retriangulation.py +525 -0
  82. vineyards-1.0.0/vineyards/sos.py +197 -0
  83. vineyards-1.0.0/vineyards/transposition.py +362 -0
  84. vineyards-1.0.0/vineyards/vineyard.py +812 -0
  85. vineyards-1.0.0/vineyards.egg-info/PKG-INFO +293 -0
  86. vineyards-1.0.0/vineyards.egg-info/SOURCES.txt +87 -0
  87. vineyards-1.0.0/vineyards.egg-info/dependency_links.txt +1 -0
  88. vineyards-1.0.0/vineyards.egg-info/requires.txt +25 -0
  89. vineyards-1.0.0/vineyards.egg-info/top_level.txt +1 -0
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to the installable ``vineyards`` package are recorded here.
4
+ The GitHub Releases page mirrors tagged versions.
5
+
6
+ ## [1.0.0] - 2026-07-17
7
+
8
+ ### Added
9
+ - Event-driven kinetic Delaunay / vineyard engines in 2D and 3D (exact algebraic
10
+ flip and transposition times; INF hull; SoS on space and on time).
11
+ - Optional GMP C++ kernels: ``_exact_poly``, ``_kinetic_2d``, ``_kinetic_3d``
12
+ (bit-exact flip sequences vs pure Python when built).
13
+ - Sparse GF(2) R=DU C++ kernel ``_sparse`` with local insert/delete and
14
+ bubble paths, differential-tested against dense numpy.
15
+ - Certificate poly caches and binary float-key event heaps in kinetic C++.
16
+ - Shared arithmetic header ``cpp/kinetic_arith.hpp``.
17
+ - Oracle snap (``recover`` flip) for residual multi-cospheric 3D instants that
18
+ local bistellar flips cannot order; vineyard construction fails only if the
19
+ snap itself fails.
20
+ - Type stubs (``.pyi``), ``py.typed``, Sphinx docs, Read the Docs and GitHub
21
+ Pages configs, cibuildwheel publish workflow.
22
+
23
+ ### Changed
24
+ - Production default R=DU backend is sparse C++ when built
25
+ (``default_backend_cls()``). Dense remains the bit-exact algebra oracle.
26
+ Override with ``VINEYARDS_BACKEND=dense|sparse``.
27
+ - Kinetic ``run()`` prefers C++ when built; override with
28
+ ``VINEYARDS_KINETIC_CPP=0``.
29
+
30
+ ### Notes
31
+ - Exact kinetic arithmetic favors tens to low hundreds of points; use
32
+ ``track_features`` or frame-bound ``moving_vineyard`` at larger scale.
33
+ - Binary wheels bundle GMP where present (LGPL for that component); MIT for
34
+ this project's own code.
35
+ - cibuildwheel smoke (``scripts/wheel_smoke.py``) imports all four C++ kernels
36
+ on Linux/macOS so GMP bundling failures cannot ship silently; Windows
37
+ requires sparse only.
38
+ - GMP kernels link ``libgmpxx.a`` / ``libgmp.a`` when present (static) so
39
+ delocate/auditwheel do not need to vendor Homebrew or manylinux libgmp
40
+ dylibs (fixes first-release wheel matrix failures).
41
+ - Linux wheels build on **manylinux_2_28** so runtime dependency ``gudhi``
42
+ (manylinux_2_28 wheels only) can install during cibuildwheel's test step.
@@ -0,0 +1,22 @@
1
+ cff-version: 1.2.0
2
+ title: "Vineyards over changing triangulations"
3
+ message: "If you use this software, please cite it using these metadata."
4
+ type: software
5
+ authors:
6
+ - given-names: Shawn
7
+ family-names: Ray
8
+ repository-code: "https://github.com/codingwithshawnyt/vineyards"
9
+ abstract: >-
10
+ Exact persistent-homology vineyards for moving 2D and 3D point sets whose
11
+ Delaunay / alpha complex changes by flips: vine identity is carried through
12
+ the flips from the maintained persistence pairing, with no diagram-matching
13
+ heuristic. Includes frame-bound and event-driven (kinetic) engines, a weighted
14
+ (regular / Laguerre) alpha path for points carrying radii.
15
+ keywords:
16
+ - topological data analysis
17
+ - persistent homology
18
+ - vineyards
19
+ - Delaunay triangulation
20
+ - kinetic data structures
21
+ license: MIT
22
+ version: 1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shawn Ray
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,16 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include pyproject.toml
6
+ include setup.py
7
+ recursive-include cpp *.cpp *.hpp *.h
8
+ recursive-include vineyards *.py *.pyi
9
+ recursive-include docs *.rst *.py Makefile
10
+ recursive-include scripts *.py
11
+ graft vineyards
12
+ global-exclude *.pyc
13
+ global-exclude __pycache__
14
+ global-exclude *.so
15
+ global-exclude *.dylib
16
+ prune docs/_build
@@ -0,0 +1,293 @@
1
+ Metadata-Version: 2.4
2
+ Name: vineyards
3
+ Version: 1.0.0
4
+ Summary: Vineyards over changing triangulations: exact vine tracking for moving 2D and 3D point sets through Delaunay flips (frame-bound and event-driven).
5
+ Author-email: Shawn Ray <shawnray5699@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/codingwithshawnyt/vineyards
8
+ Project-URL: Repository, https://github.com/codingwithshawnyt/vineyards
9
+ Project-URL: Issues, https://github.com/codingwithshawnyt/vineyards/issues
10
+ Project-URL: Changelog, https://github.com/codingwithshawnyt/vineyards/blob/main/CHANGELOG.md
11
+ Keywords: topological-data-analysis,persistent-homology,vineyards,Delaunay,kinetic-data-structures,computational-topology
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Typing :: Typed
18
+ Classifier: Programming Language :: C++
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
25
+ Classifier: Topic :: Scientific/Engineering :: Visualization
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: numpy>=1.24
30
+ Requires-Dist: gudhi>=3.6
31
+ Provides-Extra: test
32
+ Requires-Dist: pytest>=7.0; extra == "test"
33
+ Requires-Dist: matplotlib>=3.7; extra == "test"
34
+ Requires-Dist: pillow>=9.0; extra == "test"
35
+ Requires-Dist: pybind11>=2.10; extra == "test"
36
+ Requires-Dist: setuptools>=61.0; extra == "test"
37
+ Provides-Extra: dionysus
38
+ Requires-Dist: dionysus>=2.2; extra == "dionysus"
39
+ Provides-Extra: plot
40
+ Requires-Dist: matplotlib>=3.7; extra == "plot"
41
+ Requires-Dist: pillow>=9.0; extra == "plot"
42
+ Provides-Extra: viz
43
+ Requires-Dist: matplotlib>=3.7; extra == "viz"
44
+ Requires-Dist: pillow>=9.0; extra == "viz"
45
+ Requires-Dist: pyvista>=0.44; extra == "viz"
46
+ Provides-Extra: docs
47
+ Requires-Dist: sphinx>=7.0; extra == "docs"
48
+ Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
49
+ Dynamic: license-file
50
+
51
+ # Vineyards over changing triangulations
52
+
53
+ A vineyard (Cohen-Steiner, Edelsbrunner, Morozov, SoCG 2006) is the 1-parameter
54
+ family of persistence diagrams of a changing filtration: each diagram point
55
+ traces a vine through time. CEM06 updates the decomposition `D = R·U` in linear
56
+ time per transposition when the complex is fixed and only filtration values
57
+ move. This library closes the remaining case: moving 2D/3D point sets whose
58
+ Delaunay/alpha complex changes by flips. Flips are handled by simplex
59
+ delete/insert on the CEM06 transposition kernel, and vine identity is carried
60
+ through those updates from the maintained pairing (no diagram-point matching).
61
+
62
+ Two engines share that handoff. `moving_vineyard` is frame-bound (sample motion,
63
+ diff consecutive complexes). `KineticVineyard2D` / `KineticVineyard3D` are
64
+ event-driven: linear trajectories, flips and alpha-value crossings at exact
65
+ algebraic times. For hundreds to thousands of points, `track_features` matches
66
+ prominent GUDHI diagram points across frames. Weighted (regular/Laguerre) alpha
67
+ accepts `weights=` (squared radii); use `assert_no_hidden` when a point can drop
68
+ out of the regular triangulation.
69
+
70
+ ![vineyard figure](https://raw.githubusercontent.com/codingwithshawnyt/vineyards/main/examples/vineyard.png)
71
+
72
+ ## Install
73
+
74
+ From PyPI (recommended):
75
+
76
+ ```bash
77
+ pip install vineyards
78
+ ```
79
+
80
+ That pulls `numpy` and `gudhi`. A C++17 compiler is required to build the sparse
81
+ GF(2) kernel at install time. For the optional GMP exact-arithmetic kernel:
82
+
83
+ ```bash
84
+ # Debian/Ubuntu
85
+ sudo apt install build-essential libgmp-dev python3-dev
86
+
87
+ # macOS (Homebrew)
88
+ brew install gmp
89
+ ```
90
+
91
+ From a clone (development):
92
+
93
+ ```bash
94
+ python3 -m venv .venv
95
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
96
+ pip install -U pip setuptools wheel pybind11
97
+ pip install -e '.[test]'
98
+ python setup.py build_ext --inplace # _sparse; with GMP also _exact_poly, _kinetic_2d, _kinetic_3d
99
+ pytest -q -m "not slow"
100
+ ```
101
+
102
+ Extras: `pip install 'vineyards[plot]'` (matplotlib) or `'vineyards[viz]'`
103
+ (matplotlib + pyvista). Plotting imports those only when called. Dionysus
104
+ cross-checks: `pip install 'vineyards[dionysus]'`. Documentation:
105
+ `pip install 'vineyards[docs]'` then `cd docs && make html` (also built on
106
+ GitHub Pages / Read the Docs). Override the GMP prefix with `GMP_PREFIX` if
107
+ needed (Homebrew defaults to `/opt/homebrew` or `/usr/local`).
108
+
109
+ Environment toggles:
110
+
111
+ | Variable | Effect |
112
+ | --- | --- |
113
+ | `VINEYARDS_BACKEND=dense` | Force dense numpy R=DU (default: sparse C++ when built) |
114
+ | `VINEYARDS_BACKEND=sparse` | Require sparse C++ (error if extension missing) |
115
+ | `VINEYARDS_KINETIC_CPP=0` | Force pure-Python kinetic Delaunay (default: C++ when built) |
116
+
117
+ ## Quickstart
118
+
119
+ Frame-bound vineyard on a moving point set:
120
+
121
+ ```python
122
+ import numpy as np
123
+ from vineyards import (
124
+ PersistenceState, gudhi_alpha_filtration,
125
+ moving_vineyard, compute_vineyard, plot_vineyard,
126
+ )
127
+
128
+ points = np.random.default_rng(0).random((20, 2))
129
+ state = PersistenceState.from_simplex_tree(gudhi_alpha_filtration(points))
130
+ diagram = state.diagram() # rows (dim, birth, death)
131
+
132
+ target = {sigma: 2.0 * v for sigma, v in state.filtration}
133
+ vineyard, events = compute_vineyard(state, target, num_samples=50)
134
+
135
+ frames = [points + 0.02 * k for k in range(10)]
136
+ vineyard, events = moving_vineyard(frames)
137
+ plot_vineyard(vineyard, filename="vineyard.png")
138
+ ```
139
+
140
+ Event-driven (kinetic) vineyard:
141
+
142
+ ```python
143
+ from fractions import Fraction
144
+ import numpy as np
145
+ from vineyards import LinearTrajectory, KineticVineyard2D # or KineticVineyard3D
146
+
147
+ pts = np.random.default_rng(0).random((10, 2))
148
+ vel = 0.3 * np.random.default_rng(1).standard_normal((10, 2))
149
+ traj = LinearTrajectory(pos0=pts, vel=vel)
150
+ kv = KineticVineyard2D(traj, horizon=Fraction(1, 4))
151
+ dgm = kv.diagram_at(Fraction(1, 6))
152
+ kv.record(Fraction(1, 4))
153
+ assert kv.verify_identity()
154
+ vineyard = kv.vineyard()
155
+ ```
156
+
157
+ Lower-star filtrations on a fixed complex use
158
+ `lower_star_filtration(simplices, vertex_values)`. The default R=DU backend is
159
+ the sparse C++ kernel when built (`CppSparseBackend`); force dense with
160
+ `VINEYARDS_BACKEND=dense` or `backend_cls=DenseBackend`.
161
+
162
+ ## Positioning
163
+
164
+ [Dionysus 2](https://www.mrzv.org/software/dionysus2/) (≥ 2.2) implements CEM06
165
+ vineyards on a fixed complex; Dionysus 1 is the earlier public vineyards code.
166
+ [GUDHI](https://gudhi.inria.fr/) supplies alpha/Delaunay geometry and the
167
+ independent diagram oracle used throughout this repository. Fixed-complex
168
+ homotopies are checked against Dionysus when installed
169
+ (`tests/test_dionysus_comparison.py`); changing triangulations are outside
170
+ Dionysus and are the scope of `moving_vineyard` and the kinetic engines.
171
+
172
+ ## Validation
173
+
174
+ Claims below name the tests that assert them (run with `pytest`):
175
+
176
+ | Claim | Tests |
177
+ | --- | --- |
178
+ | Diagrams match GUDHI after retriangulation / moving frames | `tests/test_retriangulation.py`, `tests/test_moving_vineyard.py`, `tests/test_3d.py` |
179
+ | Algebraic invariants (`D = R·U`, reduced `R`, unitriangular `U`) | `PersistenceState.verify` exercised across the suite (e.g. `tests/test_persistence.py`, `tests/test_local_ops.py`) |
180
+ | Vine identity vs reduced pairing | `tests/test_vineyard.py`, `tests/test_moving_vineyard.py` (`test_vine_identity_through_switches`) |
181
+ | Kinetic Delaunay vs GUDHI (2D / 3D) | `tests/test_kinetic_delaunay.py`, `tests/test_kinetic_delaunay_3d.py` |
182
+ | Kinetic filtration order vs GUDHI | `tests/test_kinetic_filtration.py`, `tests/test_kinetic_filtration_3d.py`, `tests/test_kinetic_filtration_dynamic.py` |
183
+ | Kinetic vineyard diagrams + vine identity vs GUDHI | `tests/test_kinetic_vineyard.py`, `tests/test_kinetic_vineyard_3d.py` |
184
+ | Dense vs sparse backend (`R`/`U`/`low` and diagrams) | `tests/test_backend_equiv.py` |
185
+ | Python vs C++ exact poly / Sturm | `tests/test_cpp_exact_poly.py` |
186
+ | SoS signs vs shrinking-ε and raw determinants | `tests/test_sos.py` |
187
+ | Weighted alpha vs weighted GUDHI | `tests/test_3d.py` (weighted cases) |
188
+ | Weighted flip continuity (bracketing) | `tests/test_3d.py::test_weighted_flip_continuity_lemma` |
189
+ | Multi-flip-at-one-τ kinetic vineyard | `tests/test_kinetic_vineyard.py::test_kinetic_vineyard_through_coincident_flip_cluster` |
190
+ | Frame-bound vs kinetic cancellation | `tests/test_frame_vs_kinetic.py` |
191
+ | Sparse bubble insert/delete vs dense | `tests/test_backend_equiv.py::test_sparse_bubble_path_matches_dense` |
192
+
193
+ ## Scope and limitations
194
+
195
+ - Frame-bound engine: exact on the piecewise-linear interpolation of the
196
+ samples; events that cancel inside one frame interval are invisible (the
197
+ kinetic engine is the remedy; see `tests/test_frame_vs_kinetic.py`).
198
+ - Kinetic engine: exact event times, 2D and 3D (interior flips and hull
199
+ changes via vertex-at-infinity). With GMP, `run()` uses the C++ kernels
200
+ (`vineyards._kinetic_2d` / `vineyards._kinetic_3d`) with integer-coefficient
201
+ certificates (scale-cleared trajectories), bit-exact on flip sequences
202
+ (`tests/test_kinetic_cpp_2d.py`, `tests/test_kinetic_cpp_3d.py`); set
203
+ `VINEYARDS_KINETIC_CPP=0` for pure Python. The C++ event queue uses tight
204
+ root isolation and float-first extract-min (exact AlgT only on near-ties).
205
+ Exact arithmetic still favors tens-low hundreds of points for long horizons;
206
+ use `track_features` or frame-bound at scale.
207
+ - Degeneracies: geometric predicates use Edelsbrunner-Mücke Simulation of
208
+ Simplicity; coincident flip certificates (interior InCircle/InSphere and
209
+ hull/spoke orientation vs infinity) are resolved by SoS on time. Hull
210
+ legality is the geometric supporting-line / extreme-vertex test (raw
211
+ orientation so collinear hull points stay on the line). Several flips at
212
+ one algebraic time are batched in the kinetic vineyard. A residual that
213
+ local flips cannot order is finished by an oracle snap of the Delaunay
214
+ complex (recorded as a ``recover`` flip); only an unsuccessful snap raises
215
+ at vineyard construction.
216
+ - Weighted alpha: non-hiding is required (`assert_no_hidden`); the weighted
217
+ flip-continuity handoff is confirmed numerically against weighted GUDHI
218
+ in `tests/test_3d.py`.
219
+ - Geometry for frame-bound alpha values is GUDHI's; the kinetic path has its
220
+ own exact predicates. **Default R=DU backend is sparse C++** when
221
+ `vineyards._sparse` is built (`default_backend_cls()` /
222
+ `CppSparseBackend`); dense numpy remains the bit-exact reference oracle
223
+ (`VINEYARDS_BACKEND=dense` or `backend_cls=DenseBackend`). Install builds
224
+ the sparse kernel when a C++17 toolchain is available. GMP enables the
225
+ exact-poly and kinetic-2D/3D kernels (shared arithmetic in
226
+ `cpp/kinetic_arith.hpp`; binary float-key event heaps; certificate poly
227
+ caches; optional if GMP headers are missing; CI on Linux asserts all four
228
+ modules import). Type stubs (`.pyi`) ship for the C extensions. Docs:
229
+ `docs/` (Sphinx; GitHub Pages + Read the Docs configs). Releases publish
230
+ **sdists and platform wheels** via cibuildwheel.
231
+ - CI: Linux (Ubuntu) is the primary surface. Windows builds the sparse kernel
232
+ and runs core tests (GMP exact-poly / kinetic-2D/3D are skipped when GMP is
233
+ absent).
234
+
235
+ ## Measured sparse speedups
236
+
237
+ From `python examples/benchmark_sparse.py` on this machine after
238
+ `python setup.py build_ext --inplace` (Apple M4 Max, macOS 15, arm64, Python
239
+ 3.13). Columns are dense vs C++ sparse wall time and the ratio dense/sparse;
240
+ `nnzR/m^2` and `nnzU/m^2` are filled fractions of the reduced matrices.
241
+
242
+ | n | m | nnzR/m^2 | nnzU/m^2 | reduce | update | local |
243
+ |----:|----:|---------:|---------:|-------:|-------:|------:|
244
+ | 40 | 217 | 0.006 | 0.012 | 7.1x | 123.1x | 16.4x |
245
+ | 70 | 393 | 0.004 | 0.007 | 8.0x | 200.6x | 15.6x |
246
+ | 100 | 575 | 0.003 | 0.005 | 10.3x | 211.9x | 19.0x |
247
+
248
+ Re-run the script on your hardware; numbers vary with CPU and size.
249
+
250
+ ## Knee rates
251
+
252
+ Pairing-switch share of value-update transpositions under random-walk motion
253
+ (`update%` in the script). From `python examples/counter_report.py` on the same
254
+ machine (Apple M4 Max, macOS 15, arm64):
255
+
256
+ | n | m | knee/switch % | crossings/flip | changed/flip | flip frames |
257
+ |----:|-----:|------:|-------:|------:|----:|
258
+ | 8 | 33 | 9.42% | 19.8 | 9.6 | 82 |
259
+ | 30 | 159 | 3.16% | 549.4 | 91.0 | 45 |
260
+ | 100 | 572 | 1.21% | 5747.7 | 540.2 | 16 |
261
+ | 300 | 1767 | 0.52% | 27006.2 | 2181.6 | 5 |
262
+
263
+ ## References
264
+
265
+ - D. Cohen-Steiner, H. Edelsbrunner, D. Morozov. Vines and Vineyards by
266
+ Updating Persistence in Linear Time. In Proc. 22nd Annu. Symp. Comput.
267
+ Geom. (SoCG), pp. 119-126, 2006.
268
+ - H. Edelsbrunner, D. Letscher, A. Zomorodian. Topological Persistence and
269
+ Simplification. Discrete Comput. Geom. 28:511-533, 2002.
270
+ - H. Edelsbrunner, J. Harer. Computational Topology: An Introduction. AMS,
271
+ 2010.
272
+ - H. Edelsbrunner, E. P. Mücke. Simulation of Simplicity: A Technique to Cope
273
+ with Degenerate Cases in Geometric Algorithms. ACM Trans. Graph. 9(1):66-104,
274
+ 1990.
275
+ - The GUDHI Project. GUDHI User and Reference Manual.
276
+ https://gudhi.inria.fr/doc/latest/
277
+ - D. Morozov. Dionysus 2. https://www.mrzv.org/software/dionysus2/
278
+
279
+ ## Citation
280
+
281
+ ```bibtex
282
+ @software{ray_vineyards,
283
+ author = {Ray, Shawn},
284
+ title = {Vineyards over changing triangulations},
285
+ url = {https://github.com/codingwithshawnyt/vineyards},
286
+ year = {2026}
287
+ }
288
+ ```
289
+
290
+ See also `CITATION.cff`.
291
+
292
+ Research code from a summer internship at ISTA (group of Herbert Edelsbrunner),
293
+ 2026.
@@ -0,0 +1,243 @@
1
+ # Vineyards over changing triangulations
2
+
3
+ A vineyard (Cohen-Steiner, Edelsbrunner, Morozov, SoCG 2006) is the 1-parameter
4
+ family of persistence diagrams of a changing filtration: each diagram point
5
+ traces a vine through time. CEM06 updates the decomposition `D = R·U` in linear
6
+ time per transposition when the complex is fixed and only filtration values
7
+ move. This library closes the remaining case: moving 2D/3D point sets whose
8
+ Delaunay/alpha complex changes by flips. Flips are handled by simplex
9
+ delete/insert on the CEM06 transposition kernel, and vine identity is carried
10
+ through those updates from the maintained pairing (no diagram-point matching).
11
+
12
+ Two engines share that handoff. `moving_vineyard` is frame-bound (sample motion,
13
+ diff consecutive complexes). `KineticVineyard2D` / `KineticVineyard3D` are
14
+ event-driven: linear trajectories, flips and alpha-value crossings at exact
15
+ algebraic times. For hundreds to thousands of points, `track_features` matches
16
+ prominent GUDHI diagram points across frames. Weighted (regular/Laguerre) alpha
17
+ accepts `weights=` (squared radii); use `assert_no_hidden` when a point can drop
18
+ out of the regular triangulation.
19
+
20
+ ![vineyard figure](https://raw.githubusercontent.com/codingwithshawnyt/vineyards/main/examples/vineyard.png)
21
+
22
+ ## Install
23
+
24
+ From PyPI (recommended):
25
+
26
+ ```bash
27
+ pip install vineyards
28
+ ```
29
+
30
+ That pulls `numpy` and `gudhi`. A C++17 compiler is required to build the sparse
31
+ GF(2) kernel at install time. For the optional GMP exact-arithmetic kernel:
32
+
33
+ ```bash
34
+ # Debian/Ubuntu
35
+ sudo apt install build-essential libgmp-dev python3-dev
36
+
37
+ # macOS (Homebrew)
38
+ brew install gmp
39
+ ```
40
+
41
+ From a clone (development):
42
+
43
+ ```bash
44
+ python3 -m venv .venv
45
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
46
+ pip install -U pip setuptools wheel pybind11
47
+ pip install -e '.[test]'
48
+ python setup.py build_ext --inplace # _sparse; with GMP also _exact_poly, _kinetic_2d, _kinetic_3d
49
+ pytest -q -m "not slow"
50
+ ```
51
+
52
+ Extras: `pip install 'vineyards[plot]'` (matplotlib) or `'vineyards[viz]'`
53
+ (matplotlib + pyvista). Plotting imports those only when called. Dionysus
54
+ cross-checks: `pip install 'vineyards[dionysus]'`. Documentation:
55
+ `pip install 'vineyards[docs]'` then `cd docs && make html` (also built on
56
+ GitHub Pages / Read the Docs). Override the GMP prefix with `GMP_PREFIX` if
57
+ needed (Homebrew defaults to `/opt/homebrew` or `/usr/local`).
58
+
59
+ Environment toggles:
60
+
61
+ | Variable | Effect |
62
+ | --- | --- |
63
+ | `VINEYARDS_BACKEND=dense` | Force dense numpy R=DU (default: sparse C++ when built) |
64
+ | `VINEYARDS_BACKEND=sparse` | Require sparse C++ (error if extension missing) |
65
+ | `VINEYARDS_KINETIC_CPP=0` | Force pure-Python kinetic Delaunay (default: C++ when built) |
66
+
67
+ ## Quickstart
68
+
69
+ Frame-bound vineyard on a moving point set:
70
+
71
+ ```python
72
+ import numpy as np
73
+ from vineyards import (
74
+ PersistenceState, gudhi_alpha_filtration,
75
+ moving_vineyard, compute_vineyard, plot_vineyard,
76
+ )
77
+
78
+ points = np.random.default_rng(0).random((20, 2))
79
+ state = PersistenceState.from_simplex_tree(gudhi_alpha_filtration(points))
80
+ diagram = state.diagram() # rows (dim, birth, death)
81
+
82
+ target = {sigma: 2.0 * v for sigma, v in state.filtration}
83
+ vineyard, events = compute_vineyard(state, target, num_samples=50)
84
+
85
+ frames = [points + 0.02 * k for k in range(10)]
86
+ vineyard, events = moving_vineyard(frames)
87
+ plot_vineyard(vineyard, filename="vineyard.png")
88
+ ```
89
+
90
+ Event-driven (kinetic) vineyard:
91
+
92
+ ```python
93
+ from fractions import Fraction
94
+ import numpy as np
95
+ from vineyards import LinearTrajectory, KineticVineyard2D # or KineticVineyard3D
96
+
97
+ pts = np.random.default_rng(0).random((10, 2))
98
+ vel = 0.3 * np.random.default_rng(1).standard_normal((10, 2))
99
+ traj = LinearTrajectory(pos0=pts, vel=vel)
100
+ kv = KineticVineyard2D(traj, horizon=Fraction(1, 4))
101
+ dgm = kv.diagram_at(Fraction(1, 6))
102
+ kv.record(Fraction(1, 4))
103
+ assert kv.verify_identity()
104
+ vineyard = kv.vineyard()
105
+ ```
106
+
107
+ Lower-star filtrations on a fixed complex use
108
+ `lower_star_filtration(simplices, vertex_values)`. The default R=DU backend is
109
+ the sparse C++ kernel when built (`CppSparseBackend`); force dense with
110
+ `VINEYARDS_BACKEND=dense` or `backend_cls=DenseBackend`.
111
+
112
+ ## Positioning
113
+
114
+ [Dionysus 2](https://www.mrzv.org/software/dionysus2/) (≥ 2.2) implements CEM06
115
+ vineyards on a fixed complex; Dionysus 1 is the earlier public vineyards code.
116
+ [GUDHI](https://gudhi.inria.fr/) supplies alpha/Delaunay geometry and the
117
+ independent diagram oracle used throughout this repository. Fixed-complex
118
+ homotopies are checked against Dionysus when installed
119
+ (`tests/test_dionysus_comparison.py`); changing triangulations are outside
120
+ Dionysus and are the scope of `moving_vineyard` and the kinetic engines.
121
+
122
+ ## Validation
123
+
124
+ Claims below name the tests that assert them (run with `pytest`):
125
+
126
+ | Claim | Tests |
127
+ | --- | --- |
128
+ | Diagrams match GUDHI after retriangulation / moving frames | `tests/test_retriangulation.py`, `tests/test_moving_vineyard.py`, `tests/test_3d.py` |
129
+ | Algebraic invariants (`D = R·U`, reduced `R`, unitriangular `U`) | `PersistenceState.verify` exercised across the suite (e.g. `tests/test_persistence.py`, `tests/test_local_ops.py`) |
130
+ | Vine identity vs reduced pairing | `tests/test_vineyard.py`, `tests/test_moving_vineyard.py` (`test_vine_identity_through_switches`) |
131
+ | Kinetic Delaunay vs GUDHI (2D / 3D) | `tests/test_kinetic_delaunay.py`, `tests/test_kinetic_delaunay_3d.py` |
132
+ | Kinetic filtration order vs GUDHI | `tests/test_kinetic_filtration.py`, `tests/test_kinetic_filtration_3d.py`, `tests/test_kinetic_filtration_dynamic.py` |
133
+ | Kinetic vineyard diagrams + vine identity vs GUDHI | `tests/test_kinetic_vineyard.py`, `tests/test_kinetic_vineyard_3d.py` |
134
+ | Dense vs sparse backend (`R`/`U`/`low` and diagrams) | `tests/test_backend_equiv.py` |
135
+ | Python vs C++ exact poly / Sturm | `tests/test_cpp_exact_poly.py` |
136
+ | SoS signs vs shrinking-ε and raw determinants | `tests/test_sos.py` |
137
+ | Weighted alpha vs weighted GUDHI | `tests/test_3d.py` (weighted cases) |
138
+ | Weighted flip continuity (bracketing) | `tests/test_3d.py::test_weighted_flip_continuity_lemma` |
139
+ | Multi-flip-at-one-τ kinetic vineyard | `tests/test_kinetic_vineyard.py::test_kinetic_vineyard_through_coincident_flip_cluster` |
140
+ | Frame-bound vs kinetic cancellation | `tests/test_frame_vs_kinetic.py` |
141
+ | Sparse bubble insert/delete vs dense | `tests/test_backend_equiv.py::test_sparse_bubble_path_matches_dense` |
142
+
143
+ ## Scope and limitations
144
+
145
+ - Frame-bound engine: exact on the piecewise-linear interpolation of the
146
+ samples; events that cancel inside one frame interval are invisible (the
147
+ kinetic engine is the remedy; see `tests/test_frame_vs_kinetic.py`).
148
+ - Kinetic engine: exact event times, 2D and 3D (interior flips and hull
149
+ changes via vertex-at-infinity). With GMP, `run()` uses the C++ kernels
150
+ (`vineyards._kinetic_2d` / `vineyards._kinetic_3d`) with integer-coefficient
151
+ certificates (scale-cleared trajectories), bit-exact on flip sequences
152
+ (`tests/test_kinetic_cpp_2d.py`, `tests/test_kinetic_cpp_3d.py`); set
153
+ `VINEYARDS_KINETIC_CPP=0` for pure Python. The C++ event queue uses tight
154
+ root isolation and float-first extract-min (exact AlgT only on near-ties).
155
+ Exact arithmetic still favors tens-low hundreds of points for long horizons;
156
+ use `track_features` or frame-bound at scale.
157
+ - Degeneracies: geometric predicates use Edelsbrunner-Mücke Simulation of
158
+ Simplicity; coincident flip certificates (interior InCircle/InSphere and
159
+ hull/spoke orientation vs infinity) are resolved by SoS on time. Hull
160
+ legality is the geometric supporting-line / extreme-vertex test (raw
161
+ orientation so collinear hull points stay on the line). Several flips at
162
+ one algebraic time are batched in the kinetic vineyard. A residual that
163
+ local flips cannot order is finished by an oracle snap of the Delaunay
164
+ complex (recorded as a ``recover`` flip); only an unsuccessful snap raises
165
+ at vineyard construction.
166
+ - Weighted alpha: non-hiding is required (`assert_no_hidden`); the weighted
167
+ flip-continuity handoff is confirmed numerically against weighted GUDHI
168
+ in `tests/test_3d.py`.
169
+ - Geometry for frame-bound alpha values is GUDHI's; the kinetic path has its
170
+ own exact predicates. **Default R=DU backend is sparse C++** when
171
+ `vineyards._sparse` is built (`default_backend_cls()` /
172
+ `CppSparseBackend`); dense numpy remains the bit-exact reference oracle
173
+ (`VINEYARDS_BACKEND=dense` or `backend_cls=DenseBackend`). Install builds
174
+ the sparse kernel when a C++17 toolchain is available. GMP enables the
175
+ exact-poly and kinetic-2D/3D kernels (shared arithmetic in
176
+ `cpp/kinetic_arith.hpp`; binary float-key event heaps; certificate poly
177
+ caches; optional if GMP headers are missing; CI on Linux asserts all four
178
+ modules import). Type stubs (`.pyi`) ship for the C extensions. Docs:
179
+ `docs/` (Sphinx; GitHub Pages + Read the Docs configs). Releases publish
180
+ **sdists and platform wheels** via cibuildwheel.
181
+ - CI: Linux (Ubuntu) is the primary surface. Windows builds the sparse kernel
182
+ and runs core tests (GMP exact-poly / kinetic-2D/3D are skipped when GMP is
183
+ absent).
184
+
185
+ ## Measured sparse speedups
186
+
187
+ From `python examples/benchmark_sparse.py` on this machine after
188
+ `python setup.py build_ext --inplace` (Apple M4 Max, macOS 15, arm64, Python
189
+ 3.13). Columns are dense vs C++ sparse wall time and the ratio dense/sparse;
190
+ `nnzR/m^2` and `nnzU/m^2` are filled fractions of the reduced matrices.
191
+
192
+ | n | m | nnzR/m^2 | nnzU/m^2 | reduce | update | local |
193
+ |----:|----:|---------:|---------:|-------:|-------:|------:|
194
+ | 40 | 217 | 0.006 | 0.012 | 7.1x | 123.1x | 16.4x |
195
+ | 70 | 393 | 0.004 | 0.007 | 8.0x | 200.6x | 15.6x |
196
+ | 100 | 575 | 0.003 | 0.005 | 10.3x | 211.9x | 19.0x |
197
+
198
+ Re-run the script on your hardware; numbers vary with CPU and size.
199
+
200
+ ## Knee rates
201
+
202
+ Pairing-switch share of value-update transpositions under random-walk motion
203
+ (`update%` in the script). From `python examples/counter_report.py` on the same
204
+ machine (Apple M4 Max, macOS 15, arm64):
205
+
206
+ | n | m | knee/switch % | crossings/flip | changed/flip | flip frames |
207
+ |----:|-----:|------:|-------:|------:|----:|
208
+ | 8 | 33 | 9.42% | 19.8 | 9.6 | 82 |
209
+ | 30 | 159 | 3.16% | 549.4 | 91.0 | 45 |
210
+ | 100 | 572 | 1.21% | 5747.7 | 540.2 | 16 |
211
+ | 300 | 1767 | 0.52% | 27006.2 | 2181.6 | 5 |
212
+
213
+ ## References
214
+
215
+ - D. Cohen-Steiner, H. Edelsbrunner, D. Morozov. Vines and Vineyards by
216
+ Updating Persistence in Linear Time. In Proc. 22nd Annu. Symp. Comput.
217
+ Geom. (SoCG), pp. 119-126, 2006.
218
+ - H. Edelsbrunner, D. Letscher, A. Zomorodian. Topological Persistence and
219
+ Simplification. Discrete Comput. Geom. 28:511-533, 2002.
220
+ - H. Edelsbrunner, J. Harer. Computational Topology: An Introduction. AMS,
221
+ 2010.
222
+ - H. Edelsbrunner, E. P. Mücke. Simulation of Simplicity: A Technique to Cope
223
+ with Degenerate Cases in Geometric Algorithms. ACM Trans. Graph. 9(1):66-104,
224
+ 1990.
225
+ - The GUDHI Project. GUDHI User and Reference Manual.
226
+ https://gudhi.inria.fr/doc/latest/
227
+ - D. Morozov. Dionysus 2. https://www.mrzv.org/software/dionysus2/
228
+
229
+ ## Citation
230
+
231
+ ```bibtex
232
+ @software{ray_vineyards,
233
+ author = {Ray, Shawn},
234
+ title = {Vineyards over changing triangulations},
235
+ url = {https://github.com/codingwithshawnyt/vineyards},
236
+ year = {2026}
237
+ }
238
+ ```
239
+
240
+ See also `CITATION.cff`.
241
+
242
+ Research code from a summer internship at ISTA (group of Herbert Edelsbrunner),
243
+ 2026.