# gspx
`gspx` is a Rust crate for sparse graph signal processing. It provides fast
implementations of graph convolution using rational kernel fitting and
Chebyshev polynomial approximation. Supports dynamic graphs for applications
with a changing network topology.
Designed around large sparse networks such as power systems, but the API works
with any sparse Laplacian.
<p align="center">
<img src="assets/readme/usa-bandpass-source-a.png" alt="USA bandpass from source A" width="980">
</p>
<p align="center">
<a href="https://crates.io/crates/gspx">crates.io</a> |
<a href="https://docs.rs/gspx">docs.rs</a>
</p>
License: `GPL-3.0-only`
MSRV: Rust `1.85`
## Install
```toml
[dependencies]
gspx = "0.1"
```
## Quick Start
Load a Laplacian from a `.mat` file and filter:
```rust,no_run
use gspx::{StaticConvolver, impulse, load_laplacian};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let laplacian = load_laplacian(Path::new("graph.mat"))?;
let signal = impulse(&laplacian, 0, 1)?;
let mut convolver = StaticConvolver::new(laplacian)?;
let filtered = convolver.bandpass(signal.view(), &[0.25, 1.0, 4.0], 2)?;
Ok(())
}
```
Other loaders: `load_signal` for `.mat` signal matrices, `load_ply_laplacian`
and `load_ply_xyz` for PLY meshes.
## Signals
Filtering APIs accept 1D signals `(n_vertices,)` or 2D matrices
`(n_vertices, n_signals)`. For SGMA the signal matrix is
`(n_vertices, n_times)` and the time vector length must match `n_times`.
## Examples
The repository includes runnable examples under `examples/`. Larger example
Laplacians and meshes live under `resources/library` and are not packaged into
the published crate.
```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)?;
Ok(())
}
```
```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
```
## Performance
`perf_core` measures end-to-end filtering calls with Criterion. Each value is
the mean time in milliseconds for one deterministic real `N x 1` signal. Exact
filters use one scale (`[1.0]`) and lowpass/bandpass use order `1`.
### Static vs Dynamic Filtering
Real power-grid Laplacians from `resources/library`. No branch edits are
applied between dynamic calls, isolating filtering cost.
<table>
<thead>
<tr>
<th rowspan="2">Graph</th>
<th rowspan="2">N</th>
<th colspan="3">Static (ms)</th>
<th colspan="3">Dynamic (ms)</th>
</tr>
<tr>
<th>Lowpass</th>
<th>Bandpass</th>
<th>Highpass</th>
<th>Lowpass</th>
<th>Bandpass</th>
<th>Highpass</th>
</tr>
</thead>
<tbody>
<tr><td>Hawaii</td><td><code>37</code></td><td><code>0.00051</code></td><td><code>0.00101</code></td><td><code>0.00054</code></td><td><code>0.00051</code></td><td><code>0.00118</code></td><td><code>0.00053</code></td></tr>
<tr><td>WECC</td><td><code>240</code></td><td><code>0.00179</code></td><td><code>0.00344</code></td><td><code>0.00169</code></td><td><code>0.00171</code></td><td><code>0.00359</code></td><td><code>0.00195</code></td></tr>
<tr><td>Texas</td><td><code>2k</code></td><td><code>0.01704</code></td><td><code>0.03621</code></td><td><code>0.01754</code></td><td><code>0.01742</code></td><td><code>0.03490</code></td><td><code>0.01720</code></td></tr>
<tr><td>East</td><td><code>16k</code></td><td><code>1.63247</code></td><td><code>3.66904</code></td><td><code>1.42601</code></td><td><code>1.26126</code></td><td><code>2.91676</code></td><td><code>0.96470</code></td></tr>
<tr><td>EastWest</td><td><code>65k</code></td><td><code>2.09393</code></td><td><code>3.60965</code></td><td><code>1.53460</code></td><td><code>2.02737</code></td><td><code>3.35085</code></td><td><code>1.74977</code></td></tr>
<tr><td>USA</td><td><code>82k</code></td><td><code>2.02103</code></td><td><code>4.04234</code></td><td><code>1.45063</code></td><td><code>1.62788</code></td><td><code>3.88886</code></td><td><code>1.52146</code></td></tr>
</tbody>
</table>
Exact filtering on 65k–82k nodes stays in the low-single-digit millisecond
range. Structure matters as much as node count.
### Constructor and Lowpass
| `Texas` | `2k` | `0.00286` | `0.01713` |
| `East` | `16k` | `0.76098` | `1.65513` |
| `EastWest` | `65k` | `0.77505` | `2.02311` |
| `USA` | `82k` | `0.80452` | `2.00556` |
### Dynamic Updates on `USA`
| Warmed `add_branch_once` | `1.78406` |
| `bandpass_one` after `1` update | `5.22890` |
| `bandpass_one` after `10` updates | `7.25095` |
| `bandpass_one` after `50` updates | `19.39280` |
## Platform Notes
- Targets Linux, macOS, 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
- Python `sgwt`: [github.com/lukelowry/sgwt](https://github.com/lukelowry/sgwt)
- Python docs: [sgwt.readthedocs.io](https://sgwt.readthedocs.io/)
- Julia: [github.com/lukelowry/SpectralGraphWavelet.jl](https://github.com/lukelowry/SpectralGraphWavelet.jl)
- SuiteSparse: [github.com/DrTimothyAldenDavis/SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse)
- Synthetic grid cases: [electricgrids.engr.tamu.edu](https://electricgrids.engr.tamu.edu/electric-grid-test-cases/)
- Author: [lukelowry.github.io](https://lukelowry.github.io/)
- Google Scholar: [scholar.google.com/citations](https://scholar.google.com/citations?user=CTynuRMAAAAJ&hl=en)