Skip to main content

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:

  • toposigdoes not exist (hallucinated by LLM ideation)
  • HodgeLaplacian (PyPI) — fake package, do not install
  • toponetx — real but v0.x with volatile API surface; not reliable for production
  • graph-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ᵀ v for some vertex potential v (conservative flow)
  • a curl part: ∂_2 t for some triangle circulation t (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.sparse optional 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hodgex-0.1.0.tar.gz (15.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hodgex-0.1.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

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

Hashes for hodgex-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3147cdd3c71a7228a709e022cc1739ef093e30676f013f38bcacf929437a3b62
MD5 3a1599acecaf50004e1dbceae165352f
BLAKE2b-256 990d8554a2d54fb7c216925508e73a9d533086a759c165ee0e3c99d9926a3f36

See more details on using hashes here.

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

Hashes for hodgex-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d94601b95af68b7ad6dc809183e79c03da40fa3e005ad3a01853eae84944aa66
MD5 8bc4ae67c5dc53fd66371150aca92f94
BLAKE2b-256 91c86269bc7e158eef2a92dc7668d77f5f3398940c5fbd47901ed591e88903f4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page