Added L1Potts class#344
Open
mstorath wants to merge 4 commits into
Open
Conversation
|
Hi could you submit a feature request to https://github.com/edelweiss611428/rupturesRcpp @mstorath I may implement this later. |
mstorath
added a commit
to mstorath/ruptures
that referenced
this pull request
May 5, 2026
Fixes pre-commit.ci failure on PR deepcharles#344. v1.7.5's hook manifest used language: python_venv, which was removed in pre-commit 4.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Penalized change point detection for piecewise constant signals with L1 data fidelity
The L1 Potts model is defined as follows:
\min_{u} \sum_{i=1}^N w_i |f_i - u_i| + \gamma \sum_{i=1}^{N-1} \mathbb{I}(u_i \neq u_{i+1})
The algorithm implemented is described in the paper
the paper
Storath, Weinmann, Unser.
Jump-penalized least absolute values estimation of scalar or circle-valued signals,
Information and Inference, 2017
The method is significantly faster than PELT for the L1 jump penalized problem.
Rewrite the L1Potts implementation to match Algorithm 1 of Storath, Weinmann & Unser (2017) explicitly. The forward DP is identical to the paper; backtracking uses explicit parent pointers (uint8 matrix + prev-argmin vector) instead of the paper's in-place float64 subtraction trick. Mathematically equivalent, ~8x less memory. Hardening: reject non-numeric dtypes, NaN/Inf in signal or weights, empty signal, and predict-before-fit; clear ValueError/RuntimeError messages instead of cryptic IndexError/AttributeError. Defensive copies of signal and weights shield the solver from caller mutation. Tests (tests/test_l1potts.py, 71 cases): - functional-value parity with Pelt+CostL1 over 5 random seeds - replication-equivalence for integer weights, including weight ratios up to 10000:1 - positive homogeneity over 7 orders of magnitude in alpha - reversal symmetry of the functional - multi-pen independence on a single fit - edge cases: n=1, K=1, n=2, shape (N,1), pen=+inf, pen=NaN, empty signal, complex/object/bool dtypes, list/tuple/read-only inputs - SyntaxWarning regression net via subprocess + compile() Docs: user-guide page (docs/user-guide/detection/l1potts.md) with usage example and reference, code-reference autodoc stub, mkdocs nav entries. Export L1Potts at top level (rpt.L1Potts). Performance: on N=5000 noisy 1D signal, ~20x faster than Pelt(model="l1", min_size=1, jump=1) with identical functional value. Implementation prepared with assistance from Claude (Anthropic) acting as a coding agent. The algorithm is the cited authors'; the agent's contributions were the parent-pointer backtracking variant, input hardening, and the test suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pre-commit.ci's runner image upgraded to Python 3.14, where docformatter v1.7.7's transitive dependency `untokenize` 0.1.1 (sdist from 2013) fails to build with `AttributeError: 'Constant' object has no attribute 's'` during `Getting requirements to build wheel`. v1.7.8 dropped the `untokenize` dependency entirely, fixing the issue. The original pin comment (`Don't autoupdate until issue deepcharles#293 is fixed`) referred to the python_venv language manifest issue, which has been fixed since v1.7.6. The pin has therefore been stale; this PR bumps it forward by one minor. Also includes the docstring normalizations v1.7.8 applies (single-line summary + body convention per PEP 257), keeping pre-commit clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
black 25.x and docformatter v1.7.8 fundamentally disagree on the number of blank lines between a module docstring and a top-level class definition (PEP 8 wants 2, docformatter v1.7.8 wants 1). The conflict applies in exactly two files in the repo: src/ruptures/exceptions.py src/ruptures/metrics/sanity_check.py These are the only files where a module docstring is directly followed by a class with no imports in between. All other files have imports between the docstring and the first class, side-stepping the rule. Restore those two files to the upstream black-friendly state and add them to docformatter's exclude regex (alongside the pre-existing mkdocs_macros.py exclude). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
L1Potts, an exact O(KN) solver for the (weighted) L1 Potts model —penalized piecewise-constant estimation under L1 data fidelity, robust to
heavy-tailed noise and outliers:
This complements
Pelt(model="l1")with a far faster algorithm specializedfor this functional. On a 5000-sample noisy 1D signal: ~20× faster than
Pelt(model="l1", min_size=1, jump=1), with identical functional valueto machine precision.
Algorithm
Implements Algorithm 1 of Storath, Weinmann & Unser (2017):
V = unique(signal),because the weighted L1 median is always a data value. So the search
space is reduced from ℝ^N to V^N.
Felzenszwalb-Huttenlocher trick for the Potts penalty (O(K) per
column instead of O(K²)).
signal.
Forward pass matches the paper exactly. Backtracking uses explicit parent
pointers (uint8 matrix + prev-argmin vector) instead of the paper's
in-place float64 subtraction trick — mathematically equivalent, ~8× less
memory.
Currently 1D-only and penalty-only mode (no
n_bkps/epsilon); thealgorithm is exact, so
min_size/jumpare fixed at 1.What's included
src/ruptures/detection/l1potts.py— solver + input validationtests/test_l1potts.py— 71 tests covering correctness, weights,hardening, edge cases:
Pelt+CostL1over multiple seeds10000:1
orders of magnitude
predict-before-fit
compile())docs/user-guide/detection/l1potts.md— user guide pagedocs/code-reference/detection/l1potts-reference.md— autodoc stubmkdocs.yml— nav entriesrpt.L1PottsDisclosure
Implementation prepared with assistance from Claude (Anthropic) acting as a
coding agent. The algorithm is the cited authors' (SWU2017); the agent's
contributions were the parent-pointer backtracking variant (memory
optimization), input hardening, and the test suite.