Skip to content

GROUPOID

Pre-alpha research prototype. Not production software. See STATUS and LIMITATIONS.

Groupoid-based aggregation for federated learning on Riemannian manifolds.

GROUPOID explores using the mathematical structure of transport groupoids, cellular sheaves, and Riemannian geometry to aggregate model parameters across heterogeneous federated clients. The central hypothesis is that geometry-aware aggregation (Karcher mean with parallel transport) can outperform naive Euclidean averaging when client parameter spaces are heterogeneous, and that cohomological invariants (H^1) can diagnose irreconcilable model divergence before it degrades performance.

Implemented and tested

Module Description Test coverage
groupoid.manifold Karcher mean via geomstats FrechetMean Hypothesis (500 examples)
groupoid.groupoid Morphism composition, inverse Hypothesis (500 examples)
groupoid.cohomology H^1 via cycle-basis holonomy Hypothesis (500 examples)
groupoid.sheaf Cellular sheaf, restriction maps Hypothesis (500 examples)
groupoid.laplacian Sheaf Laplacian, spectral analysis, diffusion Unit: PSD + delta^T-delta equality on non-orthogonal maps, transport-consistent kernel; Integration: spectral analysis, diffusion
groupoid.aggregation Transport-aware federated aggregation pipeline Integration tests

Implemented, validated against ground truth, not yet integrated

Module Description Test coverage
groupoid.transport Schild's ladder, pole ladder parallel transport Unit: pole ladder matches geomstats analytic parallel transport in direction (cosine > 0.999) and magnitude on S^2; Schild's ladder asserted as a coarser approximation
groupoid.persistence Vietoris-Rips persistent homology Unit: circle 1-cycle via max persistence, two-cluster component count at a finite filtration, translation-invariant bottleneck. Dimension-aware: diagram retains an H0/H1 label and track_divergence compares H0-vs-H0 only, verified against an independent MST reconstruction of the H0 diagram and shown not to leak an H1-only change into the H0 divergence. Betti degeneracy at thresh=inf documented in LIMITATIONS

Implemented, smoke-tested, not yet integrated

Module Description Test coverage
groupoid.optimizer Riemannian SGD, Adam, curvature-adaptive LR Smoke: step stays on S^2; curvature-adaptive LR damps/falls back. Core descent/convergence not validated

Not yet implemented

  • Differential privacy (Opacus, TenSEAL integration)
  • Federated training loop with real neural networks
  • Communication protocol for distributed deployment
  • Convergence guarantees or formal proofs

Test coverage

The committed suite reaches 100% line and branch coverage of the groupoid package on Python 3.10-3.12, enforced in CI (--cov-branch --cov-fail-under=100). Coverage measures which lines run, not whether behavior is correct; the tables above record the validation depth per module, which coverage alone does not capture.

Architecture

Client A ---T_AB---> Client B
  |                    |
  T_AC               T_BD
  |                    |
  v                    v
Client C ---T_CD---> Client D
        \          /
         \        /
       Karcher Mean
      (on manifold)
         /        \
        /          \
   global model -> local updates
   (via inverse transport)

Installation

From source (not published on PyPI):

git clone https://github.com/smaniches/GROUPOID.git
cd GROUPOID
pip install -e ".[dev]"

Quick example

import networkx as nx
import numpy as np
from geomstats.geometry.hypersphere import Hypersphere
from groupoid import TransportGroupoidAggregator

manifold = Hypersphere(dim=2)
graph = nx.DiGraph([("A", "B"), ("A", "C")])

aggregator = TransportGroupoidAggregator(
    manifold=manifold, graph=graph, base_node="A"
)

theta = np.pi / 6
R = np.array([
    [np.cos(theta), -np.sin(theta), 0],
    [np.sin(theta),  np.cos(theta), 0],
    [0, 0, 1],
])
aggregator.register_transport("A", "B", R)
aggregator.register_transport("A", "C", R.T)

client_params = {
    "A": np.array([0.0, 0.0, 1.0]),
    "B": np.array([0.1, 0.0, 0.995]),
    "C": np.array([-0.1, 0.0, 0.995]),
}
client_params = {k: v / np.linalg.norm(v) for k, v in client_params.items()}

result = aggregator.aggregate(client_params)
print(f"H^1 = {result.h1_norm:.2e} (consistent: {result.is_consistent})")

License

Copyright 2026 TOPOLOGICA LLC. Licensed under the Apache License, Version 2.0.