From da5d551613dc85d6617d6953fa5e847afffbe8ac Mon Sep 17 00:00:00 2001 From: MatthewMiddlehurst Date: Sun, 19 Jul 2026 22:20:51 +0100 Subject: [PATCH 1/4] early release PR --- README.md | 52 +++++++++++++++++--------- aeon/__init__.py | 2 +- docs/changelog.md | 1 + docs/changelogs/v1.5.md | 81 +++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 5 files changed, 118 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 26a46f4b39..66ec67a5d6 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@

`aeon` is a scikit-learn compatible Python library for learning from time series. -It covers classification, regression, clustering, forecasting, anomaly detection, distances, -segmentation, similarity search, transformations and benchmarking. +It covers classification, regression, clustering, forecasting, anomaly detection, +distances, segmentation, similarity search, transformations and benchmarking. -Many implementations in `aeon` are contributed and maintained by the researchers who developed the original methods. These include state-of-the-art models for forecasting, classification, regression, and clustering, including deep learning approaches. +Many implementations in `aeon` are contributed and maintained by the researchers who +developed the original methods. These include state-of-the-art models for forecasting, +classification, regression, and clustering, including deep learning approaches. [Documentation](https://www.aeon-toolkit.org/) · [Examples](https://www.aeon-toolkit.org/en/stable/examples.html) · @@ -54,7 +56,6 @@ evaluate new methods. That means: - **State of the art, sooner.** New methods often land in `aeon` alongside publication. - **Evidence-based defaults.** What's included — and what's recommended — is grounded in published comparative studies. - A selection of algorithms available in `aeon` written by `aeon` core developers or contributors: | Method | Reference | Task | @@ -70,7 +71,6 @@ A selection of algorithms available in `aeon` written by `aeon` core developers Code in `aeon` and related toolkits has been used in a wide range of benchmarking studies: - | Study | Reference | Area | |-----------------------------------|-------------------------------------------------------------------------------------------|--------------| | Clustering | [Holder et al., 2024](https://link.springer.com/article/10.1007/s10115-023-01952-0) | Benchmarking | @@ -102,8 +102,12 @@ pip install aeon[all_extras] For development installs and platform-specific notes, see the [installation guide](https://www.aeon-toolkit.org/en/stable/installation.html). +The latest version of `aeon` is v1.5.1. + ## Quick start + Fit a classifier on a standard UCR dataset: + ```python from aeon.classification.convolution_based import RocketClassifier from aeon.datasets import load_gunpoint @@ -136,9 +140,13 @@ Ten task areas, one consistent API: ## Getting started examples +For more examples across tasks, visit the +[examples gallery](https://www.aeon-toolkit.org/en/stable/examples.html). + ### Classification -Time series classification predicts class labels for unseen series using a model fitted on a collection of labelled time series. +Time series classification predicts class labels for unseen series using a model fitted +on a collection of labelled time series. ```python import numpy as np @@ -165,7 +173,6 @@ print(y_pred) # ['low' 'low' 'high'] ``` - ### Clustering Time series clustering groups similar time series together from an unlabelled collection. @@ -197,7 +204,8 @@ pred = forecaster.forecast(y) print(pred) ``` -For more advanced forecasting, `aeon` also includes deep learning and machine learning methods not available elsewhere in Python, such as `SETARTree` and `SETARForest`. +For more advanced forecasting, `aeon` also includes deep learning and machine learning +methods not available elsewhere in Python, such as `SETARTree` and `SETARForest`. ### Deep learning @@ -227,9 +235,6 @@ print(clf.score(X_test, y_test)) See the [examples gallery](https://www.aeon-toolkit.org/en/stable/examples.html) for GPU usage, custom architectures, and benchmarking against classical methods. -For more examples across tasks, visit the -[examples gallery](https://www.aeon-toolkit.org/en/stable/examples.html). - ## Support aeon There are several ways to engage with the project: @@ -255,7 +260,8 @@ Useful links: - [Governance](https://github.com/aeon-toolkit/aeon/blob/main/GOVERNANCE.md) - [Project website](https://www.aeon-toolkit.org/) -The `aeon` developers are volunteers, so please be patient with issue triage and pull request review. +The `aeon` developers are volunteers, so please be patient with issue triage and +pull request review. ## Citation @@ -274,16 +280,26 @@ If you use `aeon` in academic work, please cite the project: } ``` -If you let us know about your paper using `aeon`, we will happily list it on the [project website](https://www.aeon-toolkit.org/en/latest/papers_using_aeon.html). +If you let us know about your paper using `aeon`, we will happily list it on +the [project website](https://www.aeon-toolkit.org/en/latest/papers_using_aeon.html). ## Project history -`aeon` was forked from `sktime` `v0.16.0` in 2022 by an initial group of eight core developers, and has since been substantially rewritten and extended. -Our core development team of 13 spans academia and industry, representing seven nationalities across the globe. -You can read more about the project's history, values, and governance on the [About Us page](https://www.aeon-toolkit.org/en/stable/about.html). +`aeon` was forked from `sktime` `v0.16.0` in 2022 by an initial group of eight core +developers, and has since been substantially rewritten and extended. +Our core development team of 13 spans academia and industry, representing seven +nationalities across the globe. +You can read more about the project's history, values, and governance on the +[About Us page](https://www.aeon-toolkit.org/en/stable/about.html). ## Project status -`aeon` is under active development. The core package is stable and widely used. The following modules are currently considered in development, and the deprecation policy does not necessarily apply (although we only rarely make non-compatible changes): `anomaly_detection`, `forecasting`, `segmentation`, `similarity_search`, `visualisation`, `transformations.collection.self_supervised`, `transformations.collection.imbalance`. +`aeon` is under active development. The core package is stable and widely used. +The following modules are currently considered in development, and the deprecation +policy does not necessarily apply (although we only rarely make non-compatible changes): +`anomaly_detection`, `forecasting`, `segmentation`, `similarity_search`, +`visualisation`, `transformations.collection.self_supervised`, +`transformations.collection.imbalance`. -Please check the documentation for task-specific capabilities, limitations, and current status. +Please check the documentation for task-specific capabilities, limitations, and +current status. diff --git a/aeon/__init__.py b/aeon/__init__.py index ea13204dc9..43ca59f653 100644 --- a/aeon/__init__.py +++ b/aeon/__init__.py @@ -1,3 +1,3 @@ """aeon toolkit.""" -__version__ = "1.5.0" +__version__ = "1.5.1" diff --git a/docs/changelog.md b/docs/changelog.md index 5f53eef19d..199fc57c74 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -9,6 +9,7 @@ To stay up to date with `aeon` releases, subscribe to aeon [here](https://libraries.io/pypi/aeon) or follow us on [LinkedIn](https://www.linkedin.com/company/aeon-toolkit/). +- [Version 1.5.1](changelogs/v1.5.md) - [Version 1.5.0](changelogs/v1.5.md) - [Version 1.4.0](changelogs/v1.4.md) - [Version 1.3.0](changelogs/v1.3.md) diff --git a/docs/changelogs/v1.5.md b/docs/changelogs/v1.5.md index d93519bac0..c902e38217 100644 --- a/docs/changelogs/v1.5.md +++ b/docs/changelogs/v1.5.md @@ -1,3 +1,84 @@ +# v1.5.1 + +July 2026 + +## Anomaly Detection + +- [ENH] Changes left stampi so fit_is_empty ({pr}`3591`) {user}`TonyBagnall` + +## Classification + +- [BUG] Relegate flat test to a warning ({pr}`3598`) {user}`TonyBagnall` +- [BUG] Fix testing sklearn CV splitter to not have empty splits ({pr}`3608`) {user}`TonyBagnall` +- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` +- [ENH] Speedup BOSS/cBOSS (up to 7x speedup, average 2x-3x) by vectorizing sparse matrix distance computations ({pr}`3640`) {user}`patrickzib` +- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` + +## Clustering + +- [ENH] add time series agglomerative clusterer ({pr}`3553`) {user}`SotonSweetXss` +- [BUG] KASBA fix and clustering tests ({pr}`3604`) {user}`TonyBagnall` +- [BUG] Fix SCUM predict refitting ({pr}`3581`) {user}`TonyBagnall` +- [BUG] NaiveForecaster: validate seasonal_period for strategy='seasonal_last' ({pr}`3615`) {user}`CedricConday` +- [BUG] AutoARIMA: respect max_d in the differencing loop ({pr}`3614`) {user}`CedricConday` +- [ENH] Better examples for CES and AutoCES ({pr}`3592`) {user}`GiGiKoneti` +- [MNT] Enhanced forecasting tests ({pr}`3580`) {user}`TonyBagnall` +- [MNT] Forecasting test coverage ({pr}`3602`) {user}`TonyBagnall` + +## Regression + +- [BUG] Fix sporadic fail for Inception time file save ({pr}`3601`) {user}`TonyBagnall` +- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` +- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` + +## Similarity Search + +- [MNT] Similarity search cleanup ({pr}`3222`) {user}`baraline` + +## Transformations + +- [BUG] Padder: validate fill_value and reject array-likes (#3495) ({pr}`3585`) {user}`CedricConday` +- [BUG] Fix nightly tests related to SFA failing ({pr}`3612`) {user}`patrickzib` +- [BUG] fix outlier_norm using wrong feature indices in pycatch22 path ({pr}`3638`) {user}`BB0813` +- [BUG] fix uncommented njit decorator in sfa_fast ({pr}`3634`) {user}`patrickzib` +- [ENH] Improved coverage for channel selectors ({pr}`3522`) {user}`TonyBagnall` +- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` +- [ENH] Parallelize SAX and PAA transformers ({pr}`2980`) {user}`aadya940` +- [ENH] speed up RandomShapeletTransform ({pr}`3574`) {user}`TonyBagnall` +- [MNT] Deprecation of StatsModelsACF and StatsModelsPACF ({pr}`3653`) {user}`TonyBagnall` +- [DOC] Fix unequal length transformer docstrings ({pr}`3573`) {user}`kiwoongyoon` +- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` + +## Unit Testing + +- [MNT] Enhanced forecasting tests ({pr}`3580`) {user}`TonyBagnall` +- [MNT] Similarity search cleanup ({pr}`3222`) {user}`baraline` +- [MNT] Use Agg backend in shapelet plotting tests ({pr}`3611`) {user}`TonyBagnall` + +## Other + +- [BUG] Uniformize kernelspace name and display_name across notebooks ({pr}`3636`) {user}`baraline` +- [DOC] Update changelog.md for 1.5.0 and make requirement to edit this file more explicit ({pr}`3597`) {user}`TonyBagnall` +- [ENH] Add parameters to method timer and stop `fit` timer from overwriting the attribute ({pr}`3656`) {user}`MatthewMiddlehurst` +- [MNT] Upgrade Python version to 3.13 in ReadTheDocs config ({pr}`3635`) {user}`MatthewMiddlehurst` +- [MNT] Fix n_jobs > 3 in code speed notebook causing CI failures ({pr}`3639`) {user}`baraline` +- [MNT] Improve coverage for base package ({pr}`3516`) {user}`TonyBagnall` + +## Contributors + +The following have contributed to this release through a collective 32 GitHub Pull Requests: + +{user}`aadya940`, +{user}`baraline`, +{user}`BB0813`, +{user}`CedricConday`, +{user}`GiGiKoneti`, +{user}`kiwoongyoon`, +{user}`MatthewMiddlehurst`, +{user}`patrickzib`, +{user}`SotonSweetXss`, +{user}`TonyBagnall` + # v1.5.0 June 2026 diff --git a/pyproject.toml b/pyproject.toml index 74749034c4..3b303c0ea9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aeon" -version = "1.5.0" +version = "1.5.1" description = "A toolkit for time series machine learning" authors = [ {name = "aeon developers", email = "contact@aeon-toolkit.org"}, From 646c6e08ac3ab1fe5a91d400558c22fa06ab56a1 Mon Sep 17 00:00:00 2001 From: MatthewMiddlehurst Date: Mon, 20 Jul 2026 20:48:42 +0100 Subject: [PATCH 2/4] minor instead of patch --- README.md | 2 +- aeon/__init__.py | 2 +- docs/changelog.md | 2 +- docs/changelogs/v1.5.md | 81 ----------------------------------------- docs/changelogs/v1.6.md | 3 ++ pyproject.toml | 2 +- 6 files changed, 7 insertions(+), 85 deletions(-) create mode 100644 docs/changelogs/v1.6.md diff --git a/README.md b/README.md index 66ec67a5d6..ff57ca41bb 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ pip install aeon[all_extras] For development installs and platform-specific notes, see the [installation guide](https://www.aeon-toolkit.org/en/stable/installation.html). -The latest version of `aeon` is v1.5.1. +The latest version of `aeon` is v1.6.0. ## Quick start diff --git a/aeon/__init__.py b/aeon/__init__.py index 43ca59f653..94ffcd1314 100644 --- a/aeon/__init__.py +++ b/aeon/__init__.py @@ -1,3 +1,3 @@ """aeon toolkit.""" -__version__ = "1.5.1" +__version__ = "1.6.0" diff --git a/docs/changelog.md b/docs/changelog.md index 199fc57c74..e0a1428a92 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -9,7 +9,7 @@ To stay up to date with `aeon` releases, subscribe to aeon [here](https://libraries.io/pypi/aeon) or follow us on [LinkedIn](https://www.linkedin.com/company/aeon-toolkit/). -- [Version 1.5.1](changelogs/v1.5.md) +- [Version 1.6.0](changelogs/v1.6.md) - [Version 1.5.0](changelogs/v1.5.md) - [Version 1.4.0](changelogs/v1.4.md) - [Version 1.3.0](changelogs/v1.3.md) diff --git a/docs/changelogs/v1.5.md b/docs/changelogs/v1.5.md index c902e38217..d93519bac0 100644 --- a/docs/changelogs/v1.5.md +++ b/docs/changelogs/v1.5.md @@ -1,84 +1,3 @@ -# v1.5.1 - -July 2026 - -## Anomaly Detection - -- [ENH] Changes left stampi so fit_is_empty ({pr}`3591`) {user}`TonyBagnall` - -## Classification - -- [BUG] Relegate flat test to a warning ({pr}`3598`) {user}`TonyBagnall` -- [BUG] Fix testing sklearn CV splitter to not have empty splits ({pr}`3608`) {user}`TonyBagnall` -- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` -- [ENH] Speedup BOSS/cBOSS (up to 7x speedup, average 2x-3x) by vectorizing sparse matrix distance computations ({pr}`3640`) {user}`patrickzib` -- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` - -## Clustering - -- [ENH] add time series agglomerative clusterer ({pr}`3553`) {user}`SotonSweetXss` -- [BUG] KASBA fix and clustering tests ({pr}`3604`) {user}`TonyBagnall` -- [BUG] Fix SCUM predict refitting ({pr}`3581`) {user}`TonyBagnall` -- [BUG] NaiveForecaster: validate seasonal_period for strategy='seasonal_last' ({pr}`3615`) {user}`CedricConday` -- [BUG] AutoARIMA: respect max_d in the differencing loop ({pr}`3614`) {user}`CedricConday` -- [ENH] Better examples for CES and AutoCES ({pr}`3592`) {user}`GiGiKoneti` -- [MNT] Enhanced forecasting tests ({pr}`3580`) {user}`TonyBagnall` -- [MNT] Forecasting test coverage ({pr}`3602`) {user}`TonyBagnall` - -## Regression - -- [BUG] Fix sporadic fail for Inception time file save ({pr}`3601`) {user}`TonyBagnall` -- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` -- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` - -## Similarity Search - -- [MNT] Similarity search cleanup ({pr}`3222`) {user}`baraline` - -## Transformations - -- [BUG] Padder: validate fill_value and reject array-likes (#3495) ({pr}`3585`) {user}`CedricConday` -- [BUG] Fix nightly tests related to SFA failing ({pr}`3612`) {user}`patrickzib` -- [BUG] fix outlier_norm using wrong feature indices in pycatch22 path ({pr}`3638`) {user}`BB0813` -- [BUG] fix uncommented njit decorator in sfa_fast ({pr}`3634`) {user}`patrickzib` -- [ENH] Improved coverage for channel selectors ({pr}`3522`) {user}`TonyBagnall` -- [ENH] Speed up interval-based forests (BaseIntervalForest / RandomIntervals) ({pr}`3620`) {user}`TonyBagnall` -- [ENH] Parallelize SAX and PAA transformers ({pr}`2980`) {user}`aadya940` -- [ENH] speed up RandomShapeletTransform ({pr}`3574`) {user}`TonyBagnall` -- [MNT] Deprecation of StatsModelsACF and StatsModelsPACF ({pr}`3653`) {user}`TonyBagnall` -- [DOC] Fix unequal length transformer docstrings ({pr}`3573`) {user}`kiwoongyoon` -- [ENH] Speed up DrCIF part 1: Optimise Catch22 transformer ({pr}`3618`) {user}`TonyBagnall` - -## Unit Testing - -- [MNT] Enhanced forecasting tests ({pr}`3580`) {user}`TonyBagnall` -- [MNT] Similarity search cleanup ({pr}`3222`) {user}`baraline` -- [MNT] Use Agg backend in shapelet plotting tests ({pr}`3611`) {user}`TonyBagnall` - -## Other - -- [BUG] Uniformize kernelspace name and display_name across notebooks ({pr}`3636`) {user}`baraline` -- [DOC] Update changelog.md for 1.5.0 and make requirement to edit this file more explicit ({pr}`3597`) {user}`TonyBagnall` -- [ENH] Add parameters to method timer and stop `fit` timer from overwriting the attribute ({pr}`3656`) {user}`MatthewMiddlehurst` -- [MNT] Upgrade Python version to 3.13 in ReadTheDocs config ({pr}`3635`) {user}`MatthewMiddlehurst` -- [MNT] Fix n_jobs > 3 in code speed notebook causing CI failures ({pr}`3639`) {user}`baraline` -- [MNT] Improve coverage for base package ({pr}`3516`) {user}`TonyBagnall` - -## Contributors - -The following have contributed to this release through a collective 32 GitHub Pull Requests: - -{user}`aadya940`, -{user}`baraline`, -{user}`BB0813`, -{user}`CedricConday`, -{user}`GiGiKoneti`, -{user}`kiwoongyoon`, -{user}`MatthewMiddlehurst`, -{user}`patrickzib`, -{user}`SotonSweetXss`, -{user}`TonyBagnall` - # v1.5.0 June 2026 diff --git a/docs/changelogs/v1.6.md b/docs/changelogs/v1.6.md new file mode 100644 index 0000000000..e265142962 --- /dev/null +++ b/docs/changelogs/v1.6.md @@ -0,0 +1,3 @@ +# v1.6.0 + +July 2026 diff --git a/pyproject.toml b/pyproject.toml index 3b303c0ea9..5c9ca83ea9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aeon" -version = "1.5.1" +version = "1.6.0" description = "A toolkit for time series machine learning" authors = [ {name = "aeon developers", email = "contact@aeon-toolkit.org"}, From ac3e4ef6af4fa94b4fe5d16ec5490d58e12e616d Mon Sep 17 00:00:00 2001 From: MatthewMiddlehurst Date: Thu, 23 Jul 2026 12:39:32 +0100 Subject: [PATCH 3/4] deprecate LS --- .github/workflows/fast_release.yml | 1 + .../classification/shapelet_based/__init__.py | 2 - aeon/classification/shapelet_based/_ls.py | 246 ------------------ .../shapelet_based/tests/test_ls.py | 31 --- .../_write_estimator_results.py | 1 - docs/api_reference/classification.rst | 1 - docs/changelogs/v1.6.md | 6 + examples/classification/classification.ipynb | 2 - examples/classification/shapelet_based.ipynb | 8 +- 9 files changed, 8 insertions(+), 290 deletions(-) delete mode 100644 aeon/classification/shapelet_based/_ls.py delete mode 100644 aeon/classification/shapelet_based/tests/test_ls.py diff --git a/.github/workflows/fast_release.yml b/.github/workflows/fast_release.yml index 99405f7c83..ca49f4d73b 100644 --- a/.github/workflows/fast_release.yml +++ b/.github/workflows/fast_release.yml @@ -29,6 +29,7 @@ jobs: retention-days: 5 upload-wheels: + needs: build-project runs-on: ubuntu-24.04 environment: diff --git a/aeon/classification/shapelet_based/__init__.py b/aeon/classification/shapelet_based/__init__.py index 3b76ddddec..dc99c70cf9 100644 --- a/aeon/classification/shapelet_based/__init__.py +++ b/aeon/classification/shapelet_based/__init__.py @@ -5,10 +5,8 @@ "RDSTClassifier", "SASTClassifier", "RSASTClassifier", - "LearningShapeletClassifier", ] -from aeon.classification.shapelet_based._ls import LearningShapeletClassifier from aeon.classification.shapelet_based._rdst import RDSTClassifier from aeon.classification.shapelet_based._rsast import RSASTClassifier from aeon.classification.shapelet_based._sast import SASTClassifier diff --git a/aeon/classification/shapelet_based/_ls.py b/aeon/classification/shapelet_based/_ls.py deleted file mode 100644 index d5eb2be68a..0000000000 --- a/aeon/classification/shapelet_based/_ls.py +++ /dev/null @@ -1,246 +0,0 @@ -"""A Learning Shapelet classifier (LSC). - -Learning shapelet classifier that simply wraps the LearningShapelet class from tslearn. -""" - -__maintainer__ = ["MatthewMiddlehurst"] -__all__ = ["LearningShapeletClassifier"] - - -import numpy as np -from deprecated.sphinx import deprecated - -from aeon.classification.base import BaseClassifier - - -def _X_transformed_tslearn(X): - if X.ndim == 3: - X_transformed = np.transpose(X, (0, 2, 1)) - elif X.ndim == 2: - X_transformed = np.transpose(X) - return X_transformed - - -# TODO: remove in v1.6.0 -@deprecated( - version="1.5.0", - reason=( - "LearningShapeletClassifier is deprecated and will be removed in v1.6.0. " - "Use other shapelet-based classifiers such as ShapeletTransformClassifier " - "or RDSTClassifier instead." - ), - category=FutureWarning, -) -class LearningShapeletClassifier(BaseClassifier): - """ - Learning Shapelet classifier. - - Deprecated and will be removed in v1.6.0. Use other shapelet-based - classifiers such as :class:`ShapeletTransformClassifier` or - :class:`RDSTClassifier` instead. - - This is a wrapper for the `LearningShapelet` class of `tslearn`. - Learning Shapelet classifier, presented in [1]_, operates by - identifying discriminative subsequences, called shapelets, within - the input time series data. These shapelets are representative patterns - that capture essential characteristics of different classes or categories - within the data. - - Parameters - ---------- - n_shapelets_per_size: dict, default=None - Dictionary giving, for each shapelet size (key), - the number of such shapelets to be trained (value). - If None, `grabocka_params_to_shapelet_size_dict` is used and the - size used to compute is that of the shortest time series passed at fit - time. - max_iter: int, default=10000 - Number of training epochs. - batch_size: int, default=256 - Batch size to be used. - verbose: {0, 1, 2}, default=0 - `keras` verbose level. - optimizer: str or keras.optimizers.Optimizer, default="sgd" - `keras` optimizer to use for training. - weight_regularizer: float or None, default=0.0 - Strength of the L2 regularizer to use for training the classification - (softmax) layer. If 0, no regularization is performed. - shapelet_length: float, default=0.15 - The length of the shapelets, expressed as a fraction of the time - series length. - Used only if `n_shapelets_per_size` is None. - total_lengths: int, default=3 - The number of different shapelet lengths. Will extract shapelets of - length i * shapelet_length for i in [1, total_lengths] - Used only if `n_shapelets_per_size` is None. - max_size: int or None, default=None - Maximum size for time series to be fed to the model. If None, it is - set to the size (number of timestamps) of the training time series. - scale: bool, default=False - Whether input data should be scaled for each feature of each time - series to lie in the [0-1] interval. Default for this parameter is set to - `False`. - random_state : int or None, default=None - The seed of the pseudo random number generator to use when shuffling - the data. If int, random_state is the seed used by the random number - generator; If None, the random number generator is the RandomState - instance used by `np.random`. - save_transformed_data: bool = False, - Whether to save the transformed data for later use in the internal variable - ``self.transformed_data_``. - - References - ---------- - .. Grabocka, J., Schilling, N., Wistuba, M. and Schmidt-Thieme, L., 2014, August. - Learning time-series shapelets. In Proceedings of the 20th ACM SIGKDD - international conference on Knowledge discovery and data mining (pp. 392-401). - - Examples - -------- - >>> from aeon.classification.shapelet_based import LearningShapeletClassifier - >>> from aeon.testing.data_generation import make_example_3d_numpy - >>> X, y = make_example_3d_numpy(random_state=0) - >>> clf = LearningShapeletClassifier(max_iter=50, random_state=0) # doctest: +SKIP - >>> clf.fit(X, y) # doctest: +SKIP - MrSQMClassifier(...) - >>> clf.predict(X) # doctest: +SKIP - """ - - _tags = { - "capability:multivariate": True, - "algorithm_type": "shapelet", - "cant_pickle": True, - "python_dependencies": ["tslearn", "tensorflow"], - } - - def __init__( - self, - n_shapelets_per_size: dict | None = None, - max_iter: int = 10000, - batch_size: int = 256, - verbose: int = 0, - optimizer: str = "sgd", - weight_regularizer: float | None = 0.0, - shapelet_length: float = 0.15, - total_lengths: int = 3, - max_size: int | None = None, - scale: bool = False, - random_state: int | None = None, - save_transformed_data: bool = False, - ) -> None: - self.n_shapelets_per_size = n_shapelets_per_size - self.max_iter = max_iter - self.batch_size = batch_size - self.verbose = verbose - self.optimizer = optimizer - self.weight_regularizer = weight_regularizer - self.shapelet_length = shapelet_length - self.total_lengths = total_lengths - self.max_size = max_size - self.scale = scale - self.random_state = random_state - self.save_transformed_data = save_transformed_data - - super().__init__() - - def _fit(self, X, y): - from tslearn.shapelets import LearningShapelets - - self.clf_ = LearningShapelets( - n_shapelets_per_size=self.n_shapelets_per_size, - max_iter=self.max_iter, - batch_size=self.batch_size, - verbose=self.verbose, - optimizer=self.optimizer, - weight_regularizer=self.weight_regularizer, - shapelet_length=self.shapelet_length, - total_lengths=self.total_lengths, - max_size=self.max_size, - scale=self.scale, - random_state=self.random_state, - ) - X_t = _X_transformed_tslearn(X) - self.clf_.fit(X_t, y) - if self.save_transformed_data: - self.transformed_data_ = X_t - - return self - - def _predict(self, X) -> np.ndarray: - X_t = _X_transformed_tslearn(X) - return self.clf_.predict(X_t) - - def _predict_proba(self, X) -> np.ndarray: - X_t = _X_transformed_tslearn(X) - return self.clf_.predict_proba(X_t) - - def get_transform(self, X): - """Return shapelet transform for a set of time series. - - Parameters - ---------- - X : array-like of shape=(n_ts, sz, d) - Time series dataset. - - Returns - ------- - array of shape=(n_ts, n_shapelets) - Shapelet-Transform of the provided time series. - """ - if not self.is_fitted: - raise ValueError( - "You must fit the classifier before recovering the transform" - ) - if not self.save_transformed_data: - raise ValueError( - "Set save_transformed_data=True in the constructor to save the " - "transformed data for later use" - ) - return self.transformed_data_ - - def get_locations(self, X): - """Compute shapelet match location for a set of time series. - - Parameters - ---------- - X : array-like of shape=(n_ts, sz, d) - Time series dataset. - - Returns - ------- - array of shape=(n_ts, n_shapelets) - Location of the shapelet matches for the provided time series. - """ - if not self.is_fitted: - raise ValueError( - "You must fit the classifier before recovering the transform" - ) - if not self.save_transformed_data: - raise ValueError( - "Set save_transformed_data=True in the constructor to save the " - "transformed data for later use" - ) - return self.clf_.locate(self.transformed_data_) - - @classmethod - def _get_test_params(cls, parameter_set: str = "default") -> dict | list[dict]: - """Return testing parameter settings for the estimator. - - Parameters - ---------- - parameter_set : str, default="default" - Name of the set of test parameters to return, for use in tests. If no - special parameters are defined for a value, will return `"default"` set. - For classifiers, a "default" set of parameters should be provided for - general testing, and a "results_comparison" set for comparing against - previously recorded results if the general set does not produce suitable - probabilities to compare against. - - Returns - ------- - params : dict or list of dict, default={} - Parameters to create testing instances of the class. - Each dict are parameters to construct an "interesting" test instance, i.e., - `MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance. - """ - return {"max_iter": 10, "batch_size": 10, "total_lengths": 1} diff --git a/aeon/classification/shapelet_based/tests/test_ls.py b/aeon/classification/shapelet_based/tests/test_ls.py deleted file mode 100644 index 02c86f9b5d..0000000000 --- a/aeon/classification/shapelet_based/tests/test_ls.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Learned Shapelets tests.""" - -import numpy as np -import pytest - -from aeon.classification.shapelet_based import LearningShapeletClassifier -from aeon.testing.data_generation import make_example_3d_numpy -from aeon.utils.validation._dependencies import _check_soft_dependencies - - -@pytest.mark.skipif( - not _check_soft_dependencies(["tslearn", "tensorflow"], severity="none"), - reason="skip test if required soft dependency not available", -) -def test_get_transform(): - """Learned Shapelets tests not covered by standard test suite.""" - X = make_example_3d_numpy(return_y=False, n_cases=10, n_timepoints=20) - y = np.array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1]) - - # Test get transform and location with and without save_transformed_data - with pytest.warns(FutureWarning, match="LearningShapeletClassifier"): - clf = LearningShapeletClassifier( - max_iter=10, total_lengths=1, save_transformed_data=True - ) - with pytest.raises(ValueError): - clf.get_transform(X) - with pytest.raises(ValueError): - clf.get_locations(X) - clf.fit(X, y) - t = clf.get_transform(X) - assert isinstance(t, np.ndarray) diff --git a/aeon/testing/expected_results/_write_estimator_results.py b/aeon/testing/expected_results/_write_estimator_results.py index 7fdbe1f86b..03d6fec996 100644 --- a/aeon/testing/expected_results/_write_estimator_results.py +++ b/aeon/testing/expected_results/_write_estimator_results.py @@ -33,7 +33,6 @@ # wrappers "MrSEQLClassifier", "MrSQMClassifier", - "LearningShapeletClassifier", # Unknown failure, needs investigation "SignatureClassifier", "TDMVDCClassifier", diff --git a/docs/api_reference/classification.rst b/docs/api_reference/classification.rst index f3d9953c4c..43ed4ac13f 100644 --- a/docs/api_reference/classification.rst +++ b/docs/api_reference/classification.rst @@ -140,7 +140,6 @@ Shapelet-based :toctree: auto_generated/ :template: class.rst - LearningShapeletClassifier RDSTClassifier SASTClassifier RSASTClassifier diff --git a/docs/changelogs/v1.6.md b/docs/changelogs/v1.6.md index e265142962..928c32e037 100644 --- a/docs/changelogs/v1.6.md +++ b/docs/changelogs/v1.6.md @@ -1,3 +1,9 @@ # v1.6.0 July 2026 + +## Classification + +### Deprecation + +- Removed `LearningShapeletClassifier` as scheduled after its deprecation in v1.5.0. diff --git a/examples/classification/classification.ipynb b/examples/classification/classification.ipynb index 51da84f4fb..69c912af6a 100644 --- a/examples/classification/classification.ipynb +++ b/examples/classification/classification.ipynb @@ -585,8 +585,6 @@ " aeon.classification.distance_based._time_series_neighbors.KNeighborsTimeSeriesClassifier),\n", " ('LITETimeClassifier',\n", " aeon.classification.deep_learning._lite_time.LITETimeClassifier),\n", - " ('LearningShapeletClassifier',\n", - " aeon.classification.shapelet_based._ls.LearningShapeletClassifier),\n", " ('MLPClassifier', aeon.classification.deep_learning._mlp.MLPClassifier),\n", " ('MUSE', aeon.classification.dictionary_based._muse.MUSE),\n", " ('MiniRocketClassifier',\n", diff --git a/examples/classification/shapelet_based.ipynb b/examples/classification/shapelet_based.ipynb index 63411d6c15..a600319388 100644 --- a/examples/classification/shapelet_based.ipynb +++ b/examples/classification/shapelet_based.ipynb @@ -93,9 +93,7 @@ { "data": { "text/plain": [ - "[('LearningShapeletClassifier',\n", - " aeon.classification.shapelet_based._ls.LearningShapeletClassifier),\n", - " ('RDSTClassifier', aeon.classification.shapelet_based._rdst.RDSTClassifier),\n", + "[('RDSTClassifier', aeon.classification.shapelet_based._rdst.RDSTClassifier),\n", " ('RSASTClassifier',\n", " aeon.classification.shapelet_based._rsast.RSASTClassifier),\n", " ('SASTClassifier', aeon.classification.shapelet_based._sast.SASTClassifier),\n", @@ -489,9 +487,6 @@ " - Others such as `SAST`[4] only select a small number of \"reference\" time series in the training data where all subsequences will be considered as shapelets without evaluating their quality. This leaves the \"feature selection\" step to the classifier that will use the transformation. `RSAST`[5] uses the same approach but also uses some statistical criteria to further reduce the number of candidates extracted from these reference time series.\n", " - Another approach used in `RandomDilatedShapeletTransform` is to use a semi-random extraction which is guided by a masking of the input space. Once a shapelet has been randomly sampled from a time series, the neighboring points around the sampling point are removed from the list of available sampling points. This avoids extracting self-similar shapelets and improves the diversity of the extracted shapelet set. The number of neighboring points affected by this process is controlled with the `alpha_similarity` parameter.\n", "\n", - "- **Shapelet generation**: This last approach takes another view at the problem: What if the best shapelets for my dataset are not present in the training data ? The goal is to use optimization methods, such as gradient descent or evolutionary algorithm, to generate shapelet values instead of extracting them from the input. The first shapelet generation method was Learning Shapelet [6], which due to the nature of the extraction, is only implemented as a classifier in aeon inside `LearningShapeletClassifier`.\n", - "\n", - "\n", "## Shapelet \"self-similarity\"\n", "We can visualize the notion of self similarity using the following image. Consider that we sample the shapelet highlighted in green, under self similarity (which is a special case of `alpha_similarity` where `alpha=1`), all the neighboring subsequences, which are stacked and highlighted in red, cannot be considered as shapelet candidates since they would overlap. The next valid candidates would be the ones highlighted in orange since they don't overlap with the green shapelet. The number of pruned candidates is determined by $\\alpha \\times l$ with $l$ the length of the sampled shapelet (in green).\n", "\n", @@ -832,7 +827,6 @@ " \"RDSTClassifier\",\n", " \"SASTClassifier\",\n", " \"RSASTClassifier\",\n", - " \"LearningShapeletClassifier\",\n", "]\n", "from aeon.benchmarking.results_loaders import get_estimator_results_as_array\n", "from aeon.datasets.tsc_datasets import univariate\n", From 25f3483be93d8fc359b923959de40cd6048fafdd Mon Sep 17 00:00:00 2001 From: MatthewMiddlehurst Date: Thu, 20 Aug 2026 15:15:26 +0100 Subject: [PATCH 4/4] [MNT] Validate the release tag and test the built wheel Check that the tag being released matches the version of the built distribution. A mismatch now fails before the test matrix runs, rather than at the PyPI upload after the full matrix has completed. Run the release test suite against the installed wheel instead of the source tree. The previous invocation ran from the workspace root, where "import aeon" resolved to the checked-out source, so the job could not catch the packaging faults it exists to catch. Describe the actual process for a failed release workflow in the developer guide: nothing has been published at that point, so the tag is deleted and the same version re-released, rather than a new patch version being prepared. --- .github/workflows/release.yml | 20 ++++++++++++++++++-- docs/developer_guide/release.md | 24 +++++++++++++++++------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 495e96fe46..f5ab611f57 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,6 +39,18 @@ jobs: python -m pip install build python -m build + - name: Check tag matches built version + if: github.ref_type == 'tag' + shell: bash + env: + RELEASE_TAG: ${{ github.ref_name }} + run: | + if ! ls dist/aeon-"${RELEASE_TAG#v}"-*.whl > /dev/null 2>&1; then + echo "::error::Tag ${RELEASE_TAG} does not match the built distribution." + ls dist/ + exit 1 + fi + - name: Store build files uses: actions/upload-artifact@v7 with: @@ -116,8 +128,12 @@ jobs: - name: Show dependencies run: python -m pip list - - name: Tests - run: python -m pytest -n logical + - name: Test installed wheel + shell: bash + run: | + mkdir wheel-test && cd wheel-test + python -c "import aeon; print(aeon.__file__); assert 'site-packages' in aeon.__file__, 'not the installed wheel'" + python -m pytest -c ../pyproject.toml -n logical --pyargs aeon upload-wheels: needs: test-wheels diff --git a/docs/developer_guide/release.md b/docs/developer_guide/release.md index 826f816fba..bcfafc759e 100644 --- a/docs/developer_guide/release.md +++ b/docs/developer_guide/release.md @@ -39,19 +39,29 @@ The release process is as follows, on high-level: ## `pypi` release and release validation -Creation of the GitHub release trigger the `pypi` release workflow. +Publishing the GitHub release triggers the `pypi` release workflow. The workflow builds +from the released tag, checks that the tag matches the version of the built +distribution, and runs the test suite against the built wheel rather than the source +tree. 5. **Approve the release workflow.** The release workflow will be automatically created in the GitHub Actions tab. This must be approved by a member of the release management workgroup before it will run. 6. **Wait for the ``pypi`` release CI/CD to finish.** - If tests fail due to sporadic unrelated failure, restart. If tests fail genuinely, - something went wrong in the above steps, investigate, fix, and repeat. If the bug - is known and sporadic (i.e. failure to read data from an external source), the release - workflow can be restarted. It is not necessary to create a new GitHub release, and - the workflow can be manually run from the GitHub Actions tab if more PRs are - required. + If tests fail due to a sporadic unrelated failure (i.e. failure to read data from an + external source), re-run the failed jobs. It is not necessary to create a new GitHub + release, and the workflow can also be run manually from the GitHub Actions tab by + selecting the release tag. + + If tests fail genuinely, something went wrong in the above steps. Nothing has been + uploaded to `pypi` at this point, so the version number is still free to use: delete + the GitHub release and its tag, merge the necessary fixes, then create the release + and tag again with the same version number. + + Once the `pypi` upload has succeeded the version is fixed, as `pypi` does not allow a + version to be re-uploaded. Any problem found after that point requires a new patch + version and a new release. 7. **Release workflow completion tasks.** Once the release workflow has passed, check `aeon` version on `pypi`, this should be