Hodge Laplacian + simplicial complex + 3-way Hodge decomposition on graphs. Pure scipy.sparse, 2-simplex (triangle) scope.
Project description
hodgex
Hodge Laplacians, simplicial complexes (up to 2-simplices), and the 3-way gradient + curl + harmonic Hodge decomposition of edge flows. Pure scipy.sparse, no C++ dependencies, no optional backends.
Why it exists
The topological-signal-processing ecosystem on PyPI has gaps the audit for our Phase 7 work called out explicitly:
toposig— does not exist (hallucinated by LLM ideation)HodgeLaplacian(PyPI) — fake package, do not installtoponetx— real but v0.x with volatile API surface; not reliable for productiongraph-hodge— real, but graph-only, no simplicial complex support
hodgex fills the narrow, stable slice: build a 2-complex, compute its boundary matrices and Hodge Laplacians, project signals onto the low-frequency spectral basis, and get the canonical 3-way edge-flow decomposition. Everything is testable against hand-checkable fixtures (K5, C6, K4-with-filled-triangles).
Install
pip install hodgex
Python ≥ 3.9, numpy ≥ 1.23, scipy ≥ 1.10.
Quickstart
import numpy as np
from hodgex import SimplicialComplex2, hodge_laplacian_0, hodge_laplacian_1, low_freq_projection, hodge_decompose_edge
# Build K5 (complete graph on 5 vertices) and fill every triangle
sc = SimplicialComplex2.from_edges(
n_vertices=5,
edges=[(i, j) for i in range(5) for j in range(i + 1, 5)],
).fill_all_triangles()
# Boundary matrices: B1 is (V, E), B2 is (E, T)
B1, B2 = sc.boundary_matrices()
# Hodge Laplacians
L0 = hodge_laplacian_0(B1) # vertex Laplacian = classical graph Laplacian
L1 = hodge_laplacian_1(B1, B2) # edge Laplacian (the "real" Hodge object)
# Spectral smoothing: project a vertex signal onto the k lowest eigenvectors of L0
signal = np.array([1., 2., 3., 4., 100.]) # vertex 4 is an outlier
smoothed = low_freq_projection(signal, L0, k=2)
# 3-way Hodge decomposition of an edge flow
edge_flow = np.random.default_rng(0).standard_normal(B1.shape[1])
grad, curl, harm = hodge_decompose_edge(edge_flow, B1, B2)
assert np.allclose(grad + curl + harm, edge_flow)
What's inside
| Function | Returns |
|---|---|
SimplicialComplex2(n, edges, triangles) |
an oriented 2-complex; rejects self-loops, enforces triangle ⊂ edges |
.fill_all_triangles() |
mutates to add every 3-clique as a triangle (fills C_n's cycles) |
.boundary_matrices() |
returns (B1, B2) as scipy.sparse.csr_matrix |
hodge_laplacian_0(B1) |
L0 = B1 @ B1.T — classical graph Laplacian |
hodge_laplacian_1(B1, B2) |
L1 = B1.T @ B1 + B2 @ B2.T — edge Laplacian |
hodge_laplacian_2(B2) |
L2 = B2.T @ B2 |
low_freq_projection(signal, L, k) |
topology-aware smoothing: project onto k smallest eigenvectors |
kernel_dimension(L) |
Betti number (β_0 from L0, β_1 from L1) via low-eigenvalue counting |
hodge_decompose_edge(flow, B1, B2) |
(grad, curl, harm) — sums exactly to flow, mutually L2-orthogonal |
The math (1-minute version)
An oriented 2-complex has a chain complex C_2 → C_1 → C_0 with boundary maps ∂_2 = B2 (triangles → edges) and ∂_1 = B1 (edges → vertices). By construction ∂_1 ∘ ∂_2 = 0 — hodgex asserts this at build time.
The Hodge Laplacian at dimension k combines the two adjacent boundaries:
L0 = ∂_1 ∂_1ᵀ (vertices: the combinatorial graph Laplacian)
L1 = ∂_1ᵀ ∂_1 + ∂_2 ∂_2ᵀ (edges: down-Laplacian + up-Laplacian)
L2 = ∂_2ᵀ ∂_2 (triangles)
The Hodge decomposition theorem on C_1 (edge signals):
C_1 = im(∂_1ᵀ) ⊕ im(∂_2) ⊕ ker(L1)
≡ gradient ⊕ curl ⊕ harmonic
Every edge flow splits uniquely into:
- a gradient part:
∂_1ᵀ vfor some vertex potentialv(conservative flow) - a curl part:
∂_2 tfor some triangle circulationt(vortex flow) - a harmonic part: in the kernel of L1 — neither a gradient nor a curl (persistent cycles)
The three subspaces are mutually L²-orthogonal. The dimension of the harmonic subspace equals β_1, the first Betti number — the number of independent 1-cycles not filled in by triangles.
Key subtlety (called out by the audit for the OMEGA use-case): on a connected graph, the kernel of L0 is only the constant vector (β_0 = 1). So the "harmonic component of a vertex signal" is trivially the signal's mean. The interesting content of vertex-signal smoothing is the low-eigenvalue projection onto L0 — low_freq_projection(signal, L0, k) returns exactly this. The genuine 3-way gradient + curl + harmonic split only appears on edge signals (1-forms); hence hodge_decompose_edge.
Correctness
16 unit tests cover:
| Test | Checks |
|---|---|
| K5 spectrum | L0 eigenvalues exactly [0, 5, 5, 5, 5] |
| K5 triangle count | fill_all_triangles produces 10 triangles (C(5,3)) |
| C6 β_1 | one independent cycle on a 6-gon |
| C3 unfilled | β_1 = 1 |
| C3 filled | β_1 = 0 (triangle kills the cycle) |
| K4 filled | β_1 = 0 |
| Two disjoint edges | β_0 = 2 (matches connected components) |
| Chain complex | B1 @ B2 = 0 exactly at machine precision |
| Low-freq k=1 | returns the mean broadcast |
| Low-freq k monotone | more eigenvectors → smaller reconstruction error |
| Hodge decomp sum | grad + curl + harm == flow |
| Harmonic on C6 | pure-cycle flow projects onto the 1-dim L1 kernel |
| Kernel dimension helper | matches dense eigvalsh count |
| Input validation | self-loops, missing triangle edges, out-of-range vertices all raise |
Run with pytest.
Roadmap
v0.2 (planned):
- Weighted Hodge Laplacian (per-edge weights for non-combinatorial cases)
- 3-simplex (tetrahedron) support, capped — compute grows as O(C(n, 4))
- Persistent homology wrapper (bridge to ripser for the Phase 7B path)
- GPU-backed eigendecomposition via
cupy.sparseoptional extra
Who's using it
Built for OMEGA Swarm Phase 7 (Topological Signal Intelligence) — agent-signal denoising via L0 spectral filtering on a PMFG correlation backbone. Sister library to phawkes (Hawkes processes), fisherrao (information geometry), tailcor (tail-contagion decomposition), diebold-yilmaz (spillover index). Same "small, tested, publishable" ethos.
Authors
- Pierre Samson (@darw007d) — idea, use-case, design decisions
- Claude Opus (Anthropic) — implementation and tests
Citations
- Lim, L.-H. (2020). Hodge Laplacians on graphs. SIAM Review 62(3), 685-715.
- Schaub, M.T. et al. (2020). Signal processing on higher-order networks: Livin' on the edge... and beyond. Signal Processing 187.
- Eckmann, B. (1944). Harmonische Funktionen und Randwertaufgaben in einem Komplex. Commentarii Mathematici Helvetici 17.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hodgex-0.1.0.tar.gz.
File metadata
- Download URL: hodgex-0.1.0.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3147cdd3c71a7228a709e022cc1739ef093e30676f013f38bcacf929437a3b62
|
|
| MD5 |
3a1599acecaf50004e1dbceae165352f
|
|
| BLAKE2b-256 |
990d8554a2d54fb7c216925508e73a9d533086a759c165ee0e3c99d9926a3f36
|
File details
Details for the file hodgex-0.1.0-py3-none-any.whl.
File metadata
- Download URL: hodgex-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d94601b95af68b7ad6dc809183e79c03da40fa3e005ad3a01853eae84944aa66
|
|
| MD5 |
8bc4ae67c5dc53fd66371150aca92f94
|
|
| BLAKE2b-256 |
91c86269bc7e158eef2a92dc7668d77f5f3398940c5fbd47901ed591e88903f4
|