Changelog
This changelog highlights notable updates. For full commit history, refer to GitHub.
Unreleased
Add a
CITATION.cfffile, read by GitHub’s Cite this repository button, and the citation of the TSB-SEG benchmark (NeurIPS 2026) to the README.
0.3.0 (2026-10-14)
Upgrading from 0.2.0: VQTSSDetector, VSAXDetector and the HDP-HSMM
aliases are removed; GreedyGaussianDetector returns change points instead
of labels; the arguments of SNLDSDetector are keyword-only;
ProphetDetector, and ChangeFinderDetector without n_cps, select
change points differently by default; BeastDetector needs Rbeast from
PyPI (tsseg[beast]); the supervision and dependency tags of several
detectors change. KCPDDetector, HMMDetector, PatssDetector and the
n_cps parameter of AutoPlaitDetector are deprecated. Several fixes
change the output of AutoPlait, BEAST, IGTS, Window and the vendored ruptures
solvers (ties): results computed with 0.2.0 can differ.
New detector
ChangeForestDetector: classifier-based nonparametric change point detection with random forests (Londschien, Bühlmann and Kovács, JMLR 2023). It wraps the officialchangeforestpackage, installed with the new extrapip install tsseg[changeforest]. Withn_cpsit runs best-first binary segmentation on the package’s gains, a semi-supervised mode that is not part of the paper.New
RuLSIFDetector(Liu, Yamada, Collier and Sugiyama, Neural Networks 2013): change points are peaks of the symmetrised relative Pearson divergence between the subsequences before and after each point, estimated by RuLSIF with cross-validated kernel width and regularisation. NumPy port of the authors’ MATLAB code (MIT); guided (n_cps) or thresholded.TSCP2Detectortakes arandom_stateseed (Noneby default, as before): with an integer, every fit initialises the encoder, shuffles the training windows and draws the dropout masks identically, so two fits on the same series return the same change points. Without a seed, the weights and the shuffling changed at every fit, and nothing declared the detector non-deterministic. The seed goes throughtf.keras.utils.set_random_seed, which also reseeds the global Python, NumPy and TensorFlow generators.Dependencies:
tqdmandjoblibare no longer required. No module of tsseg imports them since the unfinished Python port of AutoPlait was removed (joblibremains installed with scikit-learn).fetch_datasetshows a download progress bar only whentqdmis installed.Tags: the undocumented
semi_supervisedtag ofAmocDetector,EspressoDetectorandRandomDetectoris removed:capability:semi_supervisedis the tag (thesemi_supervisedparameter ofRandomDetectoris unchanged). The deprecatedHMMDetector, neither unsupervised nor semi-supervised, declarescapability:semi_supervised: its number of states is the length ofemission_funcs. A contract test rejects unknown tags and checks that every detector declares both supervision tags, at least one of themTrue.Tags:
python_dependenciesisNoneor a list of the optional modules a detector imports.ProphetDetector,TGLADDetectorandTireDetectordeclared a string ("torch,networkx"), and eight detectors declared nothing; they now declare["numba", "aeon"](ClapDetector),["numba"](ClaspDetector,EAggloDetector),["torch"](E2USDDetector,Time2StateDetector),["stumpy"](FLUSSDetector),["npbad"](PatssDetector) and["Rbeast"](BeastDetector). A contract test checks the format and that the tag lists the modules the test suite skips a detector on.make testandmake docsrun the test and documentation jobs of the CI (pytest --tb=short -q;sphinx -W -E -b html docs docs/_build/html).PYTEST_ARGSselects tests,DOCS_BUILD_DIRchanges the output directory,PYTHONthe interpreter.Reproduction tests: tests that reproduce a published result on a dataset downloaded on demand (pinned by its SHA-256 digest) go to
tests/reproduction/and are markedreproduction. The marker is deselected by default (pytest -m reproductionruns them); the protocol is intests/reproduction/README.md.GreedyGaussianDetectorreturns the change point indices, as itsreturns_dense=Truetag and its documentation announce: it returned one segment label per time point (0, ..., 0, 1, ..., 1, ...) since 0.2.0, which code reading the output as change point indices misinterpreted.EAggloDetectorreturns anumpyinteger array instead of a Python list, and the deprecatedHMMDetectorreturns integer labels instead of floats.InformationGainDetectorstops its search when every candidate position (the multiples ofstep) is already a change point, and then returns fewer thank_maxchange points: whenk_maxexceeded the number of candidates (ceil(n / step) - 1), it returned-1and0among the change points, for instance[-1, 0, 5, 10, 15, 20, 25]fork_max=10, step=5on 30 points. The other change points are unchanged.Code that no module of the package imports is removed: an unfinished Python port of AutoPlait, older BOCD and TIRE scripts, the former IGTS implementation, a MATLAB-engine wrapper of ESPRESSO, PaTSS scripts that import a
baselinespackage foreign to tsseg, and the MATLAB reference sources of ESPRESSO and iCID. The wheel no longer ships these modules nor the four data files of the MATLAB sources (two.matfiles of the iCID demo, two.txtnotes of ESPRESSO). The deprecated aliasesHdpHsmmDetectorV1,HdpHsmmLegacyDetectorandLegacyHdpHsmmDetectoroftsseg.algorithms.hdp_hsmmare removed: useHdpHsmmDetector.AutoPlaitDetectorreturns the segmentation that the AutoPlait binary writes: each time point gets the number of its regime, so that segments of the same regime share a label, and change points are the first index of a segment. The wrapper read the change points one index early (the last index of the previous segment), took the labels ofsegment.labels(one row per regime, whose second column is the regime’s path in the split tree) and applied them to the segments in time order, padding with the last one: on regimes A-B-A it returned1 | 0 | 0with a single change point. It now raisesRuntimeErrorwhen the binary’s segments do not cover each time point exactly once.AutoPlaitDetectoris tagged unsupervised (capability:unsupervisedTrue,capability:semi_supervisedFalse), as the AutoPlait binary always ran: it chooses the number of segments itself and has no guided mode. It runs withoutn_cpsand withouty; it raisedValueErrorwhen both were missing.n_cpsis deprecated and ignored: passing it,0included, emits aFutureWarningatfitand no longer launches the binary once with a number of segments that it rejected. The output does not change.AutoPlaitDetectorhands the AutoPlait binary a series that it reads with the right length. The binary counts the time points of its input with a line buffer of 254 bytes, and the wrapper wrote 25 characters per value (%.18e): rows of 10 channels or more were counted twice or more, and the binary segmented a longer series padded with rows of zeros (62,442 time points instead of 31,392 on ActRecTut subject 1), whose last segment ended past the series. Values are now written as float32 with 9 significant digits, the precision at which the binary reads them, so that series of up to 15 channels always fit. A series whose rows still do not fit (the 76 channels of JIGSAWS) raisesValueErrorbefore the binary runs, and a binary that reports another length than the series’ raisesRuntimeError. Series of fewer than 10 channels were not affected.The AutoPlait runner (
AutoPlait._run_autoplaitintsseg.algorithms.autoplait.autoplait_c) runs the binary once, with the command line of the bundled binary (autoplait d list_file out_dir), and raisesRuntimeErrorif it fails. It no longer takesn_cps, nor tries a command line withn_cps + 1or with the length of the series: the only binary that takes a second argument, the modified version of the FLOSS project page, reads it as a minimum number of regimes, not of segments. The documentation now points to the original C code of Matsubara et al., whichc/autoplait/holds, instead of the FLOSS page.AutoPlaitDetectorreturns the same output.The output contract tests check what the documentation states: change points are a 1-D integer
numpyarray, strictly increasing, within[1, n - 1]; state labels are a 1-D integernumpyarray of lengthn. They accepted0, duplicates and float labels.Guided mode on a series without any change point:
ClaspDetector(n_change_points=0orn_segments=1),ClapDetector(n_change_points=0),EspressoDetector(n_segments=1),Time2StateDetector,E2USDDetectorandTiccDetector(n_states=1) accept the value and return no change point (a single state); their parameter validation rejected it.AmocDetectorcomputes the error of every split from cumulative sums, in linear time: it recomputed both means at every split (quadratic time). The detected change point is the same.fitandfit_predictreject aygiven as apandas.DataFramethat does not have exactly one column, as the error message states: a frame with two columns (or none) was accepted.ChangeFinderDetectorwithoutn_cps: the data-driven threshold ismean + threshold_factor * stdof the scores (threshold_factor, 2.0 by default as before), the minimum distance between change points can be a fraction of the length (min_distance_fraction), and theensemblingstrategy merges the detections of the channels that lie within the tolerance (by default the minimum distance) and can require a share of the channels to agree (consensus, 0 by default); it returned every distinct peak of every channel. Thel2strategy and the behaviour withn_cpsare unchanged by default.FLUSSDetectoracceptsn_segments=None(number of segments unknown): the regime extraction keeps the minima of the corrected arc curve belowthreshold(0.5), and theensemblingstrategy merges the detections of the channels withintolerance(consensus, the share of the channels that must agree, is 0 by default). A knownn_segmentsbehaves as before.New helper
tsseg.algorithms.utils.consensus_change_points: change points detected by enough channels, without a target count.Documentation matches the code: the tag list of the detectors page (
returns_denseisTruefor change point indices; guided mode is thecapability:semi_supervisedtag), the getting-started guide (examples that run without optional dependencies, guided mode through constructor parameters, real test commands), the datasets page (one bundled dataset, no test fixture to import), the extras table, change point examples that named their outputlabels, and the citation (Springer proceedings).Docstrings:
PeltDetector,BottomUpDetector,WindowDetector,DynpDetectorandKCPDDetectordocument their parameters;BinSegDetectorstates its actualpenaltydefault (10; passpenalty=Noneto stop onepsilon); theHidalgoDetectorexample runs with tsseg instead of aeon, and itsK_statesdefault is 1; theICIDDetectorpsi_listdefault isNone. Thecomputemethods of the state detection metrics and thevalidatemethods of the parameter constraints are documented. A test checks that each detector documents exactly its constructor parameters, with their actual defaults.EAggloDetector:penalty="mean_diff_penalty"no longer raises (the penalty was compiled with numba, which rejects the list of segment boundaries it receives). The documentation now describes the goodness-of-fit statistic, the V-statistics and the merge of the first and last clusters inherited fromecp, and the over-segmentation obtained from one cluster per time point.ClaspDetectorruns whenNUMBA_NUM_THREADSis below the number of cores (as set by users or by SLURM jobs):n_jobs=-1meantos.cpu_count()threads, more than numba accepts, which raisedValueError: The number of threads must be between 1 and ....n_jobs=-1now means all the threads numba may use, and a largern_jobsis capped at that number.TireDetector: the minimum distance between two change points ispeak_distance_fractiontimes the series length, as documented; the default is 0 (no constraint). The prominence threshold of the unsupervised mode is theprominence_thresholdparameter, 0.5 by default.Removed
VQTSSDetectorandVSAXDetector, two methods developed for tsseg without a publication. On a 54-series sample of TSB-SEG, VSAX scored below a random segmentation (bidirectional covering 0.351 against 0.377, ARI 0.163 against 0.208), and VQTSS returned one vector-quantisation code per time step rather than a segmentation (ARI 0.001), with more than 10 minutes per series beyond 2,500 points. Both labelled local patterns rather than regimes. Code that imports them must drop them; they remain in the history of the repository and in tsseg 0.2.0.ProphetDetectornow placesn_candidatesevenly spaced candidates, fits Prophet’s rate changesdeltaand returns then_changepointslargest|delta|relative to their median, or, whenn_changepointsisNone(the new default), those abovedelta_threshold(a relative form of TCPDBench’s rule), at leasttoleranceapart. New parametersn_candidates,changepoint_prior_scale(500 by default, calibrated on TSB-SEG),delta_threshold,seasonality(off by default) andmax_points(longer series are reduced to block means before the fit);n_changepointsnow accepts 0;multivariate_strategydefaults to"l2", and"ensembling"averages the per-channel scores instead of voting.The vendored ruptures solvers have a numba backend for the
l1,l2,rbfandcosinecosts, with the same segmentation as the Python path:backend="auto"(the default) picks it when numba is installed,"numba"and"python"force either path, inPeltDetector,DynpDetector,BinSegDetector,BottomUpDetector,WindowDetectorandKCPDDetector. The kernel costs no longer build the n x n Gram matrix (for PELT and DynP, a transcription of the C solvers of ruptures’KernelCPD); DynP no longer memoises O((n / jump)^2) sub-problems; BinSeg finds the best split of a segment in one sweep from each end.SNLDSDetectoris now shipped with the package. It was listed intsseg.algorithmswithout its module, sofrom tsseg.algorithms import *failed on the 0.2.0 wheel. The authors’ TensorFlow code (Google Research, Apache-2.0) is vendored intsseg/algorithms/snlds/vendor; install the dependencies withpip install tsseg[snlds]. Its constructor is now keyword-only, and its tags declare it semi-supervised (n_statesis the number of regimes) rather than unsupervised. The wheel now also ships theLICENSEfiles of the vendored code.CostRbf: the median heuristic no longer counts the distance of a sample to itself, whose rounding residues shifted gamma by about 1e-4, and no longer builds the Gram matrix.Ties: every vendored solver treats values closer than
1e-9of the costs involved (at least of the signal’s cost per sample,tie_unit) as equal, and decides between them by position as upstream decides between exactly equal values: earliest change point (PELT, DynP), last change point of a segment and first segment (BinSeg), leftmost merge (BottomUp), no peak on a plateau and latest tied peak first (Window); a value tied withpenorepsilonstops, and PELT keeps a start tied with its pruning bound (pruning less never loses the optimum).PatssDetectoris deprecated (FutureWarning) and will be removed in a future release: it needsnpbad, which is not on PyPI, and over-segments (17 times more change points than the ground truth on a 54-series sample of TSB-SEG). The authors maintain PaTSEmb, which needs Java.WindowDetectorsearches its peaks as ruptures does (argrelmax(mode="wrap")): the vendored peak search cut the neighbourhood of a score at the ends of the signal, and could add a change point next to the first or last window.The vendored ruptures costs convert the signal to float64 in
fit: float32 signals were costed in float32, andCostCosinefailed on integers.BeastDetectoruses theRbeastpackage from PyPI, version 0.1.25 or later (new extratsseg[beast]), instead of the copy of Rbeast 0.1.15 vendored inc/Rbeast, which never returned on univariate series longer than about 4,600 points. Change points were one sample late (Rbeast counts time from 1); they are now the index of the first sample of the new segment.mcmc_seeddefaults to 1: with 0, Rbeast draws a random seed and the output changed from one call to the next.Third-party licenses:
bocd/,ticc/,time2state/and the aeon-derivedeagglo/,hidalgo/,hmm/andigts/now ship a copy of their upstream license, in the source tree and the wheel. The code adapted inbocd/is under the MIT license, not Apache-2.0 as documented. The README listsBeastDetector’s dependency Rbeast, under the license of its R package (GPL >= 2), and no longer claims that every vendored directory contains a license file.pen_scale="bic"inPeltDetector,BinSegDetector,BottomUpDetectorandWindowDetector: the penalty is a coefficient onlog(n) * u, u the cost of one sample of the whole signal (d forl2on unit-variance channels, where it is the BIC penalty; the mean absolute deviation forl1; 1 for the kernel costs), so that one value applies across lengths, dimensions and costs.New
GaussianHMMDetector: a hidden Markov model with diagonal Gaussian emissions, fitted by Baum-Welch (EM) from k-means initialisations and decoded by Viterbi. The number of states is given (n_states) or chosen by BIC.HMMDetector, which only decoded with user-supplied parameters, is deprecated (FutureWarning).BinSegDetector,BottomUpDetectorandWindowDetectoracceptn_cps=0again (no change point), asDynpDetectorandKCPDDetectordo: the parameter schema requiredn_cps >= 1, which failed the semi-supervised runs on series without change points.SNLDSDetectorfollows the training schedules of the paper: the temperature of the discrete transitions and the weight of the cross-entropy regulariser start at 1,000 (temperature_init,xent_init) and decay as in the Reacher experiment, scaled ton_train_steps. Before, both stayed inactive (temperature 1, weight 1e-10). Training drawsbatch_sizewindows oftrain_windowpoints per step (new parameter, default 256;Nonefor the whole series), so a step no longer runs over the whole series, and the defaults arebatch_size=8andn_train_steps=2000. Prediction is compiled, about 100 times faster on MoCap. The detector declares a parameter schema and thenon_deterministictag, and no longer reseeds NumPy’s global generator. SNLDS is documented as experimental: on a sample of TSB-SEG it scores below a random segmentation.KCPDDetectoris deprecated (FutureWarning): it isPeltDetectororDynpDetectorwith a kernel cost (model="rbf","cosine", or"l2"for the linear kernel).PatssDetectorruns with scikit-learn 1.8, which removedLogisticRegression(multi_class=...)and raised aTypeError: the one-vs-rest elastic-net logistic regression of PaTSS is nowOneVsRestClassifier(LogisticRegression(...)), the same model (predicted probabilities equal to 1e-15 under scikit-learn 1.6).F1Scoregives 1.0 when neither the ground truth nor the prediction has a change point and the series length is read fromy_true:F1Score().compute([n], [])gave 0.0, because the empty prediction was rejected before the boundaries were set aside (compute([n], [n])andcompute([n], [], n_timepoints=n)already gave 1.0). Scores computed from labels (convert_labels_to_segments=True) are unchanged. The warning about a margin rounded to 0 samples is only emitted when change points are matched.F1Score,Covering,BidirectionalCoveringandGaussianF1Scoreaccept the series length incompute(y_true, y_pred, n_timepoints=n); the change points may then include or omit0andn, so the raw output of a detector can be scored directly. Withoutn_timepoints(and withoutconvert_labels_to_segments=True), the length is still read from the change points, scores are unchanged, and aUserWarningnow says so: ay_truethat does not end with the series length silently lost its last change point (F1Score(margin=5).compute([300, 700], [300])gives 1.0; withn_timepoints=1000, 0.667).Detector pages give the time complexity of 18 more detectors, as implemented with their default settings. Two values were wrong: PELT is \(O(C\,n)\) expected and \(O(C\,n^{2})\) at worst, with no factor in the number of change points (the page said \(O(C\,K\,n)\)), and ChangeFinder solves one \(k \times k\) system per sample, so \(O(d\,n + n\,k^{3})\) (the page said \(O(n\,k^{2})\)).
The tGLAD page gives the default of
epochs(100; it said 2000) and explains why the paper reports \(O(n)\) where the page states \(O(d^{3}\,n)\) per epoch. TheTGLADDetectordocstring links to the authors’ repository (Harshs27/tGLAD).
0.2.0 (2026-09-26)
Replaced the NumPy
HdpHsmmDetectorwith a corrected sampler equivalent topyhsmm’sWeakLimitHDPHSMM(auxiliary self-transitions of Johnson & Willsky, right-censored last segment, chain started from the prior, numba forward messages) and calibrated defaults.numbais optional again, astsseg[accelerators]announces:ClapDetector,ClaspDetectorandEAggloDetectorare imported lazily (they need numba), andHdpHsmmDetectorfalls back to interpreted code without it.EAggloDetectorandGreedyGaussianDetectordeclarereturns_dense=True, consistent with their change point output.KCPDDetector:decimationparameter,pen_scale="bic"(penalty as a coefficient onlog(n) * d), andn_cps=0accepted.
0.1.1 (2026-04-20)
Added
BeastDetectorandChangeFinderDetector.InformationGainDetector(IGTS) extended to univariate series, with the optimisation of the paper.Parameter schemas for all detectors; GF1 score exposed.
Fixes to GLAD, Hidalgo and VSAX; continuous integration (lint, tests, docs).
0.1.0 (2026-02-10)
Initial public release.
Vendored a lightweight subset of
rupturesv1.1.8 — BinSeg, BottomUp, DynP, KCPD, PELT and Window detectors no longer need an external dependency.Added
BottomUpDetectorandDynpDetectorwrappers with aeon-compatible interfaces.Replaced the surrogate PyTorch
TSCP2Detectorwith a faithful TensorFlow + TCN implementation matching the original baseline.Replaced the
pyhsmm-backedHdpHsmmDetectorwith a native NumPy/SciPy sticky HDP implementation.pyhsmmandpybasicbayesare no longer installed by default.Improved
ClaspDetectoraxis handling.Added Sphinx documentation with GitHub Pages deployment.
Documented test suite layout and fixtures.