# gspx
`gspx` is a Rust crate for sparse graph signal processing. It provides fast
implementations of graph convolution using rational kernel
fitting (and alternatively using the slower Chebyshev method). Supports dynamic graphs for applications with a changing network topology.
It was designed around large sparse networks such as power systems, but the
API works with any sparse Laplacian.
License: `GPL-3.0-only`
MSRV: Rust `1.85`
## Install
```toml
[dependencies]
gspx = "0.1"
```
## What It Provides
- Exact lowpass, bandpass, and highpass filtering on sparse Laplacians
- Reusable dynamic filtering after low-rank branch updates
- Chebyshev polynomial approximations for faster approximate filtering
- Rational kernel fitting through vector fitting
- SGMA for joint spatial-temporal analysis
## Quick Start
If you already have a Laplacian in memory, you can start filtering
immediately:
```rust
use gspx::{StaticConvolver, impulse};
use sprs::TriMat;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut triplets = TriMat::<f64>::new((3, 3));
triplets.add_triplet(0, 0, 1.0);
triplets.add_triplet(0, 1, -1.0);
triplets.add_triplet(1, 0, -1.0);
triplets.add_triplet(1, 1, 2.0);
triplets.add_triplet(1, 2, -1.0);
triplets.add_triplet(2, 1, -1.0);
triplets.add_triplet(2, 2, 1.0);
let laplacian = triplets.to_csc();
let signal = impulse(&laplacian, 0, 1)?;
let scales = [0.25, 1.0, 4.0];
let mut convolver = StaticConvolver::new(laplacian)?;
let filtered = convolver.bandpass(signal.view(), &scales, 2)?;
println!("computed {} scales", filtered.len());
Ok(())
}
```
## Loading Local Files
If your graph data is already stored on disk, use the direct file loaders:
```rust,no_run
use gspx::{load_laplacian, load_ply_laplacian, load_ply_xyz, load_signal};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let laplacian = load_laplacian(Path::new("/path/to/graph.mat"))?;
let signal = load_signal(Path::new("/path/to/signals.mat"))?;
let mesh_graph = load_ply_laplacian(Path::new("/path/to/mesh.ply"))?;
let mesh_xyz = load_ply_xyz(Path::new("/path/to/mesh.ply"))?;
println!("graph vertices: {}", laplacian.rows());
println!("signal shape: {:?}", signal.raw_dim());
println!("mesh vertices: {}", mesh_graph.rows());
println!("mesh coordinate shape: {:?}", mesh_xyz.raw_dim());
Ok(())
}
```
## Main APIs
### Exact and Dynamic Filtering
- `StaticConvolver` for fixed graphs
- `DynamicConvolver` for graphs that receive branch updates through `add_branch`
- `lowpass(...)`, `bandpass(...)`, and `highpass(...)` for analytical filters
### Approximate Filtering
- `ChebyModel` to fit Chebyshev kernels
- `ChebyConvolver` to apply them efficiently on sparse graphs
### Custom Rational Kernels
- `VfModel` to fit rational kernels from sampled spectral responses
- `VfKernel` to store poles, residues, and direct terms
### SGMA
- `Sgma` for spectral graph modal analysis
- `spectrum(...)`, `spectrum_complex(...)`, `find_peaks(...)`, `find_modes(...)`
### Helpers
- `impulse(&laplacian, vertex_index, n_channels)`
- `impulse_1d(&laplacian, vertex_index)`
- `estimate_spectral_bound(&laplacian)`
## Signal Shape Conventions
Filtering APIs accept either:
- 1D signals with shape `(n_vertices,)`
- 2D signal matrices with shape `(n_vertices, n_signals)`
For SGMA, the signal matrix is `(n_vertices, n_times)` and the time vector
length must match `n_times`.
## Repository Examples
The repository includes runnable examples under `examples/`.
If you cloned this repository, the larger example Laplacians and meshes used by
some examples live under `resources/library`. They are not packaged into the
published crate.
`ExampleData` is a convenience wrapper for that local directory layout, or for
your own directory if you mirror the same subfolders:
```rust,no_run
use gspx::{Dataset, ExampleData, GraphKind, KernelPreset};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = ExampleData::from_dir("resources/library")?;
let texas = data.graph(Dataset::Texas, GraphKind::Delay)?;
let coords = data.coords(Dataset::Texas)?;
let kernel = data.vf_kernel(KernelPreset::ModifiedMorlet)?;
println!("Texas vertices: {}", texas.rows());
println!("Texas coordinate shape: {:?}", coords.raw_dim());
println!("kernel poles: {}", kernel.poles.len());
Ok(())
}
```
Common example commands from a cloned checkout:
```bash
cargo run --example basic_static_filters
cargo run --example basic_dynamic_topology
cargo run --example basic_chebyshev
cargo run --example basic_vf_fit
cargo run --example basic_sgma
cargo run --example basic_resource
cargo run --example basic_local_files
```
Plotting and benchmark-report examples also live in `examples/`, but they are
primarily for repository use rather than typical crate consumption.
## Platform Notes
- `gspx` currently targets Linux and Windows
- Sparse shifted solves use SuiteSparse-backed LDL factorization via `sprs-ldl`
- The published crate does not include repository example datasets
## Citation
If you use `gspx` for research, cite the associated HICSS paper.
```bibtex
@inproceedings{lowery-gspx-2026,
title={Using Spectral Graph Wavelets to Analyze Large Power System Oscillation Modes},
author={Lowery, Luke and Baek, Jongoh and Birchfield, Adam},
year={2026}
}
```
The same citation metadata is also available in [`CITATION.cff`](./CITATION.cff).
## Links
- Julia implementation: <https://github.com/lukelowry/SpectralGraphWavelet.jl>
- SuiteSparse: <https://github.com/DrTimothyAldenDavis/SuiteSparse>
- Synthetic grid cases: <https://electricgrids.engr.tamu.edu/electric-grid-test-cases/>
- Author page: <https://lukelowry.github.io/>
- Google Scholar: <https://scholar.google.com/citations?user=CTynuRMAAAAJ&hl=en>