Reference implementation of two noise-robust algorithms for ergodic time-series analysis:
- STPDP (Sum of Trend Power and Detrended Power) — noise-robust estimator of the mean orbital period.
- SSRPC (State Space Reconstruction with Principal Components) — noise-robust state space reconstruction that enables consistent estimation of the maximal Lyapunov exponent even at high noise levels.
See the accompanying paper for the full theory.
The figures below illustrate the key advantage of SSRPC: the slope of the divergence curve (the MLE) stays consistent at heavy noise levels, whereas the conventional method does not.
The repository includes NoLiTSA as a git submodule. Clone recursively so it is pulled in:
git clone --recursive https://github.com/Lee-Jun-Hyuk-37/SSRPC.git
cd SSRPCThen create a Python environment. With conda:
conda create -n ssrpc python=3.10
conda activate ssrpc
pip install -e .Or with a plain virtualenv:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .pip install -e . installs numpy, scipy, numba, matplotlib, and
scikit-learn, then registers the ssrpc package in editable mode.
import numpy as np
import ssrpc
# `data` is a 1-D scalar time series with sampling period `sample`.
result = ssrpc.estimate_mle(
data,
sample=0.01,
maxt=500,
linear_region=(0, 200), # slope of divergence over these indices
)
print(result.omega) # mean orbital period (STPDP)
print(result.dim) # retained principal-component count
print(result.mle) # maximal Lyapunov exponent
print(result.divergence) # ndarray of length maxt
print(result.time) # matching time axisestimate_mle auto-estimates omega when it is not supplied. Pass
omega=... explicitly to reuse a previously computed value, and
dimension=... to skip the STPDP validity test and keep exactly that
many leading components.
The original class-based API is preserved and can be mixed with the functional helpers:
from ssrpc import STPDP, SSRPC
omega = STPDP(data, max_length=500).calculate_omega()
model = SSRPC(data, sample=0.01, omega=omega)
model.reconstruct()
model.calculate_divergence(maxt=500)
model.plot_divergence(expected=1.5)
lyap = model.mle((0, 200))Existing code that used from SSRPC import SSRPC / from STPDP import STPDP keeps working unchanged.
- Importing
ssrpctransparently patches a memory leak in NoLiTSA's@jit-compiled distance functions. The replacements produce the same numerical output and remove hundreds of MB of leaked memory per MLE run. SSRPC.calculate_divergenceandSSRPC.mlenow raiseRuntimeErrorwhen called out of order; the originalprint-only warnings were easy to miss.
@article{lee2023ssrpc,
title={Noise-robust estimation of the maximal Lyapunov exponent based on state space reconstruction with principal components},
author={Lee, Jun Hyuk and Park, Il Seung and Ahn, Jooeun},
journal={Chaos, Solitons \& Fractals},
volume={174},
pages={113916},
year={2023},
publisher={Elsevier}
}Published in Chaos, Solitons & Fractals. Link
I appreciate M. Mannattil for the wonderful NoLiTSA repository. Thanks to co-authors Il Seung Park and Jooeun Ahn for their contributions to this study.

