API Reference¶
Aggregation¶
groupoid.aggregation.TransportGroupoidAggregator
dataclass
¶
Federated aggregator using groupoid transport and Riemannian geometry.
Given a network of clients with local model parameters on a Riemannian manifold, this aggregator: - Uses parallel transport (groupoid morphisms) to align parameters - Checks cohomological consistency before aggregation - Computes intrinsic Karcher mean on the parameter manifold - Distributes the result back via inverse transport
Parameters¶
manifold
A geomstats manifold instance for the parameter space.
graph
Directed graph of the client network.
base_node
The node to which all parameters are transported for aggregation.
consistency_threshold
Maximum H^1 norm before raising a consistency warning.
track_divergence
When True, each :meth:aggregate round computes the persistent
homology of the transported client parameters and the bottleneck
distance to the previous round (H0 against H0), populating
FederatedRound.divergence.
Source code in groupoid/aggregation.py
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | |
aggregate(client_params, weights=None)
¶
Run one round of groupoid-aware federated aggregation.
Parameters¶
client_params Map from client node ID to local model parameters. weights Optional per-client weights for the Karcher mean.
Returns¶
FederatedRound The aggregation result including global params, consistency metrics, and per-client local updates.
Source code in groupoid/aggregation.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | |
check_consistency(client_params)
¶
Check cohomological consistency of current transport maps.
Returns the H^1 norm. A value near zero indicates that the local models can be consistently aggregated.
Source code in groupoid/aggregation.py
182 183 184 185 186 187 188 189 190 191 | |
register_transport(source, target, matrix)
¶
Register a transport map between two clients.
Source code in groupoid/aggregation.py
104 105 106 107 108 109 110 111 112 113 | |
register_transport_from_points(source, target, source_point, target_point, method='pole', n_rungs=2)
¶
Compute and register a transport map from two manifold points.
Builds the parallel-transport matrix along the geodesic from
source_point to target_point via
:func:groupoid.transport.compute_transport_matrix (pole or
Schild's ladder) and registers it for the (source, target)
edge. This wires the ladder approximations into the aggregation
pipeline for the case where per-client base points are known but
explicit transport matrices are not. The ladders are
approximations with a small residual; see LIMITATIONS.md for the
measured behavior.
Returns¶
np.ndarray The registered transport matrix.
Source code in groupoid/aggregation.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
groupoid.aggregation.FederatedRound
dataclass
¶
Result of a single federated aggregation round.
divergence is populated only when the aggregator was constructed
with track_divergence=True: it is the persistent-homology summary
of the transported client parameters, with the bottleneck distance to
the previous round's summary (None on the first round with tracking
off; see :func:groupoid.persistence.track_divergence).
Source code in groupoid/aggregation.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
Manifold Operations¶
groupoid.manifold.karcher_mean(manifold, points, weights=None, max_iter=100, tol=1e-06)
¶
Compute the Karcher (Frechet) mean on a Riemannian manifold.
Parameters¶
manifold : geomstats manifold A geomstats manifold instance with a metric. points : np.ndarray Array of shape (n_points, *point_shape) on the manifold. weights : np.ndarray or None Optional weights for the mean computation. max_iter : int Maximum iterations for gradient descent. tol : float Convergence tolerance.
Returns¶
np.ndarray The Karcher mean point on the manifold.
Source code in groupoid/manifold.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
Groupoid¶
groupoid.groupoid.Morphism
¶
Bases: BaseModel
A morphism in the transport groupoid.
Represents a transport map between two nodes (clients) in the federated learning network.
Source code in groupoid/groupoid.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
groupoid.groupoid.compose(f, g)
¶
Compose two morphisms f and g (f followed by g).
Requires f.target == g.source (category composition law).
Parameters¶
f : Morphism First morphism (applied first). g : Morphism Second morphism (applied second).
Returns¶
Morphism The composed morphism from f.source to g.target.
Raises¶
CompositionError If f.target != g.source.
Source code in groupoid/groupoid.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | |
groupoid.groupoid.inverse(f)
¶
Compute the inverse of a morphism.
Parameters¶
f : Morphism The morphism to invert.
Returns¶
Morphism The inverse morphism from f.target to f.source.
Source code in groupoid/groupoid.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
Cohomology¶
groupoid.cohomology.compute_h1(graph, transport_maps)
¶
Compute the H^1 cohomology norm of a transport cocycle.
Returns 0.0 (up to numerical tolerance) when the cocycle is a coboundary, meaning the local data is globally consistent.
The H^1 obstruction on a cycle is the deviation from the identity of
the holonomy, i.e. the ordered product of the restriction/transport
maps around the entire cycle. The cocycle must therefore be fully
specified: every edge of every basis cycle needs a transport map in
one direction or the other (the reverse direction is inverted). A
missing edge map leaves the holonomy undefined, so this function
raises :class:IncompleteCocycleError rather than silently forming a
partial product.
Parameters¶
graph : nx.DiGraph The groupoid nerve (directed graph of nodes/clients). transport_maps : dict Maps (source, target) edge tuples to transport matrices.
Returns¶
float The cohomology norm. Zero indicates global consistency.
Raises¶
IncompleteCocycleError If any edge on a basis cycle has no transport map in either direction, so the cycle's holonomy is undefined.
Source code in groupoid/cohomology.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
Sheaf¶
groupoid.sheaf.Sheaf
¶
A cellular sheaf on a graph.
Assigns vector spaces to nodes and linear restriction maps to edges.
Source code in groupoid/sheaf.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
get_restriction_map(source, target)
¶
Get the restriction map for an edge.
Source code in groupoid/sheaf.py
29 30 31 | |
get_section(node)
¶
Get the section value at a node.
Source code in groupoid/sheaf.py
37 38 39 | |
restrict(section, source, target)
¶
Apply the restriction map to a section.
Parameters¶
section : np.ndarray The section value at the source node. source : str Source node identifier. target : str Target node identifier.
Returns¶
np.ndarray The restricted section at the target node.
Source code in groupoid/sheaf.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
restrict_along_path(section, path)
¶
Restrict a section along a path of nodes.
Parameters¶
section : np.ndarray The section value at path[0]. path : list[str] Ordered list of nodes forming a path in the graph.
Returns¶
np.ndarray The section restricted to path[-1].
Source code in groupoid/sheaf.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
set_restriction_map(source, target, matrix)
¶
Set the restriction map for an edge.
Source code in groupoid/sheaf.py
22 23 24 25 26 27 | |
set_section(node, value)
¶
Set a section value at a node.
Source code in groupoid/sheaf.py
33 34 35 | |
Sheaf Laplacian¶
groupoid.laplacian.build_sheaf_laplacian(sheaf, stalk_dim)
¶
Build the sheaf Laplacian matrix.
For a sheaf F on graph G with n nodes and stalk dimension d, the sheaf Laplacian is a (nd) x (nd) block matrix defined as:
L_F = delta^T @ delta
where delta is the connection coboundary. For each edge (u, v) with restriction (transport) map R = R_{uv}: stalk(u) -> stalk(v), the coboundary acts as (delta x){(u,v)} = x_v - R{uv} x_u, so L = delta^T @ delta has blocks (summed over incident edges):
L[u,u] += R_{uv}^T @ R_{uv} (source diagonal)
L[v,v] += I (target diagonal)
L[u,v] += -R_{uv}^T (off-diagonal)
L[v,u] += -R_{uv} (off-diagonal)
L is symmetric positive semi-definite for ANY restriction maps (it is delta^T delta); its kernel is the space of transport-consistent global sections (x_v = R_{uv} x_u on every edge).
Parameters¶
sheaf A Sheaf instance with restriction maps set. stalk_dim Dimension of each stalk (vector space at each node).
Returns¶
np.ndarray The sheaf Laplacian matrix of shape (nd, nd).
Source code in groupoid/laplacian.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
groupoid.laplacian.spectral_analysis(sheaf, stalk_dim, tol=1e-10)
¶
Compute spectral decomposition of the sheaf Laplacian.
Parameters¶
sheaf A Sheaf instance with restriction maps. stalk_dim Dimension of each stalk. tol Tolerance for identifying zero eigenvalues.
Returns¶
SpectralSummary Full spectral summary including connectivity and consensus rate.
Source code in groupoid/laplacian.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
groupoid.laplacian.sheaf_diffusion_step(sheaf, sections, stalk_dim, step_size=0.1)
¶
One step of sheaf diffusion (Laplacian smoothing).
Drives local sections toward global consistency by flowing along the negative gradient of the sheaf Laplacian energy:
E(x) = x^T L_F x = sum_{(i,j)} ||R_{ij} x_i - x_j||^2
Parameters¶
sheaf Sheaf with restriction maps. sections Current section values at each node. stalk_dim Dimension of each stalk. step_size Diffusion step size (must be < 1/lambda_max for stability).
Returns¶
dict[str, npt.NDArray[np.float64]] Updated section values after one diffusion step.
Source code in groupoid/laplacian.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
Parallel Transport¶
groupoid.transport.schild_ladder(manifold, tangent_vec, base_point, end_point, n_rungs=1)
¶
Parallel transport via Schild's ladder.
An iterative approximation of parallel transport that only requires
the exponential and logarithmic maps. This is the coarser of the two
ladders: on S^2 its direction agrees with the analytic parallel
transport only to cosine ~0.98 and does not converge to the exact
value as n_rungs increases (see pole_ladder for the accurate
variant and LIMITATIONS.md for the measured behavior).
Parameters¶
manifold A geomstats manifold with exp and log maps. tangent_vec Tangent vector at base_point to transport. base_point Starting point on the manifold. end_point Destination point on the manifold. n_rungs Number of ladder rungs (higher = more accurate).
Returns¶
np.ndarray The transported tangent vector at end_point.
Source code in groupoid/transport.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | |
groupoid.transport.pole_ladder(manifold, tangent_vec, base_point, end_point, n_rungs=1)
¶
Parallel transport via pole ladder.
A variant of Schild's ladder that uses geodesic symmetry instead of midpoint construction. More accurate than Schild's ladder for the same number of rungs, and exactly preserves the norm of the transported vector on symmetric spaces.
Parameters¶
manifold A geomstats manifold with exp and log maps. tangent_vec Tangent vector at base_point to transport. base_point Starting point on the manifold. end_point Destination point on the manifold. n_rungs Number of ladder rungs.
Returns¶
np.ndarray The transported tangent vector at end_point.
Source code in groupoid/transport.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | |
groupoid.transport.compute_transport_matrix(manifold, base_point, end_point, method='pole', n_rungs=2)
¶
Compute the parallel transport matrix between two points.
Constructs the full linear map T such that for any tangent vector v at base_point, T @ v gives the parallel transport of v to end_point.
Parameters¶
manifold A geomstats manifold. base_point Starting point on the manifold. end_point Destination point on the manifold. method Transport method: "pole" or "schild". n_rungs Number of ladder rungs for the approximation.
Returns¶
np.ndarray Transport matrix of shape (dim, dim).
Source code in groupoid/transport.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
Riemannian Optimizers¶
groupoid.optimizer.RiemannianSGD
dataclass
¶
Riemannian stochastic gradient descent.
Updates parameters by computing the Riemannian gradient (projection of Euclidean gradient onto tangent space) and retracting back to the manifold via the exponential map. With momentum, the velocity is parallel-transported into each new iterate's tangent space (see the module docstring), so it accumulates geometry-consistently across steps.
Parameters¶
manifold A geomstats manifold instance. lr Learning rate. momentum Momentum coefficient (0 = no momentum).
Source code in groupoid/optimizer.py
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
step(point, euclidean_grad)
¶
Perform one optimization step.
Parameters¶
point Current point on the manifold. euclidean_grad Euclidean gradient (will be projected to tangent space).
Returns¶
np.ndarray Updated point on the manifold.
Source code in groupoid/optimizer.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
groupoid.optimizer.RiemannianAdam
dataclass
¶
Riemannian Adam optimizer.
Adapts the Adam optimizer to Riemannian manifolds by maintaining exponential moving averages of the Riemannian gradient and its squared norm, with updates via the exponential map. The first moment is parallel-transported into each new iterate's tangent space (see the module docstring); the second moment is a scalar gradient-norm average and needs no transport because parallel transport is an isometry (norms are invariant).
Parameters¶
manifold A geomstats manifold instance. lr Learning rate. beta1 Exponential decay rate for first moment. beta2 Exponential decay rate for second moment. eps Small constant for numerical stability.
Source code in groupoid/optimizer.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
step(point, euclidean_grad)
¶
Perform one optimization step.
Parameters¶
point Current point on the manifold. euclidean_grad Euclidean gradient.
Returns¶
np.ndarray Updated point on the manifold.
Source code in groupoid/optimizer.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
groupoid.optimizer.curvature_adaptive_lr(manifold, point, base_lr, tangent_vec)
¶
Adapt learning rate based on local sectional curvature.
In regions of high positive curvature, geodesics converge and we
should take smaller steps, so the base rate is damped by
1 / (1 + kappa). In flat or negatively curved regions the base
rate is returned unchanged; no enlargement is applied.
Parameters¶
manifold A geomstats manifold with a curvature method. point Current point on the manifold. base_lr Base learning rate to adapt. tangent_vec Direction of the update.
Returns¶
float Adapted learning rate.
Source code in groupoid/optimizer.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |
Persistent Homology¶
groupoid.persistence.compute_persistence(points, max_dim=1, max_edge_length=np.inf)
¶
Compute persistent homology of a point cloud.
Parameters¶
points Array of shape (n_points, n_features) representing model parameters or their embeddings. max_dim Maximum homological dimension to compute. max_edge_length Maximum edge length for the Rips filtration.
Returns¶
PersistenceSummary Topological summary including Betti numbers and persistence.
Source code in groupoid/persistence.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
groupoid.persistence.track_divergence(current_params, previous_summary=None, max_dim=1)
¶
Track federation divergence across rounds.
Computes persistence of current parameter distribution and, if a previous summary exists, computes the bottleneck distance to measure how much the topological structure has changed.
Parameters¶
current_params Array of shape (n_clients, n_features). previous_summary PersistenceSummary from the previous round, if available. max_dim Maximum homological dimension.
Returns¶
PersistenceSummary Updated summary with bottleneck distance to previous round.
Source code in groupoid/persistence.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |