Skip to content

Performance

Measured with benchmarks/performance.py, which runs the same series and parameters against this version, the previous release (1.1.0), the first PyTorch release (1.0.0) and the original NumPy implementation (0.4), each in a fresh process, and scores every run against the true changepoints (F1, margin 5). Apple M1, CPU, 4 threads, PyTorch 2.14, Python 3.12; median of up to five runs. Raw results, with more sizes: benchmarks/results/2026-09-23-apple-m1-cpu.json.

Workload 1.2.0 1.1.0 0.4 (NumPy) 1.0.0 (PyTorch)
Offline StudentT, 1 000 points 0.53 s 3.1 s 24 s 108 s, misses changes (F1 0.50)
Offline StudentT, 2 000 points 2.0 s 24 s 101 s not run (predicted 426 s)
Offline MultivariateT, 5-D, 1 000 points 0.94 s 3.4 s not available 30 s
Online StudentT, 1 000 points 0.16 s 0.13 s 0.12 s 39 s
Online StudentT, 5 000 points 1.9 s 1.8 s 2.0 s not run (predicted 825 s)
Online MultivariateT, 5-D, 1 000 points 0.40 s 0.37 s crashes (NameError) 51 s, wrong (F1 0.04)
OnlineChangepointDetector, 50 000 points, max_run_length=1000 7.9 s (160 µs per point) not available not available not available

1.2.0 finds every change in each of these series (F1 1.00) except the streaming one (F1 0.94, 199 changes). In short: the offline detector is 31–51x faster than the NumPy original, 206–360x faster than 1.0.0 (which also misses changes) and faster than 1.1.0 by a factor that grows with length (1.5x at 250 points, 6x at 1 000, 12x at 2 000); the online detector runs at the speed of the NumPy original (both are a Python loop over time), and its multivariate model is correct only since 1.1.0.

Detection quality on real data, measured with benchmarks/tcpd.py on the Turing Change Point Dataset (van den Burg and Williams, 2020; 30 annotated real series, TCPDBench's F1 and covering metrics, higher is better):

Method, default settings F1 cover
No changepoints at all (baseline) 0.668 0.575
BOCPD as published by TCPDBench (R package ocp) 0.696 0.636
This library, online, viterbi_changepoints 0.694 0.637
This library, offline 0.739 0.664

The online model reproduces the published BOCPD (identical F1 on 27 of the 31 series both score, which add the 2-D run_log to these 30; also with tuned settings, 0.887 against 0.890); the offline detector beats it without tuning. For a finished series, use the offline detector, or read the online posterior with viterbi_changepoints. The online readout get_map_changepoints reports changes as data arrive, without hindsight, and scores 0.571 F1 on the same series.

Complexity: the online recursion is O(T²) in time and memory (the run-length posterior R is (T+1)² float32); OnlineChangepointDetector with max_run_length=K is O(K) per observation. The offline recursion is O(T²) (vectorized per start point), and so, in practice, is the table of changepoint locations Pcp: it is O(J T²) for J rows, and rows stop once the probability of that many changepoints drops below exp(-1000) (about 190 rows for a series with three clear changes, whatever its length; up to 19x faster than 1.1.0 at 4 000 points); memory is about 16 T² bytes (see docs/devices.md).

Accelerators: see the FAQ; MPS is slower than the CPU on all of these, CUDA is unmeasured (issue #43). Only measured numbers appear in this README.