holonomy-lib 0.8.0__py3-none-any.whl
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.
- holonomy_explorer/__init__.py +1 -0
- holonomy_explorer/__main__.py +79 -0
- holonomy_explorer/batching.py +182 -0
- holonomy_explorer/common.py +187 -0
- holonomy_explorer/curvature.py +154 -0
- holonomy_explorer/datasets.py +346 -0
- holonomy_explorer/graphs.py +286 -0
- holonomy_explorer/server.py +172 -0
- holonomy_explorer/static/app.js +435 -0
- holonomy_explorer/static/charts.js +114 -0
- holonomy_explorer/static/index.html +45 -0
- holonomy_explorer/static/layout.js +85 -0
- holonomy_explorer/static/style.css +121 -0
- holonomy_explorer/static/view.js +195 -0
- holonomy_explorer/topology.py +118 -0
- holonomy_lib/__init__.py +9 -0
- holonomy_lib/_graph_utils.py +52 -0
- holonomy_lib/_quadrature.py +74 -0
- holonomy_lib/algebra/__init__.py +32 -0
- holonomy_lib/algebra/eigsh_sparse.py +251 -0
- holonomy_lib/algebra/lanczos.py +361 -0
- holonomy_lib/algebra/linear.py +180 -0
- holonomy_lib/algebra/solvers.py +547 -0
- holonomy_lib/convex/__init__.py +27 -0
- holonomy_lib/convex/least_squares.py +184 -0
- holonomy_lib/discrete_geometry/__init__.py +38 -0
- holonomy_lib/discrete_geometry/forman.py +242 -0
- holonomy_lib/discrete_geometry/ricci.py +855 -0
- holonomy_lib/experimental/__init__.py +13 -0
- holonomy_lib/experimental/heterogeneous_kappa.py +361 -0
- holonomy_lib/experimental/laplacian_eigenmaps.py +200 -0
- holonomy_lib/hyperbolic/__init__.py +27 -0
- holonomy_lib/hyperbolic/frechet_mean.py +381 -0
- holonomy_lib/hyperbolic/heat_kernel.py +608 -0
- holonomy_lib/hyperbolic/manifold_inner.py +81 -0
- holonomy_lib/info_geometry/__init__.py +64 -0
- holonomy_lib/info_geometry/divergences.py +224 -0
- holonomy_lib/info_geometry/fisher.py +179 -0
- holonomy_lib/lie/__init__.py +54 -0
- holonomy_lib/lie/so3.py +385 -0
- holonomy_lib/lie/spherical_harmonics.py +189 -0
- holonomy_lib/manifolds/__init__.py +53 -0
- holonomy_lib/manifolds/comparison.py +343 -0
- holonomy_lib/manifolds/fixed_rank.py +453 -0
- holonomy_lib/manifolds/lorentz.py +699 -0
- holonomy_lib/manifolds/lorentzian.py +567 -0
- holonomy_lib/manifolds/product.py +425 -0
- holonomy_lib/manifolds/spd.py +504 -0
- holonomy_lib/manifolds/stereographic.py +680 -0
- holonomy_lib/optimization/__init__.py +42 -0
- holonomy_lib/optimization/base.py +65 -0
- holonomy_lib/optimization/sgd.py +112 -0
- holonomy_lib/py.typed +0 -0
- holonomy_lib/sheaf/__init__.py +74 -0
- holonomy_lib/sheaf/graph_sheaf.py +352 -0
- holonomy_lib/sheaf/laplacian.py +529 -0
- holonomy_lib/simplicial/__init__.py +55 -0
- holonomy_lib/simplicial/_face_lookup.py +99 -0
- holonomy_lib/simplicial/complex.py +168 -0
- holonomy_lib/simplicial/sparse_complex.py +145 -0
- holonomy_lib/simplicial/vietoris_rips.py +277 -0
- holonomy_lib/spectral/__init__.py +55 -0
- holonomy_lib/spectral/diffusion_map.py +169 -0
- holonomy_lib/spectral/effective_resistance.py +199 -0
- holonomy_lib/spectral/embedding.py +131 -0
- holonomy_lib/spectral/heat_kernel.py +236 -0
- holonomy_lib/spectral/laplacian.py +441 -0
- holonomy_lib/spectral/magnetic.py +351 -0
- holonomy_lib/spectral/spectral_dimension.py +104 -0
- holonomy_lib/tensor_calculus/__init__.py +23 -0
- holonomy_lib/tensor_calculus/decomposition.py +208 -0
- holonomy_lib/topology/__init__.py +28 -0
- holonomy_lib/topology/_filtration.py +147 -0
- holonomy_lib/topology/_reduction.py +227 -0
- holonomy_lib/topology/hodge.py +250 -0
- holonomy_lib/topology/persistent_homology.py +301 -0
- holonomy_lib-0.8.0.dist-info/METADATA +161 -0
- holonomy_lib-0.8.0.dist-info/RECORD +82 -0
- holonomy_lib-0.8.0.dist-info/WHEEL +5 -0
- holonomy_lib-0.8.0.dist-info/entry_points.txt +2 -0
- holonomy_lib-0.8.0.dist-info/licenses/LICENSE.md +105 -0
- holonomy_lib-0.8.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# SPDX-License-Identifier: FSL-1.1-ALv2
|
|
2
|
+
# Copyright 2026 Synoros
|
|
3
|
+
|
|
4
|
+
"""Persistent homology of Vietoris-Rips filtrations.
|
|
5
|
+
|
|
6
|
+
For a point cloud `X`, the Vietoris-Rips filtration is the family of
|
|
7
|
+
simplicial complexes `{VR(X, r) : r ≥ 0}` indexed by scale `r`. As
|
|
8
|
+
`r` grows, simplices are born; the persistent-homology pipeline
|
|
9
|
+
tracks which topological features (connected components, loops,
|
|
10
|
+
voids, ...) survive across scales.
|
|
11
|
+
|
|
12
|
+
This module exports a single public function
|
|
13
|
+
|
|
14
|
+
persistence_diagrams(points_or_distances, max_dim=2, max_radius=inf,
|
|
15
|
+
input_is_distance=False, reduction_backend="python")
|
|
16
|
+
→ (diagrams, masks)
|
|
17
|
+
|
|
18
|
+
where `diagrams[k]` is a `(B, max_pairs_k, 2)` batched padded tensor
|
|
19
|
+
of `(birth, death)` pairs for the k-th persistence diagram, and
|
|
20
|
+
`masks[k]` is the matching `(B, max_pairs_k)` validity mask. Bars
|
|
21
|
+
that survive to the end of the filtration have `death = +inf`. With a finite
|
|
22
|
+
`max_radius` the filtration ends at that radius, so bars still alive there
|
|
23
|
+
get `death = +inf` as well.
|
|
24
|
+
|
|
25
|
+
Two computational paths:
|
|
26
|
+
|
|
27
|
+
H₀: sequential host union-find on sorted filtration edges, dispatched
|
|
28
|
+
per batch member. All components are born at zero; each merge records
|
|
29
|
+
one death and every remaining component gets a `(0, +inf)` bar.
|
|
30
|
+
|
|
31
|
+
H_{1..max_dim}: Z/2 left-to-right reduction of the boundary
|
|
32
|
+
matrix in filtration order (Edelsbrunner-Letscher-Zomorodian
|
|
33
|
+
2002), via `_reduction.reduce_filtration`. Sequential within one
|
|
34
|
+
filtration. Simplex enumeration is batched across point clouds, but the
|
|
35
|
+
public function runs one reduction per batch element.
|
|
36
|
+
|
|
37
|
+
References:
|
|
38
|
+
Edelsbrunner, H., Letscher, D., Zomorodian, A. (2002). Topological
|
|
39
|
+
persistence and simplification. Discrete & Computational
|
|
40
|
+
Geometry 28:511-533. Foundational PH paper.
|
|
41
|
+
Cohen-Steiner, D., Edelsbrunner, H., Harer, J. (2007). Stability
|
|
42
|
+
of persistence diagrams. Discrete & Computational Geometry
|
|
43
|
+
37(1):103-120. Stability under perturbation of input data.
|
|
44
|
+
Edelsbrunner, H., Harer, J. (2010). Computational Topology: An
|
|
45
|
+
Introduction. AMS. Reference textbook.
|
|
46
|
+
Bauer, U. (2021). Ripser: efficient computation of Vietoris-Rips
|
|
47
|
+
persistence barcodes. J. Appl. Comput. Topology 5:391-423.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from __future__ import annotations
|
|
51
|
+
|
|
52
|
+
from typing import Literal
|
|
53
|
+
|
|
54
|
+
import torch
|
|
55
|
+
|
|
56
|
+
from holonomy_lib.simplicial import (
|
|
57
|
+
SparseSimplicialComplex,
|
|
58
|
+
pairwise_distances,
|
|
59
|
+
vietoris_rips_dense,
|
|
60
|
+
)
|
|
61
|
+
from holonomy_lib.topology._filtration import build_filtration
|
|
62
|
+
from holonomy_lib.topology._reduction import reduce_filtration
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def persistence_diagrams(
|
|
66
|
+
points_or_distances: torch.Tensor,
|
|
67
|
+
max_dim: int = 2,
|
|
68
|
+
max_radius: float = float("inf"),
|
|
69
|
+
input_is_distance: bool = False,
|
|
70
|
+
reduction_backend: Literal["python", "torch"] = "python",
|
|
71
|
+
) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
|
|
72
|
+
"""Persistence diagrams for dims 0..max_dim of a Vietoris-Rips filtration.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
points_or_distances: either `(B, n, d)` point clouds or
|
|
76
|
+
`(B, n, n)` distance matrices. Set `input_is_distance=True`
|
|
77
|
+
for the latter.
|
|
78
|
+
max_dim: highest dim to compute. Common choices: 1 (clusters +
|
|
79
|
+
loops) or 2 (clusters + loops + voids).
|
|
80
|
+
max_radius: build the filtration only up to this scale. Bars
|
|
81
|
+
born after this radius are not recorded, and bars still alive at
|
|
82
|
+
this radius get `death = +inf`. Default `+inf` builds the full
|
|
83
|
+
filtration.
|
|
84
|
+
input_is_distance: if True, `points_or_distances` is read as a
|
|
85
|
+
distance matrix directly; otherwise pairwise Euclidean
|
|
86
|
+
distances are computed.
|
|
87
|
+
reduction_backend: `"python"` (default; CPython set columns)
|
|
88
|
+
or `"torch"` (LongTensor columns on the filtration's device).
|
|
89
|
+
See `_reduction.reduce_filtration` for the tradeoff.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
`(diagrams, masks)` where:
|
|
93
|
+
diagrams[k]: `(B, max_pairs_k, 2)` float Tensor of
|
|
94
|
+
`(birth, death)` pairs. Bars surviving to infinity have
|
|
95
|
+
`death = +inf`.
|
|
96
|
+
masks[k]: `(B, max_pairs_k)` bool Tensor; True for real
|
|
97
|
+
pairs, False for padding.
|
|
98
|
+
|
|
99
|
+
Both lists have length `max_dim + 1`. H_0 retains one bar per input
|
|
100
|
+
vertex, including zero-length bars when coincident vertices merge.
|
|
101
|
+
Higher-dimensional diagrams omit zero-length bars.
|
|
102
|
+
|
|
103
|
+
Notes:
|
|
104
|
+
Cost is dominated by the matrix reduction for `max_dim ≥ 1`:
|
|
105
|
+
O(B · m³) worst case where `m` is the number of simplices in
|
|
106
|
+
the filtration, but typically much faster due to sparsity. For
|
|
107
|
+
large point clouds, lower `max_radius` or `max_dim` to keep
|
|
108
|
+
the simplex count manageable.
|
|
109
|
+
|
|
110
|
+
Simplex construction is batched. Z/2 pivot reduction and H_0 union-find
|
|
111
|
+
are sequential within each filtration: every pivot or component merge
|
|
112
|
+
depends on previous ones. Ragged filtrations are dispatched separately.
|
|
113
|
+
Python reduction is an explicit host boundary; torch reduction keeps
|
|
114
|
+
columns on device but synchronizes for dependent pivot decisions. Bar
|
|
115
|
+
coordinates are returned without an autograd connection to the inputs.
|
|
116
|
+
|
|
117
|
+
Comparison vs `ripser`: tie-breaking on equal birth/death
|
|
118
|
+
values may differ; the persistence diagrams agree under
|
|
119
|
+
bottleneck distance.
|
|
120
|
+
|
|
121
|
+
References:
|
|
122
|
+
Edelsbrunner-Letscher-Zomorodian (2002).
|
|
123
|
+
Cohen-Steiner-Edelsbrunner-Harer (2007) stability.
|
|
124
|
+
"""
|
|
125
|
+
if points_or_distances.ndim != 3:
|
|
126
|
+
raise ValueError(
|
|
127
|
+
f"points_or_distances must be 3-D (B, ...); got "
|
|
128
|
+
f"{tuple(points_or_distances.shape)}"
|
|
129
|
+
)
|
|
130
|
+
if not points_or_distances.is_floating_point():
|
|
131
|
+
raise ValueError("points_or_distances must have real floating dtype")
|
|
132
|
+
if max_dim < 0:
|
|
133
|
+
raise ValueError(f"max_dim must be >= 0, got {max_dim}")
|
|
134
|
+
if max_radius < 0:
|
|
135
|
+
raise ValueError(f"max_radius must be >= 0, got {max_radius}")
|
|
136
|
+
if reduction_backend not in ("python", "torch"):
|
|
137
|
+
raise ValueError(f"unknown reduction_backend={reduction_backend!r}")
|
|
138
|
+
B = points_or_distances.shape[0]
|
|
139
|
+
|
|
140
|
+
if input_is_distance:
|
|
141
|
+
if points_or_distances.shape[-1] != points_or_distances.shape[-2]:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
f"input_is_distance=True requires (B, n, n); got "
|
|
144
|
+
f"{tuple(points_or_distances.shape)}"
|
|
145
|
+
)
|
|
146
|
+
distances = points_or_distances
|
|
147
|
+
else:
|
|
148
|
+
distances = pairwise_distances(points_or_distances)
|
|
149
|
+
|
|
150
|
+
# pairs_per_batch_per_dim[b][k] collects the (birth, death) pairs of batch
|
|
151
|
+
# member b in dimension k.
|
|
152
|
+
pairs_per_batch_per_dim: list[list[list[tuple[float, float]]]] = [
|
|
153
|
+
[[] for _ in range(max_dim + 1)] for _ in range(B)
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
# All batch members share one tensorized simplex enumeration. The reduction
|
|
157
|
+
# is sequential, so it runs once per member on that member's filtration.
|
|
158
|
+
complexes = vietoris_rips_dense(
|
|
159
|
+
distances, max_radius=max_radius, max_dim=max_dim + 1,
|
|
160
|
+
) if max_dim >= 1 else None
|
|
161
|
+
for b in range(B):
|
|
162
|
+
d_b = distances[b]
|
|
163
|
+
|
|
164
|
+
# H_0: union-find on sorted filtration edges.
|
|
165
|
+
h0_pairs = _persistent_h0(d_b, max_radius)
|
|
166
|
+
pairs_per_batch_per_dim[b][0] = h0_pairs
|
|
167
|
+
|
|
168
|
+
# H_{1..max_dim}: build the VR complex and reduce its boundary matrix.
|
|
169
|
+
# With max_dim == 0 there is no complex, so only union-find runs.
|
|
170
|
+
# Otherwise the reduction runs on whatever simplices exist, and a
|
|
171
|
+
# dimension with none adds no bars, so small clouds need no extra guard.
|
|
172
|
+
if complexes is not None:
|
|
173
|
+
complex = SparseSimplicialComplex(
|
|
174
|
+
{k: table[b][complexes.valid_mask[k][b]]
|
|
175
|
+
for k, table in complexes.simplices_by_dim.items()},
|
|
176
|
+
n_vertices=complexes.n_vertices, device=complexes.device,
|
|
177
|
+
)
|
|
178
|
+
filt = build_filtration(d_b, complex)
|
|
179
|
+
pairs_by_dim = reduce_filtration(filt, backend=reduction_backend)
|
|
180
|
+
# H_0 already came from union-find, so only dims 1..max_dim are
|
|
181
|
+
# taken from this reduction.
|
|
182
|
+
for k in range(1, max_dim + 1):
|
|
183
|
+
pairs_per_batch_per_dim[b][k] = pairs_by_dim.get(k, [])
|
|
184
|
+
|
|
185
|
+
# Pad per-dim into batched tensors.
|
|
186
|
+
diagrams: list[torch.Tensor] = []
|
|
187
|
+
masks: list[torch.Tensor] = []
|
|
188
|
+
for k in range(max_dim + 1):
|
|
189
|
+
max_pairs_k = max(
|
|
190
|
+
len(pairs_per_batch_per_dim[b][k]) for b in range(B)
|
|
191
|
+
) if B > 0 else 0
|
|
192
|
+
# No member has a bar in this dimension. Return an empty (B, 0, 2)
|
|
193
|
+
# tensor so callers can index every dimension the same way.
|
|
194
|
+
if max_pairs_k == 0:
|
|
195
|
+
diagrams.append(
|
|
196
|
+
torch.zeros(B, 0, 2, dtype=distances.dtype,
|
|
197
|
+
device=distances.device)
|
|
198
|
+
)
|
|
199
|
+
masks.append(
|
|
200
|
+
torch.zeros(B, 0, dtype=torch.bool, device=distances.device)
|
|
201
|
+
)
|
|
202
|
+
continue
|
|
203
|
+
diag = torch.zeros(
|
|
204
|
+
B, max_pairs_k, 2,
|
|
205
|
+
dtype=distances.dtype, device=distances.device,
|
|
206
|
+
)
|
|
207
|
+
# counts[b] is how many real pairs member b has in this dimension.
|
|
208
|
+
counts = [len(member[k]) for member in pairs_per_batch_per_dim]
|
|
209
|
+
# One host-to-device copy per dimension. Pairs are flattened in batch
|
|
210
|
+
# order, so each (birth, death) stays whole.
|
|
211
|
+
values = torch.tensor(
|
|
212
|
+
[pair for member in pairs_per_batch_per_dim for pair in member[k]],
|
|
213
|
+
dtype=distances.dtype, device=distances.device,
|
|
214
|
+
)
|
|
215
|
+
count_tensor = torch.tensor(counts, device=distances.device)
|
|
216
|
+
# Row b's first counts[b] slots are real and the rest are padding.
|
|
217
|
+
positions = torch.arange(max_pairs_k, device=distances.device)
|
|
218
|
+
mask = positions < count_tensor[:, None]
|
|
219
|
+
# Boolean-mask assignment fills the True slots row by row, which matches
|
|
220
|
+
# the batch-order flattening of `values`.
|
|
221
|
+
diag[mask] = values
|
|
222
|
+
diagrams.append(diag)
|
|
223
|
+
masks.append(mask)
|
|
224
|
+
|
|
225
|
+
return diagrams, masks
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# ============================================================
|
|
229
|
+
# H_0 via batched union-find on sorted filtration edges
|
|
230
|
+
# ============================================================
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _persistent_h0(
|
|
234
|
+
distances: torch.Tensor, max_radius: float,
|
|
235
|
+
) -> list[tuple[float, float]]:
|
|
236
|
+
"""Persistent H_0 for a single distance matrix.
|
|
237
|
+
|
|
238
|
+
Takes an `(n, n)` distance matrix and the radius cap. Returns `n`
|
|
239
|
+
`(birth, death)` pairs, one per vertex. Every component is born at 0.
|
|
240
|
+
Each union-find merge kills one component at that edge length. A graph
|
|
241
|
+
that is connected at the cap leaves one component alive to infinity, and
|
|
242
|
+
a disconnected one leaves one essential bar per component. Edges are
|
|
243
|
+
copied to the host in one bulk transfer. Union-find runs sequentially
|
|
244
|
+
because each merge changes the partition that later edges see.
|
|
245
|
+
"""
|
|
246
|
+
n = distances.shape[0]
|
|
247
|
+
if n == 0:
|
|
248
|
+
return []
|
|
249
|
+
if n == 1:
|
|
250
|
+
# Single vertex: one infinite bar.
|
|
251
|
+
return [(0.0, float("inf"))]
|
|
252
|
+
|
|
253
|
+
# Strictly upper-triangular edges (i, j) with i < j and d <= max_radius.
|
|
254
|
+
iu, ju = torch.triu_indices(n, n, offset=1, device=distances.device)
|
|
255
|
+
edge_lengths = distances[iu, ju]
|
|
256
|
+
if max_radius != float("inf"):
|
|
257
|
+
keep = edge_lengths <= max_radius
|
|
258
|
+
iu, ju, edge_lengths = iu[keep], ju[keep], edge_lengths[keep]
|
|
259
|
+
|
|
260
|
+
# Sort edges by length. The stable sort breaks ties by upper-triangle
|
|
261
|
+
# order, so the barcode is the same on every run.
|
|
262
|
+
sort_idx = torch.argsort(edge_lengths, stable=True)
|
|
263
|
+
sorted_u = iu[sort_idx].tolist()
|
|
264
|
+
sorted_v = ju[sort_idx].tolist()
|
|
265
|
+
sorted_lengths = edge_lengths[sort_idx].tolist()
|
|
266
|
+
|
|
267
|
+
# Disjoint-set union with path compression + union-by-rank.
|
|
268
|
+
parent = list(range(n))
|
|
269
|
+
rank = [0] * n
|
|
270
|
+
|
|
271
|
+
def find(x: int) -> int:
|
|
272
|
+
while parent[x] != x:
|
|
273
|
+
parent[x] = parent[parent[x]] # path compression
|
|
274
|
+
x = parent[x]
|
|
275
|
+
return x
|
|
276
|
+
|
|
277
|
+
pairs: list[tuple[float, float]] = []
|
|
278
|
+
for u, v, length in zip(sorted_u, sorted_v, sorted_lengths, strict=True):
|
|
279
|
+
ru, rv = find(u), find(v)
|
|
280
|
+
if ru == rv:
|
|
281
|
+
continue
|
|
282
|
+
# All vertices are born at zero, so either representative gives
|
|
283
|
+
# the same barcode. Rank only keeps the parent trees shallow.
|
|
284
|
+
if rank[ru] < rank[rv]:
|
|
285
|
+
parent[ru] = rv
|
|
286
|
+
elif rank[ru] > rank[rv]:
|
|
287
|
+
parent[rv] = ru
|
|
288
|
+
else:
|
|
289
|
+
parent[rv] = ru
|
|
290
|
+
rank[ru] += 1
|
|
291
|
+
# A successful merge kills one zero-born connected component.
|
|
292
|
+
pairs.append((0.0, length))
|
|
293
|
+
|
|
294
|
+
# Components that never merged survive to infinity. For a graph connected
|
|
295
|
+
# at the cap, that is one component.
|
|
296
|
+
n_merged = len(pairs)
|
|
297
|
+
n_surviving = n - n_merged
|
|
298
|
+
for _ in range(n_surviving):
|
|
299
|
+
pairs.append((0.0, float("inf")))
|
|
300
|
+
|
|
301
|
+
return pairs
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: holonomy-lib
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Batched PyTorch primitives for Riemannian geometry, spectral graph theory, discrete curvature, topology, and tensor decompositions.
|
|
5
|
+
Author: Synoros
|
|
6
|
+
License-Expression: FSL-1.1-ALv2
|
|
7
|
+
Project-URL: Homepage, https://github.com/Synoros-io/holonomy_lib
|
|
8
|
+
Project-URL: Repository, https://github.com/Synoros-io/holonomy_lib
|
|
9
|
+
Project-URL: Issues, https://github.com/Synoros-io/holonomy_lib/issues
|
|
10
|
+
Keywords: pytorch,differential-geometry,riemannian-manifolds,hyperbolic-geometry,riemannian-optimization,spectral-graph-theory,graph-laplacian,magnetic-laplacian,heat-kernel,diffusion-maps,effective-resistance,ricci-curvature,ricci-flow,cellular-sheaves,simplicial-complex,hodge-laplacian,persistent-homology,topological-data-analysis,information-geometry,tensor-decomposition,numerical-linear-algebra
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE.md
|
|
23
|
+
Requires-Dist: torch>=2.2
|
|
24
|
+
Requires-Dist: numpy>=1.26
|
|
25
|
+
Requires-Dist: scipy>=1.11.2
|
|
26
|
+
Provides-Extra: comparison
|
|
27
|
+
Requires-Dist: pymanopt; extra == "comparison"
|
|
28
|
+
Requires-Dist: geoopt; extra == "comparison"
|
|
29
|
+
Requires-Dist: geomstats; extra == "comparison"
|
|
30
|
+
Requires-Dist: autograd; extra == "comparison"
|
|
31
|
+
Requires-Dist: tensorly; extra == "comparison"
|
|
32
|
+
Requires-Dist: networkx; extra == "comparison"
|
|
33
|
+
Requires-Dist: gudhi; extra == "comparison"
|
|
34
|
+
Requires-Dist: ripser; extra == "comparison"
|
|
35
|
+
Requires-Dist: GraphRicciCurvature; extra == "comparison"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy; extra == "dev"
|
|
40
|
+
Requires-Dist: scipy-stubs; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# holonomy_lib
|
|
44
|
+
|
|
45
|
+
Geometry, graph and topology primitives for PyTorch. Most functions take a batch
|
|
46
|
+
of inputs (a leading batch dimension) and run on the device and dtype you pass in.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
Python 3.12 or later. Install PyTorch for your hardware first, using the
|
|
51
|
+
[PyTorch selector](https://pytorch.org/get-started/locally/), then:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install holonomy-lib
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
Effective resistance between the ends of a three-node path and of a triangle,
|
|
60
|
+
computed as one batch:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import torch
|
|
64
|
+
from holonomy_lib.spectral import effective_resistance
|
|
65
|
+
|
|
66
|
+
device = "cuda" if torch.cuda.is_available() else "cpu" # ROCm builds also use "cuda"
|
|
67
|
+
adjacency = torch.tensor([
|
|
68
|
+
[[0, 1, 0], [1, 0, 1], [0, 1, 0]], # path 0-1-2
|
|
69
|
+
[[0, 1, 1], [1, 0, 1], [1, 1, 0]], # triangle
|
|
70
|
+
], dtype=torch.float64, device=device)
|
|
71
|
+
|
|
72
|
+
R = effective_resistance(adjacency)
|
|
73
|
+
print(R.shape) # torch.Size([2, 3, 3])
|
|
74
|
+
print(R[:, 0, 2]) # tensor([2.0000, 0.6667], dtype=torch.float64)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## What's in it
|
|
78
|
+
|
|
79
|
+
[CONTENTS.md](https://github.com/Synoros-io/holonomy_lib/blob/master/CONTENTS.md) lists every function with its signature and the
|
|
80
|
+
paper it implements.
|
|
81
|
+
|
|
82
|
+
| Subpackage | Contents |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `manifolds` | Fixed-rank, SPD, Lorentz, Lorentzian, stereographic and product manifolds; model-space volumes. |
|
|
85
|
+
| `optimization` | Riemannian SGD. |
|
|
86
|
+
| `hyperbolic` | Fréchet means, manifold-aware inner products, hyperbolic heat kernels. |
|
|
87
|
+
| `lie` | SO(3) exponential and logarithm, rotations, real spherical harmonics. |
|
|
88
|
+
| `info_geometry` | Bregman and KL divergences, Fisher information, natural gradients. |
|
|
89
|
+
| `tensor_calculus` | Mode unfolding, mode products, HOSVD. |
|
|
90
|
+
| `algebra` | Truncated SVD, dense and sparse eigensolvers, conjugate gradient, LSQR. |
|
|
91
|
+
| `convex` | Regularized least squares. |
|
|
92
|
+
| `spectral` | Graph Laplacians, embeddings, heat diffusion, effective resistance, spectral dimension. |
|
|
93
|
+
| `discrete_geometry` | Ollivier and Forman curvature, Ricci flow with surgery. |
|
|
94
|
+
| `sheaf` | Graph sheaves, coboundaries, sheaf Laplacians and their spectra. |
|
|
95
|
+
| `simplicial` | Dense and sparse simplicial complexes, boundary operators, Vietoris-Rips. |
|
|
96
|
+
| `topology` | Hodge Laplacians, Betti numbers, persistent homology. |
|
|
97
|
+
|
|
98
|
+
## Explorer
|
|
99
|
+
|
|
100
|
+
`holonomy-explorer` opens a local page that runs the library live: curvature
|
|
101
|
+
and diffusion on graphs, geodesics as curvature passes through zero,
|
|
102
|
+
persistent homology of point clouds, and batch-size timings on your hardware.
|
|
103
|
+
Options: `--device`, `--dtype`, `--port`, `--no-browser`.
|
|
104
|
+
|
|
105
|
+

|
|
106
|
+
|
|
107
|
+
## Experimental
|
|
108
|
+
|
|
109
|
+
`holonomy_lib.experimental` holds two unfinished pieces that are not part of
|
|
110
|
+
the stable API:
|
|
111
|
+
|
|
112
|
+
- `hyperbolic_laplacian_eigenmaps`: nothing in its objective keeps nodes apart,
|
|
113
|
+
so an embedding can collapse to a point.
|
|
114
|
+
- `HeterogeneousKappaManifold`: a curvature per point. Its `dissimilarity` is
|
|
115
|
+
not a metric.
|
|
116
|
+
|
|
117
|
+
## Limits
|
|
118
|
+
|
|
119
|
+
- Use `float64` when accuracy matters.
|
|
120
|
+
- Sparse eigensolvers, sparse simplicial complexes and sheaves handle one
|
|
121
|
+
instance at a time. Sparse eigensolvers are not differentiable.
|
|
122
|
+
- Persistent homology runs its reduction one batch member at a time on the CPU.
|
|
123
|
+
- Ollivier curvature uses entropy-regularized transport, so it is close to the
|
|
124
|
+
exact value rather than equal to it, and it warns if the solver did not
|
|
125
|
+
converge. Edge weights act as both lengths and random-walk weights.
|
|
126
|
+
- Gradients through `cg` can be wrong when it converges in very few iterations.
|
|
127
|
+
- Effective resistance and commute time are `inf` between disconnected components.
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
uv sync --extra dev
|
|
133
|
+
python -m pytest -q
|
|
134
|
+
python -m ruff check src/ tests/
|
|
135
|
+
python -m mypy src/
|
|
136
|
+
python tools/audit.py src/holonomy_lib --strict
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
[CONVENTIONS.md](https://github.com/Synoros-io/holonomy_lib/blob/master/CONVENTIONS.md) has the coding and numerical rules;
|
|
140
|
+
[CHANGELOG.md](https://github.com/Synoros-io/holonomy_lib/blob/master/CHANGELOG.md) has the release history.
|
|
141
|
+
|
|
142
|
+
## Citation
|
|
143
|
+
|
|
144
|
+
```bibtex
|
|
145
|
+
@software{holonomy_lib,
|
|
146
|
+
author = {John Vaught},
|
|
147
|
+
title = {holonomy\_lib: Batched PyTorch primitives for geometry and topology},
|
|
148
|
+
version = {0.8.0},
|
|
149
|
+
year = {2026},
|
|
150
|
+
url = {https://github.com/Synoros-io/holonomy_lib},
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Please also cite the papers named in the docstrings of the functions you use.
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
[FSL-1.1-ALv2](https://github.com/Synoros-io/holonomy_lib/blob/master/LICENSE.md). You can use and modify it freely; competing
|
|
159
|
+
commercial use is reserved to Synoros. Each release becomes Apache-2.0 two
|
|
160
|
+
years after it ships. Releases up to 0.7.0 remain AGPL-3.0-or-later. For other
|
|
161
|
+
terms, contact [contact@synoros.io](mailto:contact@synoros.io).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
holonomy_explorer/__init__.py,sha256=TbPP-kKgdgN9bxB_XvZOmHg13uDwgdJw33PRD5FuEeU,70
|
|
2
|
+
holonomy_explorer/__main__.py,sha256=J8vfIKNMtZ9CyTFEJpcCOQnOUyN3nYZ6DyxaiFT2IDM,2759
|
|
3
|
+
holonomy_explorer/batching.py,sha256=k8jCkk6YBHxzuAmnuQRAECTTPM1pCqV18QonrHVUZMw,6720
|
|
4
|
+
holonomy_explorer/common.py,sha256=MnyVIinZkxvPFjA9bzMrdFsRqZctFhV4jxLqcjK807U,6749
|
|
5
|
+
holonomy_explorer/curvature.py,sha256=PojOi0VGM_XTde8KrAxygvmOFoXpQIc8CEgUhgkSGHw,5837
|
|
6
|
+
holonomy_explorer/datasets.py,sha256=E8ELvVSrTLKtwtgt5WSycajH1iXAJ_tF8Mu4gt1lT7c,10152
|
|
7
|
+
holonomy_explorer/graphs.py,sha256=W8ubZde8XMOzocRcWnVcgSghyMFuhTchJB3EKUUQ3mM,11740
|
|
8
|
+
holonomy_explorer/server.py,sha256=es-4SkL2WLPWV-v0KBn-lVkaN7dk3K3-67zc-789sEY,7687
|
|
9
|
+
holonomy_explorer/topology.py,sha256=6c0Avw4dpqwXgivugCDw0b7V_rYv4DaVSCOqOo_H22w,4769
|
|
10
|
+
holonomy_explorer/static/app.js,sha256=IZABPlg9iPjvFBbATyrKzycsO_aLO2V66zrUYR3VWOI,31163
|
|
11
|
+
holonomy_explorer/static/charts.js,sha256=_kq6QJjf19qRBrJGPF9zRXcr_9zjptoziQaBglOH0T4,6711
|
|
12
|
+
holonomy_explorer/static/index.html,sha256=igEQMy8IDD9-6_nDNSNvYwwtiNTOB8imz34G84tnPRo,2571
|
|
13
|
+
holonomy_explorer/static/layout.js,sha256=8lGLY3kO8gDPXHvS1veEbW3lki0FPkpGV6XyIEM-_sI,4689
|
|
14
|
+
holonomy_explorer/static/style.css,sha256=52OPsb7buVqhHVNiVKxE94rmaMO1R-0ykQVn3wWWnyU,10208
|
|
15
|
+
holonomy_explorer/static/view.js,sha256=FZA6WGFq8FkGMzoGxJBQsHq8s0gvwnYP2QBo-Jcuqw8,10630
|
|
16
|
+
holonomy_lib/__init__.py,sha256=_J5vdotTpz2rDD8pdM9MlVxjiTEE1ov9RUcEpY3z0d4,327
|
|
17
|
+
holonomy_lib/_graph_utils.py,sha256=wTkJZeAEQJ_Q1f9SDgRxi_TtPw4ZpoHyddGIjT5CFo4,1899
|
|
18
|
+
holonomy_lib/_quadrature.py,sha256=nrh6oAS8hItOUPEjHjJ3jN9nfvMhZEQIbUJkH0myaPY,2820
|
|
19
|
+
holonomy_lib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
holonomy_lib/algebra/__init__.py,sha256=1uzdf2qHyq-r7WZ80xMTFS2FjwQf9dBu7CZFfTlGiFA,1516
|
|
21
|
+
holonomy_lib/algebra/eigsh_sparse.py,sha256=0t-GcNel1rL_wb60o8-G61zBooXoNeBNE2TRpo1URYE,10737
|
|
22
|
+
holonomy_lib/algebra/lanczos.py,sha256=v-iQlTDdahoeY1LifzIk8W1U46XV-NxDVwMOABaWLms,17002
|
|
23
|
+
holonomy_lib/algebra/linear.py,sha256=SV9Tb6F6dhE9pout4FDIDAbeHVxMCnjt68v4e9llvkk,7555
|
|
24
|
+
holonomy_lib/algebra/solvers.py,sha256=1o9d_yRVVEMdLtQJsOYNMWXvNMxt4mZJFGCU76vuw08,24104
|
|
25
|
+
holonomy_lib/convex/__init__.py,sha256=-rRTZPEGDVCvMm1S5-dVrXcJ_3lU_6hR7ioCaHmX5co,1138
|
|
26
|
+
holonomy_lib/convex/least_squares.py,sha256=DkxmDqJNbegrOfyB45JDfJXU0MqrvziFVVE9g_bBvIo,7609
|
|
27
|
+
holonomy_lib/discrete_geometry/__init__.py,sha256=6ZaTTMbdSKDALMdYawUsLs7bnt-M-o62gfO3fqutOTU,1277
|
|
28
|
+
holonomy_lib/discrete_geometry/forman.py,sha256=O8A_0amiRlnQ3gt1FygEz40GszgnKAkm8rsNpgM7bWA,10567
|
|
29
|
+
holonomy_lib/discrete_geometry/ricci.py,sha256=BhJmyACa0M7Aqcj97GYTRdgmyb_rwL7ySRTp1bXYXGQ,39850
|
|
30
|
+
holonomy_lib/experimental/__init__.py,sha256=p8WqCl2OWfRHt9iVvHZm0HN2eQn1sXl7dp7LXM-i1DQ,477
|
|
31
|
+
holonomy_lib/experimental/heterogeneous_kappa.py,sha256=6-QHmB4rNCALuL2MxYzZ2oLqua-hkh9Qo_uVCsuStQ4,15282
|
|
32
|
+
holonomy_lib/experimental/laplacian_eigenmaps.py,sha256=fGqmoqMIbeJuZndS62r2wPnN5n8bfUZWS9d615n2ghk,8733
|
|
33
|
+
holonomy_lib/hyperbolic/__init__.py,sha256=0g8V___z89RsjwLbXda4gkZ1_ko8WOXQk7ffRQbR5j0,944
|
|
34
|
+
holonomy_lib/hyperbolic/frechet_mean.py,sha256=qw1C0KcSUNLesFLnUu-zoHtTyfj4O28WM5KwFy-kFQA,19281
|
|
35
|
+
holonomy_lib/hyperbolic/heat_kernel.py,sha256=RWmQpOqIxcNmn1R9DN-3X-UKGLLtb2-2KvQG_Lo1EhA,25248
|
|
36
|
+
holonomy_lib/hyperbolic/manifold_inner.py,sha256=9ax5jeu_NaLNvyyGMhvcPQBAwl0XvJ3W7AR_lwQa8WY,3301
|
|
37
|
+
holonomy_lib/info_geometry/__init__.py,sha256=T0d6Ie8d7aUlfuCMtmzflDd4DLRKdOkbzF4Jb_uLO64,2533
|
|
38
|
+
holonomy_lib/info_geometry/divergences.py,sha256=fE3Xc6DBfFQVnbIojTTw97i2xY_TSGENVbdgWD4CElA,8577
|
|
39
|
+
holonomy_lib/info_geometry/fisher.py,sha256=qOgmQnmKs8ldMBOalCQddgJkBm-9BS6RINZqu6U-81U,7067
|
|
40
|
+
holonomy_lib/lie/__init__.py,sha256=Vw2iSSqMFgjZeURD1isMW1LjlZ4uLaVwQ70ENrRstLU,2409
|
|
41
|
+
holonomy_lib/lie/so3.py,sha256=CmA1ULGgMiXyPGOaMbGNfFwYqlNgsiiHPzxC29ebmns,15503
|
|
42
|
+
holonomy_lib/lie/spherical_harmonics.py,sha256=9Ggx8OHFUgLW3IgiY6dojmhCJqTL4AAvxOyHfVsV-Ew,7299
|
|
43
|
+
holonomy_lib/manifolds/__init__.py,sha256=tCGwNUA-qlSCAxwSvVebUjAUpIFqNy8H7P7AGiAzUeY,2237
|
|
44
|
+
holonomy_lib/manifolds/comparison.py,sha256=VmvqxupDTYMxmPbgdhhcSvZCu69mV6FRwAiscYVrpMQ,15380
|
|
45
|
+
holonomy_lib/manifolds/fixed_rank.py,sha256=3tk3fhdbtlIzG04qef22QxYUi1NV1m7tlsfmHZGy0Y4,19485
|
|
46
|
+
holonomy_lib/manifolds/lorentz.py,sha256=lL7KP0B2MxVhPheLcEwFsK34aSoebw-OYLn9jcCXJhE,29004
|
|
47
|
+
holonomy_lib/manifolds/lorentzian.py,sha256=s4LNUUxefxJLsIHDcCHEcr9rvlzb7NojOOHevdrASwI,21944
|
|
48
|
+
holonomy_lib/manifolds/product.py,sha256=9xK3ailR5tKBgckW3SvMtFeA-ZZzfJVjhBnTCgR10Oc,16635
|
|
49
|
+
holonomy_lib/manifolds/spd.py,sha256=quOFTz_FVTjJt2TgRJ-p30Hxz03fIzotlsDSFAW5PGo,21834
|
|
50
|
+
holonomy_lib/manifolds/stereographic.py,sha256=XS1di52kWnl0Rp8IbeETUg0hSvlYnPQhLWrBL2_oBiU,31047
|
|
51
|
+
holonomy_lib/optimization/__init__.py,sha256=Km60vtVvlDrkHqanRysA0lkMDxwnsvvgG1IRsxA92oA,1639
|
|
52
|
+
holonomy_lib/optimization/base.py,sha256=-e2DUkvbIqZi3Hazz9m2Yc08CVOQSK5QJu4WScXUSXA,2434
|
|
53
|
+
holonomy_lib/optimization/sgd.py,sha256=hxuBGB_TCup_KW8axB9ADHiMzRzGIxPmCUUIaXN7Du4,4116
|
|
54
|
+
holonomy_lib/sheaf/__init__.py,sha256=hoHivoy_zqUOC0PuJnuzm1CPPcFxLwW1bZKvBvBN2Xw,3185
|
|
55
|
+
holonomy_lib/sheaf/graph_sheaf.py,sha256=FElURHbnqXZrFEJmmaA6C4HYXimGAFA0LMcDNWCULkU,15423
|
|
56
|
+
holonomy_lib/sheaf/laplacian.py,sha256=_18wabK8tfSBnBagquYNaUQeAjlWSrrDy1MfApyqSts,23144
|
|
57
|
+
holonomy_lib/simplicial/__init__.py,sha256=ImONeXhqF3jQjNwpJGJ79oW9dPIDA9iwO1FmpypEok0,1933
|
|
58
|
+
holonomy_lib/simplicial/_face_lookup.py,sha256=BIAPUeqJKgNTO6N4YF-4pBolodGmjz-IsrUbEWTcbC8,3801
|
|
59
|
+
holonomy_lib/simplicial/complex.py,sha256=RESjE1EslkYfqzGwl35uEJTK6K75MUSFVwgpdIdvvgY,6887
|
|
60
|
+
holonomy_lib/simplicial/sparse_complex.py,sha256=_F7V2Nt7xWs_f_rW906MsrCyiF_ouH2Ai6XSCqjVdro,5552
|
|
61
|
+
holonomy_lib/simplicial/vietoris_rips.py,sha256=PQjlukGWoOOyZ9sI6OLlLjKVvRwI5kJAqsPHvkFimas,12133
|
|
62
|
+
holonomy_lib/spectral/__init__.py,sha256=CyHxGWo3DB4VT4QpJHm4w-8GmJb95H_Ed6A16NwZy8c,2581
|
|
63
|
+
holonomy_lib/spectral/diffusion_map.py,sha256=Zdzr0Y5jTao5lJTaeAMEkivsros-P3UZl_qX4Rha6ic,7799
|
|
64
|
+
holonomy_lib/spectral/effective_resistance.py,sha256=vu9Tyzk5si-FiI7ORpzLDazhh9AtsIXe7JMjU5AsFH8,9202
|
|
65
|
+
holonomy_lib/spectral/embedding.py,sha256=zOqUMUZjsv2rZlFrxw7ZMMpFDOzOrBDozc1cKAxZTvk,5613
|
|
66
|
+
holonomy_lib/spectral/heat_kernel.py,sha256=d7V2-ovSpw3Na-DRIC7AaGDn7EhHFhhYQmiMB4o3vjY,10252
|
|
67
|
+
holonomy_lib/spectral/laplacian.py,sha256=0wD4r6Mp2CR6BDwY_PGKEFG9n7eQh_MdfiCrc5SSIpE,17776
|
|
68
|
+
holonomy_lib/spectral/magnetic.py,sha256=OSoH68ONc_0mOLKnRb-hk5axdBDMJv-IxCfCUM1-mCU,14360
|
|
69
|
+
holonomy_lib/spectral/spectral_dimension.py,sha256=Qf-hF_wqZ0QTZR-uHBHJIBVgtqJJfs6s8pxJdRoc3-E,4810
|
|
70
|
+
holonomy_lib/tensor_calculus/__init__.py,sha256=aa9Cr-Ye36PMUdRhCjaklbjPr1skdk8_K5YfI7n7ogc,654
|
|
71
|
+
holonomy_lib/tensor_calculus/decomposition.py,sha256=aX_Wglha1ZcrK-9fn6UXDJHdJ2OO6LPe9qoJseURTr8,7795
|
|
72
|
+
holonomy_lib/topology/__init__.py,sha256=_F6hKEBLSnRgfXB6qKq6E8T5GaFCqKZdunGrdiwVo6s,978
|
|
73
|
+
holonomy_lib/topology/_filtration.py,sha256=sYhq28chXVtnQQGuEWi4pJtlvFuvVZHSPSex2ejBYlo,5875
|
|
74
|
+
holonomy_lib/topology/_reduction.py,sha256=6MS6S4wHZIgQRAx6OVk0xu7MhDKFVl_xPO6bXyq-edI,10442
|
|
75
|
+
holonomy_lib/topology/hodge.py,sha256=sEuORngp21M4TyZiVz1hZmcaIvJFKUn7Y7pzHs7JthY,9714
|
|
76
|
+
holonomy_lib/topology/persistent_homology.py,sha256=J-pyKA-kUVmEvYlvBTgQQdc-NVSl9GGfjDbpAfVlgt8,12733
|
|
77
|
+
holonomy_lib-0.8.0.dist-info/licenses/LICENSE.md,sha256=-VpEBq5bW92sgS2cMsqa_s9oSKcPt5VXYQB0RLXQ0s4,3739
|
|
78
|
+
holonomy_lib-0.8.0.dist-info/METADATA,sha256=RTDbUKKihUFxAEkfwXMzYmuLFO7nvaxaB3FnOOEeDZc,6822
|
|
79
|
+
holonomy_lib-0.8.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
80
|
+
holonomy_lib-0.8.0.dist-info/entry_points.txt,sha256=DHAZ9YwjmV1pfrfQELNg7JnepSdBE63efm4z1owaa_4,70
|
|
81
|
+
holonomy_lib-0.8.0.dist-info/top_level.txt,sha256=8CFDkax9Y2qRF7jqMxpEzk53ODPnQdBmG1MuGQVj5c8,31
|
|
82
|
+
holonomy_lib-0.8.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-ALv2
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Synoros
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|