Skip to content

About

Simplicial-SINDy infers simplicial complexes from multivariate time-series data

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

Repository files navigation

simplicial-sindy

Discover simplicial complexes — nodes, edges, and triangles — from multivariate time series.

Classical network inference recovers only pairwise edges. simplicial-sindy fits a sparse polynomial dynamical model (SINDy) with an ADMM group-lasso under a second-order-cone simplicial hierarchy constraint, so the recovered structure is a valid simplicial complex: every triangle's three boundary edges are guaranteed present. This exposes genuine higher-order (triadic) interactions, not just pairwise correlations.

Install

pip install simplicial-sindy

Optional extras:

pip install "simplicial-sindy[gpu]"       # CuPy (CUDA 12) backend
pip install "simplicial-sindy[progress]"  # tqdm progress bars

Quickstart

import numpy as np
from simplicial_sindy import SindyConfig, process_data_in_windows

X = np.random.randn(20, 20_000)              # (n_nodes, n_timestamps)

cfg = SindyConfig(fs=200.0, win_len=3200, stride=1600, d_max=2,
                  tau2_q=0.95, tau3_q=0.95)
windows = process_data_in_windows(X, cfg)    # one result per sliding window

w = windows[0]
w["edges"]                       # [(i, j), ...]
w["edge_features"]["vector"]     # S2 interaction score per edge (same order)
w["triangles"]                   # [(i, j, k), ...]
w["triangle_features"]["vector"] # S3 score per triangle (same order)
w["window_start"], w["window_end"]

Every window payload also carries node_features, incidence matrices (edge_features["incidence"], triangle_features["incidence_edges"]), the chosen thresholds (tau2, tau3), and counts (n_edges, n_triangles, n_closed_triangles).

Configuration

SindyConfig groups every parameter the pipeline reads (validated on construction):

Group Fields
Windowing fs, win_sg, order, win_len, stride, overlap
Library d_max (2 → triangles)
Local samples r_target_pc, k_min, k_max
ADMM solver scale, simpl_rho, admm_rho, admm_overrelax, max_iters, row_norm_nnz_thr
Extraction tau2_q, tau3_q (score quantiles)

stride=None derives the stride from overlap. Use cfg.replace(win_len=4096) for variants and cfg.effective_stride to see the resolved value. An argparse.Namespace with the same field names also works — the pipeline only reads attributes.

Backend and threading

Importing the package is side-effect free: it never prints, never mutates os.environ, and never allocates on the GPU. CPU (NumPy) is the default.

import simplicial_sindy as ss

ss.set_backend("auto")     # use CuPy if it works, else NumPy
ss.gpu_available()         # -> bool
ss.configure_threads(1)    # opt-in: pin BLAS/OpenMP threads (solver parallelises over windows)

Environment overrides: SIMPLICIAL_SINDY_BACKEND=cpu|gpu|auto, SIMPLICIAL_SINDY_FORCE_CPU=1, and PARALLEL_SINDY_N_JOBS for the joblib worker count.

Reproducibility

The ADMM solve is iterative, so the number of BLAS threads changes floating-point reduction order and can flip a handful of simplices that sit exactly on the tau2_q / tau3_q quantile boundary. For bit-exact, run-to-run reproducible output, pin BLAS to one thread. This must happen before NumPy loads its BLAS, so either set it in the shell:

OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 python your_script.py

or call it as the very first thing in your script, above import numpy:

from simplicial_sindy import configure_threads
configure_threads(1)
import numpy as np           # BLAS picks up the setting here

Single-threaded BLAS is also usually faster here, because the pipeline already parallelises across windows with joblib. Verified: with threads pinned, this package reproduces the reference results exactly (identical edges, triangles, and scores to 0 ULP).

Also included

A lighter graph-only (d_max=1) STLSQ pipeline and the shared Savitzky-Golay preprocessing:

from simplicial_sindy import preprocess_timeseries, PreprocessConfig, run_sindy_windows

X_proc, dXdt = preprocess_timeseries(X, PreprocessConfig(fs=200.0, win_sg=29, poly_order=3))
results = run_sindy_windows(X, window_size=3200, stride=1600)   # -> [WindowResult, ...]

About

Simplicial-SINDy infers simplicial complexes from multivariate time-series data

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages