Repository: aeon-toolkit/aeon
Pull request #2754 (2025-04-18 04:23:51)

[ENH] Correct broken source links for `set_params` and `get_params` in Aeon estimator docs
<!--
Thanks for contributing a pull request! Please ensure you have taken a look at our
contribution guide: https://www.aeon-toolkit.org/en/latest/contributing.html.

Feel free to delete sections of this template if they do not apply to your PR,
avoid submitting a blank template or empty sections.
If you are a new contributor, do not delete this template without a suitable
replacement or reason. If in doubt, ask for help. We're here to help!

Please be aware that we are a team of volunteers so patience is
necessary when waiting for a review or reply. There may not be a quick turnaround for
reviews during slow periods. While we value all contributions big or small, pull
requests which do not follow our guidelines may be closed.
-->

#### Reference Issues/PRs

Fixes: #2752
<!--
Example: Fixes #1234. See also #3456.
Please use keywords (e.g., Fixes) to create link to the issues or pull requests
you resolved, so that they will automatically be closed when your pull request
is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests
-->

#### What does this implement/fix? Explain your changes.

This PR fixes broken "Source" links in the Aeon documentation for the `set_params` and `get_params` methods.
Currently, the autogenerated links incorrectly point to sklearn paths such as:

"https://github.com/aeon-toolkit/aeon/blob/main/sklearn/base.py#L255-L297"
which leads to a 404 error, since these methods are inherited from `sklearn.BaseEstimator` and not directly implemented in Aeon code.

Fixes broken "Source" links by modifying `conf.py` to exclude non-Aeon paths (e.g., `sklearn/base.py`) from autogenerated documentation links.
<!--
A clear and concise description of what you have implemented.
-->

#### Does your contribution introduce a new dependency? If yes, which one?

<!--
If your contribution does add a dependency, we may suggest adding it as an
optional/soft dependency to keep external dependencies of the core aeon package
to a minimum.
-->

#### Any other comments?

<!--
Any other information that is important to this PR or helpful for reviewers.
-->

### PR checklist

<!--
Please go through the checklist below. Please feel free to remove points if they are
not applicable. To check a box, replace the space inside the square brackets with an
'x' i.e. [x].
-->

##### For all contributions
- [ ] I've added myself to the [list of contributors](https://github.com/aeon-toolkit/aeon/blob/main/.all-contributorsrc). Alternatively, you can use the [@all-contributors](https://allcontributors.org/docs/en/bot/usage) bot to do this for you **after** the PR has been merged.
- [x] The PR title starts with either [ENH], [MNT], [DOC], [BUG], [REF], [DEP] or [GOV] indicating whether the PR topic is related to enhancement, maintenance, documentation, bugs, refactoring, deprecation or governance.

##### For new estimators and functions
- [ ] I've added the estimator/function to the online [API documentation](https://www.aeon-toolkit.org/en/latest/api_reference.html).
- [ ] (OPTIONAL) I've added myself as a `__maintainer__` at the top of relevant files and want to be contacted regarding its maintenance. Unmaintained files may be removed. This is for the full file, and you should not add yourself if you are just making minor changes or do not want to help maintain its contents.

##### For developers with write access
- [ ] (OPTIONAL) I've updated aeon's [CODEOWNERS](https://github.com/aeon-toolkit/aeon/blob/main/CODEOWNERS) to receive notifications about future changes to these files.


<!--
Thanks for contributing!
-->


aeon-actions-bot[bot]: 
## Thank you for contributing to `aeon`

I did not find any labels to add based on the title. Please add the [ENH], [MNT], [BUG], [DOC], [REF], [DEP] and/or [GOV] tags to your pull requests titles. For now you can add the labels manually.


The [Checks](https://github.com/aeon-toolkit/aeon/pull/2754/checks) tab will show the status of our automated tests. You can click on individual test runs in the tab or "Details" in the panel below to see more information if there is a failure.

If our `pre-commit` code quality check fails, any trivial fixes will automatically be pushed to your PR unless it is a draft.

Don't hesitate to ask questions on the `aeon` [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA) channel if you have any.

### PR CI actions

These checkboxes will add labels to enable/disable CI functionality for this PR. This may not take effect immediately, and a new commit may be required to run the new configuration.

- [ ] Run `pre-commit` checks for all files
- [ ] Run `mypy` typecheck tests
- [ ] Run all `pytest` tests and configurations
- [ ] Run all notebook example tests
- [ ] Run numba-disabled `codecov` tests
- [ ] Stop automatic `pre-commit` fixes (always disabled for drafts)
- [ ] Disable numba cache loading
- [ ] Push an empty commit to re-run CI checks
    
MatthewMiddlehurst: Please update your PR text, this does not represent the contents of the PR. From what I can tell clicking on the source link in the PR website build this does not actually change anything?
Kaustbh: Yes it is not working for the local build, since the generated source link still point to the local sklearn installation, 
ex. [https://github.com/aeon-toolkit/aeon/blob/myaeon/lib/python3.12/site-packages/sklearn/base.py#L251-L293](url) because of which the new added condition doesn't work, but I think after deployment, this change should take effect as expected by preventing broken links to external libraries like sklearn.
MatthewMiddlehurst: That does not seem to be the case in the PR build though https://aeon-toolkit--2754.org.readthedocs.build/en/2754/

Patch:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 37132856..cf805d09 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,7 +3,7 @@ contact_links:
     url: https://github.com/aeon-toolkit/aeon/discussions/new/choose
     about: If none of these options fit, your question or topic may be better suited to a discussion.
   - name: "\U0001F4AC Slack"
-    url: https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA
+    url: https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw
     about: For all other questions and general chat, come chat with the aeon community on Slack!
   - name: "\u2709\uFE0F Email"
     url: https://mailxto.com/jbp3ave49x
diff --git a/.github/utilities/pr_open_commenter.py b/.github/utilities/pr_open_commenter.py
index 0c3b664d..dc602329 100755
--- a/.github/utilities/pr_open_commenter.py
+++ b/.github/utilities/pr_open_commenter.py
@@ -112,7 +112,7 @@ The [Checks](https://github.com/aeon-toolkit/aeon/pull/{pr_number}/checks) tab w
 
 If our `pre-commit` code quality check fails, any trivial fixes will automatically be pushed to your PR unless it is a draft.
 
-Don't hesitate to ask questions on the `aeon` [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA) channel if you have any.
+Don't hesitate to ask questions on the `aeon` [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw) channel if you have any.
 
 ### PR CI actions
 
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 50a10ab2..090d0e86 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -9,4 +9,4 @@ the [Python Software Foundation Code of Conduct](https://www.python.org/psf/code
 If you want to make a CoC report, contact the general `aeon` email
 [contact@aeon-toolkit.org](mailto:contact@aeon-toolkit.org) or message a member of the
 [Code of Conduct Workgroup (COCW)](https://www.aeon-toolkit.org/en/stable/about.html#code-of-conduct-workgroup)
-directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA).
+directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw).
diff --git a/README.md b/README.md
index 3f6eb132..acc652fa 100644
--- a/README.md
+++ b/README.md
@@ -26,12 +26,13 @@ does not apply:
 - `segmentation`
 - `similarity_search`
 - `visualisation`
+- `transformations.collection.self_supervised`
 
 | Overview        |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
 |-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | **CI/CD**       | [![github-actions-release](https://img.shields.io/github/actions/workflow/status/aeon-toolkit/aeon/release.yml?logo=github&label=build%20%28release%29)](https://github.com/aeon-toolkit/aeon/actions/workflows/release.yml) [![github-actions-main](https://img.shields.io/github/actions/workflow/status/aeon-toolkit/aeon/pr_pytest.yml?logo=github&branch=main&label=build%20%28main%29)](https://github.com/aeon-toolkit/aeon/actions/workflows/pr_pytest.yml) [![github-actions-nightly](https://img.shields.io/github/actions/workflow/status/aeon-toolkit/aeon/periodic_tests.yml?logo=github&label=build%20%28nightly%29)](https://github.com/aeon-toolkit/aeon/actions/workflows/periodic_tests.yml) [![docs-main](https://img.shields.io/readthedocs/aeon-toolkit/stable?logo=readthedocs&label=docs%20%28stable%29)](https://www.aeon-toolkit.org/en/stable/) [![docs-main](https://img.shields.io/readthedocs/aeon-toolkit/latest?logo=readthedocs&label=docs%20%28latest%29)](https://www.aeon-toolkit.org/en/latest/) [![!codecov](https://img.shields.io/codecov/c/github/aeon-toolkit/aeon?label=codecov&logo=codecov)](https://codecov.io/gh/aeon-toolkit/aeon) [![openssf-scorecard](https://api.scorecard.dev/projects/github.com/aeon-toolkit/aeon/badge)](https://scorecard.dev/viewer/?uri=github.com/aeon-toolkit/aeon) |
 | **Code**        | [![!pypi](https://img.shields.io/pypi/v/aeon?logo=pypi&color=blue)](https://pypi.org/project/aeon/) [![!conda](https://img.shields.io/conda/vn/conda-forge/aeon?logo=anaconda&color=blue)](https://anaconda.org/conda-forge/aeon) [![!python-versions](https://img.shields.io/pypi/pyversions/aeon?logo=python)](https://www.python.org/) [![!black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![license](https://img.shields.io/badge/license-BSD%203--Clause-green?logo=style)](https://github.com/aeon-toolkit/aeon/blob/main/LICENSE) [![binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/aeon-toolkit/aeon/main?filepath=examples)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
-| **Community**   | [![!slack](https://img.shields.io/static/v1?logo=slack&label=Slack&message=chat&color=lightgreen)](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA) [![!linkedin](https://img.shields.io/static/v1?logo=linkedin&label=LinkedIn&message=news&color=lightblue)](https://www.linkedin.com/company/aeon-toolkit/) [![!x-twitter](https://img.shields.io/static/v1?logo=x&label=X/Twitter&message=news&color=lightblue)](https://twitter.com/aeon_toolkit)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
+| **Community**   | [![!slack](https://img.shields.io/static/v1?logo=slack&label=Slack&message=chat&color=lightgreen)](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw) [![!linkedin](https://img.shields.io/static/v1?logo=linkedin&label=LinkedIn&message=news&color=lightblue)](https://www.linkedin.com/company/aeon-toolkit/) [![!x-twitter](https://img.shields.io/static/v1?logo=x&label=X/Twitter&message=news&color=lightblue)](https://twitter.com/aeon_toolkit)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
 | **Affiliation** | [![numfocus](https://img.shields.io/badge/NumFOCUS-Affiliated%20Project-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org/sponsored-projects/affiliated-projects)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
 
 ## ⚙️ Installation
@@ -126,7 +127,7 @@ clu.predict(X_test)  # Assign clusters to new data
 
 [GitHub Issue Tracker]: https://github.com/aeon-toolkit/aeon/issues
 [GitHub Discussions]: https://github.com/aeon-toolkit/aeon/discussions
-[Slack]: https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA
+[Slack]: https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw
 
 For enquiries about the project or collaboration, our email is
 [contact@aeon-toolkit.org](mailto:contact@aeon-toolkit.org).
diff --git a/aeon/benchmarking/metrics/anomaly_detection/_continuous.py b/aeon/benchmarking/metrics/anomaly_detection/_continuous.py
index ade8c102..da3008db 100644
--- a/aeon/benchmarking/metrics/anomaly_detection/_continuous.py
+++ b/aeon/benchmarking/metrics/anomaly_detection/_continuous.py
@@ -180,7 +180,6 @@ def f_score_at_k_ranges(
 
     threshold = top_k_ranges_threshold(y_true, y_score, k)
     y_pred = y_score >= threshold
-    # Corrected p_alpha to 1 to match original prts behavior expectation
     return range_f_score(y_true, y_pred, p_alpha=1, r_alpha=1, cardinality="reciprocal")
 
 
@@ -209,12 +208,11 @@ def rp_rr_auc_score(
     y_score : np.ndarray
         Anomaly scores for each point of the time series of shape (n_instances,).
     max_samples: int
-        The implementation of the range-based precision and recall metrics is quite
-        slow because it relies on the non-optimized ``prts``-package. To prevent long
-        runtimes caused by scorings with high precision (many thresholds), just a
-        specific amount of possible thresholds is sampled. This parameter controls the
-        maximum number of thresholds; however, too low numbers degrade the metrics'
-        quality.
+        The calculation of the range-based precision and recall metrics can be slow.
+        To prevent long runtimes caused by scorings with high precision
+        (many thresholds), a specified amount of possible thresholds is sampled.
+        This parameter controls the maximum number of thresholds; however, too low
+        numbers degrade the metrics' quality.
     r_alpha : float
         Weight of the existence reward for the range-based recall.
     p_alpha : float
diff --git a/aeon/benchmarking/results_loaders.py b/aeon/benchmarking/results_loaders.py
index b3ed1dea..2b4ee45b 100644
--- a/aeon/benchmarking/results_loaders.py
+++ b/aeon/benchmarking/results_loaders.py
@@ -228,7 +228,7 @@ def get_available_estimators(
             f"be one of {VALID_TASK_TYPES}"
         )
     data = pd.read_csv(
-        f"http://timeseriesclassification.com/results/ReferenceResults/"
+        f"https://timeseriesclassification.com/results/ReferenceResults/"
         f"{t}/estimators.txt"
     )
     return data.iloc[:, 0].tolist() if as_list else data
@@ -241,7 +241,7 @@ def get_estimator_results(
     task: str = "classification",
     measure: str = "accuracy",
     remove_dataset_modifiers: bool = False,
-    path: str = "http://timeseriesclassification.com/results/ReferenceResults",
+    path: str = "https://timeseriesclassification.com/results/ReferenceResults",
 ):
     """Look for results for given estimators for a list of datasets.
 
@@ -327,7 +327,7 @@ def get_estimator_results_as_array(
     task: str = "classification",
     measure: str = "accuracy",
     remove_dataset_modifiers: bool = False,
-    path: str = "http://timeseriesclassification.com/results/ReferenceResults",
+    path: str = "https://timeseriesclassification.com/results/ReferenceResults",
     include_missing: bool = False,
 ):
     """Look for results for given estimators for a list of datasets.
diff --git a/aeon/forecasting/_ets.py b/aeon/forecasting/_ets.py
index efc99d6d..2899e0d7 100644
--- a/aeon/forecasting/_ets.py
+++ b/aeon/forecasting/_ets.py
@@ -1,91 +1,102 @@
 """ETSForecaster class.
 
 An implementation of the exponential smoothing statistics forecasting algorithm.
-Implements additive and multiplicative error models,
-None, additive and multiplicative (including damped) trend and
-None, additive and multiplicative seasonality
+Implements additive and multiplicative error models.
 """
 
 __maintainer__ = []
-__all__ = ["ETSForecaster", "NONE", "ADDITIVE", "MULTIPLICATIVE"]
+__all__ = ["ETSForecaster"]
+
+from typing import Union
 
 import numpy as np
 from numba import njit
 
 from aeon.forecasting.base import BaseForecaster
 
-NOGIL = False
-CACHE = True
-
-NONE = 0
-ADDITIVE = 1
-MULTIPLICATIVE = 2
+ADDITIVE = "additive"
+MULTIPLICATIVE = "multiplicative"
 
 
 class ETSForecaster(BaseForecaster):
-    """Exponential Smoothing forecaster.
+    """Exponential Smoothing (ETS) forecaster.
 
-    An implementation of the exponential smoothing forecasting algorithm.
-    Implements additive and multiplicative error models, None, additive and
-    multiplicative (including damped) trend and None, additive and mutliplicative
-    seasonality. See [1]_ for a description.
+    Implements the ETS (Error, Trend, Seasonality) forecaster, supporting additive
+    and multiplicative forms of error, trend (including damped), and seasonality
+    components. Based on the state space model formulation of exponential
+    smoothing as described in Hyndman and Athanasopoulos [1]_.
 
     Parameters
     ----------
-    error_type : int, default = 1
-        Either NONE (0), ADDITIVE (1) or MULTIPLICATIVE (2).
-    trend_type : int, default = 0
-        Either NONE (0), ADDITIVE (1) or MULTIPLICATIVE (2).
-    seasonality_type : int, default = 0
-        Either NONE (0), ADDITIVE (1) or MULTIPLICATIVE (2).
+    error_type : string or int, default=1
+        Type of error model: 'additive' (0) or 'multiplicative' (1)
+    trend_type : string, int or None, default=0
+        Type of trend component: None (0), `additive' (1) or 'multiplicative' (2)
+    seasonality_type : string or None, default=0
+        Type of seasonal component: None (0), `additive' (1) or 'multiplicative' (2)
     seasonal_period : int, default=1
-        Length of seasonality period. If seasonality_type is NONE, this is assumed to
-        be 1
-    alpha : float, default = 0.1
+        Number of time points in a seasonal cycle.
+    alpha : float, default=0.1
         Level smoothing parameter.
-    beta : float, default = 0.01
-        Trend smoothing parameter. If trend_type is NONE, this is assumed to be 0.0.
-    gamma : float, default = 0.01
-        Seasonal smoothing parameter. If seasonality is NONE, this is assumed to be
-        0.0.
-    phi : float, default = 0.99
-        Trend damping smoothing parameters
-    horizon : int, default = 1
-        The horizon to forecast to.
+    beta : float, default=0.01
+        Trend smoothing parameter.
+    gamma : float, default=0.01
+        Seasonal smoothing parameter.
+    phi : float, default=0.99
+        Trend damping parameter (used only for damped trend models).
+    horizon : int, default=1
+        Forecasting horizon (number of time steps ahead to predict).
 
     Attributes
     ----------
-    mean_sq_err_ : float
-        Mean squared error.
-    likelihood_ : float
-        Likelihood of the fitted model based on residuals.
-    residuals_ : arraylike
-        List of train set differences between fitted and actual values.
-    n_timpoints_ : int
-        Length of the series passed to fit.
+    forecast_val_ : float
+        Forecast value for the given horizon.
+    level_ : float
+        Estimated level component.
+    trend_ : float
+        Estimated trend component.
+    seasonality_ : array-like or None
+        Estimated seasonal components.
+    aic_ : float
+        Akaike Information Criterion of the fitted model.
+    avg_mean_sq_err_ : float
+        Average mean squared error of the fitted model.
+    residuals_ : list of float
+        Residuals from the fitted model.
+    fitted_values_ : list of float
+        Fitted values for the training data.
+    liklihood_ : float
+        Log-likelihood of the fitted model.
+    n_timepoints_ : int
+        Number of time points in the training series.
 
     References
     ----------
     .. [1] R. J. Hyndman and G. Athanasopoulos,
-        Forecasting: Principles and Practice. Melbourne, Australia: OTexts, 2014.
+       Forecasting: Principles and Practice, 2nd Edition. OTexts, 2014.
+       https://otexts.com/fpp3/
 
     Examples
     --------
     >>> from aeon.forecasting import ETSForecaster
     >>> from aeon.datasets import load_airline
     >>> y = load_airline()
-    >>> forecaster = ETSForecaster(alpha=0.4, beta=0.2, gamma=0.5, phi=0.8, horizon=1)
+    >>> forecaster = ETSForecaster(
+    ...     alpha=0.4, beta=0.2, gamma=0.5, phi=0.8, horizon=1,
+    ...     error_type='additive', trend_type='multiplicative',
+    ...     seasonality_type='multiplicative', seasonal_period=4
+    ... )
     >>> forecaster.fit(y)
-    ETSForecaster(alpha=0.4, beta=0.2, gamma=0.5, phi=0.8)
+    ETSForecaster(...)
     >>> forecaster.predict()
-    449.9435566831507
+    366.90200486015596
     """
 
     def __init__(
         self,
-        error_type: int = ADDITIVE,
-        trend_type: int = NONE,
-        seasonality_type: int = NONE,
+        error_type: Union[int, str] = 1,
+        trend_type: Union[int, str, None] = 0,
+        seasonality_type: Union[int, str, None] = 0,
         seasonal_period: int = 1,
         alpha: float = 0.1,
         beta: float = 0.01,
@@ -93,18 +104,28 @@ class ETSForecaster(BaseForecaster):
         phi: float = 0.99,
         horizon: int = 1,
     ):
-        self.error_type = error_type
-        self.trend_type = trend_type
-        self.seasonality_type = seasonality_type
-        self.seasonal_period = seasonal_period
         self.alpha = alpha
         self.beta = beta
         self.gamma = gamma
         self.phi = phi
-        self.mean_sq_err_ = 0
-        self.likelihood_ = 0
+        self.forecast_val_ = 0.0
+        self.level_ = 0.0
+        self.trend_ = 0.0
+        self.seasonality_ = None
+        self._beta = beta
+        self._gamma = gamma
+        self.error_type = error_type
+        self.trend_type = trend_type
+        self.seasonality_type = seasonality_type
+        self.seasonal_period = seasonal_period
+        self._seasonal_period = seasonal_period
+        self.n_timepoints_ = 0
+        self.avg_mean_sq_err_ = 0
+        self.liklihood_ = 0
+        self.k_ = 0
+        self.aic_ = 0
         self.residuals_ = []
-        self.n_timpoints_ = 0
+        self.fitted_values_ = []
         super().__init__(horizon=horizon, axis=1)
 
     def _fit(self, y, exog=None):
@@ -122,33 +143,51 @@ class ETSForecaster(BaseForecaster):
         Returns
         -------
         self
-            Fitted BaseForecaster.
+            Fitted ETSForecaster.
         """
-        self.n_timepoints_ = len(y)
-        if self.error_type != MULTIPLICATIVE and self.error_type != ADDITIVE:
-            raise ValueError("Error must be either additive or multiplicative")
-        self._seasonal_period = self.seasonal_period
-        if self.seasonal_period < 1 or self.seasonality_type == NONE:
+        _validate_parameter(self.error_type, False)
+        _validate_parameter(self.seasonality_type, True)
+        _validate_parameter(self.trend_type, True)
+
+        # Convert to string parameters to ints for numba efficiency
+        def _get_int(x):
+            if x is None:
+                return 0
+            if x == ADDITIVE:
+                return 1
+            if x == MULTIPLICATIVE:
+                return 2
+            return x
+
+        self._error_type = _get_int(self.error_type)
+        self._seasonality_type = _get_int(self.seasonality_type)
+        self._trend_type = _get_int(self.trend_type)
+        if self._seasonal_period < 1 or self._seasonality_type == 0:
             self._seasonal_period = 1
-        self._beta = self.beta
-        if self.trend_type == NONE or self.trend_type is None:
+
+        if self._trend_type == 0:
+            # Required for the equations in _update_states to work correctly
             self._beta = 0
-        self._gamma = self.gamma
-        if self.seasonality_type == NONE or self.trend_type is None:
+        if self._seasonality_type == 0:
+            # Required for the equations in _update_states to work correctly
             self._gamma = 0
-        data = np.array(y.squeeze(), dtype=np.float64)
+        data = y.squeeze()
         (
-            self._level,
-            self._trend,
-            self._seasonality,
+            self.level_,
+            self.trend_,
+            self.seasonality_,
+            self.n_timepoints_,
             self.residuals_,
-            self.mean_sq_err_,
-            self.likelihood_,
-        ) = _fit_numba(
+            self.fitted_values_,
+            self.avg_mean_sq_err_,
+            self.liklihood_,
+            self.k_,
+            self.aic_,
+        ) = _numba_fit(
             data,
-            self.error_type,
-            self.trend_type,
-            self.seasonality_type,
+            self._error_type,
+            self._trend_type,
+            self._seasonality_type,
             self._seasonal_period,
             self.alpha,
             self._beta,
@@ -166,7 +205,7 @@ class ETSForecaster(BaseForecaster):
         y : np.ndarray, default = None
             A time series to predict the next horizon value for. If None,
             predict the next horizon value after series seen in fit.
-        exog : np.ndarray, default = None
+        exog : np.ndarray, default =None
             Optional exogenous time series data assumed to be aligned with y
 
         Returns
@@ -174,40 +213,60 @@ class ETSForecaster(BaseForecaster):
         float
             single prediction self.horizon steps ahead of y.
         """
-        return _predict_numba(
-            self.trend_type,
-            self.seasonality_type,
-            self._level,
-            self._trend,
-            self._seasonality,
+        fitted_value = _predict(
+            self._trend_type,
+            self._seasonality_type,
+            self.level_,
+            self.trend_,
+            self.seasonality_,
             self.phi,
             self.horizon,
             self.n_timepoints_,
-            self.seasonal_period,
+            self._seasonal_period,
+        )
+        return fitted_value
+
+    def _initialise(self, data):
+        """
+        Initialize level, trend, and seasonality values for the ETS model.
+
+        Parameters
+        ----------
+        data : array-like
+            The time series data
+            (should contain at least two full seasons if seasonality is specified)
+        """
+        self.level_, self.trend_, self.seasonality_ = _initialise(
+            self._trend_type, self._seasonality_type, self._seasonal_period, data
         )
 
 
-@njit(nogil=NOGIL, cache=CACHE)
-def _fit_numba(
+@njit(fastmath=True, cache=True)
+def _numba_fit(
     data,
-    error_type: int,
-    trend_type: int,
-    seasonality_type: int,
-    seasonal_period: int,
-    alpha: float,
-    beta: float,
-    gamma: float,
-    phi: float,
+    error_type,
+    trend_type,
+    seasonality_type,
+    seasonal_period,
+    alpha,
+    beta,
+    gamma,
+    phi,
 ):
-    n_timepoints = len(data)
+    n_timepoints = len(data) - seasonal_period
     level, trend, seasonality = _initialise(
         trend_type, seasonality_type, seasonal_period, data
     )
-    mse = 0
-    lhood = 0
-    mul_likelihood_pt2 = 0
-    res = np.zeros(n_timepoints)  # 1 Less residual than data points
-    for t, data_item in enumerate(data[seasonal_period:]):
+    avg_mean_sq_err_ = 0
+    liklihood_ = 0
+    residuals_ = np.zeros(n_timepoints)  # 1 Less residual than data points
+    fitted_values_ = np.zeros(n_timepoints)
+    for t in range(n_timepoints):
+        index = t + seasonal_period
+        s_index = t % seasonal_period
+
+        time_point = data[index]
+
         # Calculate level, trend, and seasonal components
         fitted_value, error, level, trend, seasonality[t % seasonal_period] = (
             _update_states(
@@ -216,39 +275,59 @@ def _fit_numba(
                 seasonality_type,
                 level,
                 trend,
-                seasonality[t % seasonal_period],
-                data_item,
+                seasonality[s_index],
+                time_point,
                 alpha,
                 beta,
                 gamma,
                 phi,
             )
         )
-        res[t] = error
-        mse += (data_item - fitted_value) ** 2
-        lhood += error * error
-        mul_likelihood_pt2 += np.log(np.fabs(fitted_value))
-    mse /= n_timepoints - seasonal_period
-    lhood = (n_timepoints - seasonal_period) * np.log(lhood)
-    if error_type == MULTIPLICATIVE:
-        lhood += 2 * mul_likelihood_pt2
-    return level, trend, seasonality, res, mse, lhood
-
-
-def _predict_numba(
-    trend_type: int,
-    seasonality_type: int,
-    level: float,
-    trend: float,
-    seasonality: float,
-    phi: float,
-    horizon: int,
-    n_timepoints: int,
-    seasonal_period: int,
+        residuals_[t] = error
+        fitted_values_[t] = fitted_value
+        avg_mean_sq_err_ += (time_point - fitted_value) ** 2
+        liklihood_error = error
+        if error_type == 2:  # Multiplicative
+            liklihood_error *= fitted_value
+        liklihood_ += liklihood_error**2
+    avg_mean_sq_err_ /= n_timepoints
+    liklihood_ = n_timepoints * np.log(liklihood_)
+    k_ = (
+        seasonal_period * (seasonality_type != 0)
+        + 2 * (trend_type != 0)
+        + 2
+        + 1 * (phi != 1)
+    )
+    aic_ = liklihood_ + 2 * k_ - n_timepoints * np.log(n_timepoints)
+    return (
+        level,
+        trend,
+        seasonality,
+        n_timepoints,
+        residuals_,
+        fitted_values_,
+        avg_mean_sq_err_,
+        liklihood_,
+        k_,
+        aic_,
+    )
+
+
+@njit(fastmath=True, cache=True)
+def _predict(
+    trend_type,
+    seasonality_type,
+    level,
+    trend,
+    seasonality,
+    phi,
+    horizon,
+    n_timepoints,
+    seasonal_period,
 ):
     # Generate forecasts based on the final values of level, trend, and seasonals
     if phi == 1:  # No damping case
-        phi_h = float(horizon)
+        phi_h = 1
     else:
         # Geometric series formula for calculating phi + phi^2 + ... + phi^h
         phi_h = phi * (1 - phi**horizon) / (1 - phi)
@@ -263,8 +342,8 @@ def _predict_numba(
     )[0]
 
 
-@njit(nogil=NOGIL, cache=CACHE)
-def _initialise(trend_type: int, seasonality_type: int, seasonal_period: int, data):
+@njit(fastmath=True, cache=True)
+def _initialise(trend_type, seasonality_type, seasonal_period, data):
     """
     Initialize level, trend, and seasonality values for the ETS model.
 
@@ -277,12 +356,12 @@ def _initialise(trend_type: int, seasonality_type: int, seasonal_period: int, da
     # Initial Level: Mean of the first season
     level = np.mean(data[:seasonal_period])
     # Initial Trend
-    if trend_type == ADDITIVE:
+    if trend_type == 1:
         # Average difference between corresponding points in the first two seasons
         trend = np.mean(
             data[seasonal_period : 2 * seasonal_period] - data[:seasonal_period]
         )
-    elif trend_type == MULTIPLICATIVE:
+    elif trend_type == 2:
         # Average ratio between corresponding points in the first two seasons
         trend = np.mean(
             data[seasonal_period : 2 * seasonal_period] / data[:seasonal_period]
@@ -291,33 +370,33 @@ def _initialise(trend_type: int, seasonality_type: int, seasonal_period: int, da
         # No trend
         trend = 0
     # Initial Seasonality
-    if seasonality_type == ADDITIVE:
+    if seasonality_type == 1:
         # Seasonal component is the difference
         # from the initial level for each point in the first season
         seasonality = data[:seasonal_period] - level
-    elif seasonality_type == MULTIPLICATIVE:
+    elif seasonality_type == 2:
         # Seasonal component is the ratio of each point in the first season
         # to the initial level
         seasonality = data[:seasonal_period] / level
     else:
         # No seasonality
-        seasonality = np.zeros(1)
+        seasonality = np.zeros(1, dtype=np.float64)
     return level, trend, seasonality
 
 
-@njit(nogil=NOGIL, cache=CACHE)
+@njit(fastmath=True, cache=True)
 def _update_states(
-    error_type: int,
-    trend_type: int,
-    seasonality_type: int,
-    level: float,
-    trend: float,
-    seasonality: float,
+    error_type,
+    trend_type,
+    seasonality_type,
+    level,
+    trend,
+    seasonality,
     data_item: int,
-    alpha: float,
-    beta: float,
-    gamma: float,
-    phi: float,
+    alpha,
+    beta,
+    gamma,
+    phi,
 ):
     """
     Update level, trend, and seasonality components.
@@ -338,34 +417,34 @@ def _update_states(
         trend_type, seasonality_type, level, trend, seasonality, phi
     )
     # Calculate the error term (observed value - fitted value)
-    if error_type == MULTIPLICATIVE:
+    if error_type == 2:
         error = data_item / fitted_value - 1  # Multiplicative error
     else:
         error = data_item - fitted_value  # Additive error
     # Update level
-    if error_type == MULTIPLICATIVE:
+    if error_type == 2:
         level = trend_level_combination * (1 + alpha * error)
         trend = damped_trend * (1 + beta * error)
         seasonality = curr_seasonality * (1 + gamma * error)
-        if seasonality_type == ADDITIVE:
+        if seasonality_type == 1:
             level += alpha * error * curr_seasonality  # Add seasonality correction
             seasonality += gamma * error * trend_level_combination
-            if trend_type == ADDITIVE:
+            if trend_type == 1:
                 trend += (curr_level + curr_seasonality) * beta * error
             else:
                 trend += curr_seasonality / curr_level * beta * error
-        elif trend_type == ADDITIVE:
+        elif trend_type == 1:
             trend += curr_level * beta * error
     else:
         level_correction = 1
         trend_correction = 1
         seasonality_correction = 1
-        if seasonality_type == MULTIPLICATIVE:
+        if seasonality_type == 2:
             # Add seasonality correction
             level_correction *= curr_seasonality
             trend_correction *= curr_seasonality
             seasonality_correction *= trend_level_combination
-        if trend_type == MULTIPLICATIVE:
+        if trend_type == 2:
             trend_correction *= curr_level
         level = trend_level_combination + alpha * error / level_correction
         trend = damped_trend + beta * error / trend_correction
@@ -373,15 +452,8 @@ def _update_states(
     return (fitted_value, error, level, trend, seasonality)
 
 
-@njit(nogil=NOGIL, cache=CACHE)
-def _predict_value(
-    trend_type: int,
-    seasonality_type: int,
-    level: float,
-    trend: float,
-    seasonality: float,
-    phi: float,
-):
+@njit(fastmath=True, cache=True)
+def _predict_value(trend_type, seasonality_type, level, trend, seasonality, phi):
     """
 
     Generate various useful values, including the next fitted value.
@@ -408,7 +480,7 @@ def _predict_value(
     """
     # Apply damping parameter and
     # calculate commonly used combination of trend and level components
-    if trend_type == MULTIPLICATIVE:
+    if trend_type == 2:  # Multiplicative
         damped_trend = trend**phi
         trend_level_combination = level * damped_trend
     else:  # Additive trend, if no trend, then trend = 0
@@ -416,8 +488,30 @@ def _predict_value(
         trend_level_combination = level + damped_trend
 
     # Calculate forecast (fitted value) based on the current components
-    if seasonality_type == MULTIPLICATIVE:
+    if seasonality_type == 2:  # Multiplicative
         fitted_value = trend_level_combination * seasonality
     else:  # Additive seasonality, if no seasonality, then seasonality = 0
         fitted_value = trend_level_combination + seasonality
     return fitted_value, damped_trend, trend_level_combination
+
+
+def _validate_parameter(var, can_be_none):
+    valid_str = (ADDITIVE, MULTIPLICATIVE)
+    valid_int = (1, 2)
+    if can_be_none:
+        valid_str = (None, ADDITIVE, MULTIPLICATIVE)
+        valid_int = (0, 1, 2)
+    valid = True
+    if isinstance(var, str) or var is None:
+        if var not in valid_str:
+            valid = False
+    elif isinstance(var, int):
+        if var not in valid_int:
+            valid = False
+    else:
+        valid = False
+    if not valid:
+        raise ValueError(
+            f"variable must be either string or integer with values"
+            f" {valid_str} or {valid_int} but saw {var}"
+        )
diff --git a/aeon/networks/base.py b/aeon/networks/base.py
index e517894a..cd249ff1 100644
--- a/aeon/networks/base.py
+++ b/aeon/networks/base.py
@@ -4,6 +4,7 @@ __maintainer__ = []
 
 from abc import ABC, abstractmethod
 
+from aeon.utils.repr import get_unchanged_and_required_params_as_str
 from aeon.utils.validation._dependencies import (
     _check_python_version,
     _check_soft_dependencies,
@@ -25,6 +26,11 @@ class BaseDeepLearningNetwork(ABC):
         _check_python_version(python_version)
         super().__init__()
 
+    def __repr__(self):
+        """Format str output like scikit-learn estimators."""
+        changed_params = get_unchanged_and_required_params_as_str(self)
+        return f"{self.__class__.__name__}({changed_params})"
+
     @abstractmethod
     def build_network(self, input_shape, **kwargs):
         """Construct a network and return its input and output layers.
diff --git a/aeon/transformations/collection/self_supervised/__init__.py b/aeon/transformations/collection/self_supervised/__init__.py
new file mode 100644
index 00000000..f1b40c5d
--- /dev/null
+++ b/aeon/transformations/collection/self_supervised/__init__.py
@@ -0,0 +1,5 @@
+"""Self Supervised deep learning transformers."""
+
+__all__ = ["TRILITE"]
+
+from aeon.transformations.collection.self_supervised._trilite import TRILITE
diff --git a/aeon/transformations/collection/self_supervised/_trilite.py b/aeon/transformations/collection/self_supervised/_trilite.py
new file mode 100644
index 00000000..b0ba47a8
--- /dev/null
+++ b/aeon/transformations/collection/self_supervised/_trilite.py
@@ -0,0 +1,681 @@
+"""TRILITE SSL transformer."""
+
+from __future__ import annotations
+
+__maintainer__ = ["hadifawaz1999"]
+__all__ = ["TRILITE"]
+
+import gc
+import os
+import sys
+import time
+from copy import deepcopy
+from typing import TYPE_CHECKING
+
+import numpy as np
+from sklearn.utils import check_random_state
+
+from aeon.networks import BaseDeepLearningNetwork
+from aeon.transformations.collection import BaseCollectionTransformer
+from aeon.utils.self_supervised.general import z_normalization
+
+if TYPE_CHECKING:
+    from tensorflow.keras.callbacks import Callback
+    from tensorflow.keras.optimizers import Optimizer
+
+
+class TRILITE(BaseCollectionTransformer):
+    """TRIplet Loss In TimE (TRILITE).
+
+    TRILITE [1]_ is a self-supervised model that learns a latent
+    space through the triplet loss mechanism by reducing
+    the loss between close samples and increasing it between
+    far samples. TRILITE generates the triplets using two techniques,
+    mixing up and masking. For each reference series (ref), a positive
+    representation of ref is generated by mixing it up with two other
+    randomly chosen time series from the dataset then masking a part
+    of it. The weights of the mixing up procedure are randomly chosen
+    for the two randomly selected series in a way that the ref still
+    has the highest weight. The same procedure is used to generated
+    the negative representation however by using another ref.
+
+    Parameters
+    ----------
+    alpha : float, default = 1e-2
+        The value that controls the space of the triplet loss,
+        the smaller the value the more difficult the problem
+        becomes, the higher the value the more easy the problem
+        becomes, a balance should be found.
+    weight_ref_min : float, default = 0.6
+        The weight of the reference series used for the triplet
+        generation.
+    percentage_mask_length : int, default = 0.2
+        The percentage of time series length to calculate
+        the length of the masking used for the triplet
+        generation. Default is 20%.
+    use_mixing_up : bool, default = True
+        Wether or not to use mixing up during the triplet
+        generation phase.
+    use_masking : bool, default = True
+        Whether or not to use masking during the triplet
+        generation phase.
+    z_normalize_pos_neg : bool, default = True
+        Whether or not to z_normalize (mean 0 and std 1)
+        pos and neg samples after generating the triplet.
+    backbone_network : aeon Network, default = None
+        The backbone network used for the SSL model,
+        it can be any network from the aeon.networks
+        module on condition for it's structure to be
+        configured as "encoder", see _config attribute.
+        For TRILITE, the default network used is
+        FCNNetwork.
+    latent_space_dim : int, default = 128
+        The size of the latent space, applied using a
+        fully connected layer at the end of the network's
+        output.
+    latent_space_activation : str, default = "linear"
+        The activation to control the range of values
+        of the latent space.
+    random_state : int, RandomState instance or None, default=None
+        If `int`, random_state is the seed used by the random number generator;
+        If `RandomState` instance, random_state is the random number generator;
+        If `None`, the random number generator is the `RandomState` instance used
+        by `np.random`.
+        Seeded random number generation can only be guaranteed on CPU processing,
+        GPU processing will be non-deterministic.
+    verbose : boolean, default = False
+        Whether to output extra information.
+    optimizer : keras.optimizer, default = tf.keras.optimizers.Adam()
+        The keras optimizer used for training.
+    file_path : str, default = "./"
+        File path to save best model.
+    save_best_model : bool, default = False
+        Whether or not to save the best model, if the
+        modelcheckpoint callback is used by default,
+        this condition, if True, will prevent the
+        automatic deletion of the best saved model from
+        file and the user can choose the file name.
+    save_last_model : bool, default = False
+        Whether or not to save the last model, last
+        epoch trained, using the base class method
+        save_last_model_to_file.
+    save_init_model : bool, default = False
+        Whether to save the initialization of the  model.
+    best_file_name : str, default = "best_model"
+        The name of the file of the best model, if
+        save_best_model is set to False, this parameter
+        is discarded.
+    last_file_name : str, default = "last_model"
+        The name of the file of the last model, if
+        save_last_model is set to False, this parameter
+        is discarded.
+    init_file_name : str, default = "init_model"
+        The name of the file of the init model, if
+        save_init_model is set to False,
+        this parameter is discarded.
+    callbacks : keras callback or list of callbacks,
+        default = None
+        The default list of callbacks are set to
+        ModelCheckpoint and ReduceLROnPlateau.
+    batch_size : int, default = 64
+        The number of samples per gradient update.
+    use_mini_batch_size : bool, default = False
+        Whether or not to use the mini batch size formula.
+    n_epochs : int, default = 2000
+        The number of epochs to train the model.
+
+    Notes
+    -----
+    Adapted from the implementation from Ismail-Fawaz et. al
+    https://github.com/MSD-IRIMAS/TRILITE
+
+    References
+    ----------
+    .. [1] Ismail-Fawaz, Ali, Maxime Devanne, Jonathan Weber,
+    and Germain Forestier. "Enhancing time series classification
+    with self-supervised learning." In International Conference
+    on Agents and Artificial Intelligence (ICAART), pp. 40-47.
+    SCITEPRESS-Science and Technology Publications, 2023.
+
+    Examples
+    --------
+    >>> from aeon.transformations.collection.self_supervised import TRILITE
+    >>> from aeon.datasets import load_unit_test
+    >>> X_train, y_train = load_unit_test(split="train")
+    >>> ssl = TRILITE(latent_space_dim=2, n_epochs=5) # doctest: +SKIP
+    >>> ssl.fit(X_train) # doctest: +SKIP
+    TRILITE(...)
+    >>> X_train_transformed = ssl.transform(X_train) # doctest: +SKIP
+    """
+
+    _tags = {
+        "X_inner_type": "numpy3D",
+        "output_data_type": "Tabular",
+        "capability:multivariate": True,
+        "algorithm_type": "deeplearning",
+        "python_dependencies": "tensorflow",
+        "non_deterministic": True,
+        "cant_pickle": True,
+    }
+
+    def __init__(
+        self,
+        alpha: float = 1e-2,
+        weight_ref_min: float = 0.6,
+        percentage_mask_length: float = 0.2,
+        use_mixing_up: bool = True,
+        use_masking: bool = True,
+        z_normalize_pos_neg: bool = True,
+        backbone_network: BaseDeepLearningNetwork = None,
+        latent_space_dim: int = 128,
+        latent_space_activation: str = "linear",
+        random_state: int | np.random.RandomState | None = None,
+        verbose: bool = False,
+        optimizer: Optimizer | None = None,
+        file_path: str = "./",
+        save_best_model: bool = False,
+        save_last_model: bool = False,
+        save_init_model: bool = False,
+        best_file_name: str = "best_model",
+        last_file_name: str = "last_model",
+        init_file_name: str = "init_model",
+        callbacks: Callback | list[Callback] | None = None,
+        batch_size: int = 64,
+        use_mini_batch_size: bool = False,
+        n_epochs: int = 2000,
+    ):
+        self.alpha = alpha
+        self.weight_ref_min = weight_ref_min
+        self.percentage_mask_length = percentage_mask_length
+        self.use_mixing_up = use_mixing_up
+        self.use_masking = use_masking
+        self.z_normalize_pos_neg = z_normalize_pos_neg
+        self.backbone_network = backbone_network
+        self.latent_space_dim = latent_space_dim
+        self.latent_space_activation = latent_space_activation
+        self.random_state = random_state
+        self.verbose = verbose
+        self.optimizer = optimizer
+        self.file_path = file_path
+        self.save_best_model = save_best_model
+        self.save_last_model = save_last_model
+        self.save_init_model = save_init_model
+        self.best_file_name = best_file_name
+        self.last_file_name = last_file_name
+        self.init_file_name = init_file_name
+        self.callbacks = callbacks
+        self.batch_size = batch_size
+        self.use_mini_batch_size = use_mini_batch_size
+        self.n_epochs = n_epochs
+
+        super().__init__()
+
+    def _fit(self, X: np.ndarray, y=None):
+        """Fit the SSL model on X, y is ignored.
+
+        Parameters
+        ----------
+        X : np.ndarray
+            The training input samples of shape (n_cases, n_channels, n_timepoints)
+        y : ignored argument for interface compatibility
+
+        Returns
+        -------
+        self : object
+        """
+        import tensorflow as tf
+
+        from aeon.networks import FCNNetwork
+
+        if isinstance(self.backbone_network, BaseDeepLearningNetwork):
+            self._backbone_network = deepcopy(self.backbone_network)
+        elif self.backbone_network is None:
+            self._backbone_network = FCNNetwork()
+        else:
+            raise ValueError(
+                "The parameter backbone_network", "should be an aeon network."
+            )
+
+        X = X.transpose(0, 2, 1)
+
+        self.input_shape = X.shape[1:]
+        self.training_model_ = self.build_model(self.input_shape)
+
+        if self.save_init_model:
+            self.training_model_.save(
+                os.path.join(self.file_path, self.init_file_name + ".keras")
+            )
+
+        if self.verbose:
+            self.training_model_.summary()
+
+        if self.use_mini_batch_size:
+            mini_batch_size = min(self.batch_size, X.shape[0] // 10)
+        else:
+            mini_batch_size = self.batch_size
+
+        self.file_name_ = (
+            self.best_file_name if self.save_best_model else str(time.time_ns())
+        )
+
+        if self.callbacks is None:
+            self.callbacks_ = [
+                tf.keras.callbacks.ReduceLROnPlateau(
+                    monitor="loss", factor=0.5, patience=50, min_lr=0.0001
+                ),
+                tf.keras.callbacks.ModelCheckpoint(
+                    filepath=os.path.join(self.file_path, self.file_name_ + ".keras"),
+                    monitor="loss",
+                    save_best_only=True,
+                ),
+            ]
+        else:
+            self.callbacks_ = self._get_model_checkpoint_callback(
+                callbacks=self.callbacks,
+                file_path=self.file_path,
+                file_name=self.file_name_,
+            )
+
+        fake_y = np.zeros(shape=len(X))
+
+        train_dataset = tf.data.Dataset.from_tensor_slices((X, fake_y))
+        train_dataset = train_dataset.shuffle(buffer_size=1024).batch(mini_batch_size)
+
+        history = {"loss": []}
+
+        for callback in self.callbacks_:
+            callback.set_model(self.training_model_)
+            callback.on_train_begin()
+
+        for epoch in range(self.n_epochs):
+            epoch_loss = 0
+            num_batches = 0
+
+            for step, (x_batch_train, _) in enumerate(train_dataset):
+                ref_batch_train, pos_batch_train, neg_batch_train = (
+                    self._triplet_generation(X=x_batch_train)
+                )
+
+                with tf.GradientTape() as tape:
+                    ref_pos_neg = self.training_model_(
+                        [ref_batch_train, pos_batch_train, neg_batch_train]
+                    )
+                    loss_batch = self._triplet_loss_function(
+                        alpha=self.alpha, ref_pos_neg=ref_pos_neg
+                    )
+                    loss_mean = tf.reduce_mean(loss_batch)
+
+                gradients = tape.gradient(
+                    loss_mean, self.training_model_.trainable_weights
+                )
+                self.optimizer_.apply_gradients(
+                    zip(gradients, self.training_model_.trainable_weights)
+                )
+
+                epoch_loss += float(loss_mean)
+                num_batches += 1
+
+                for callback in self.callbacks_:
+                    callback.on_batch_end(step, {"loss": float(loss_mean)})
+
+            epoch_loss /= num_batches
+            history["loss"].append(epoch_loss)
+
+            if self.verbose:
+                sys.stdout.write(
+                    "Training loss at epoch %d: %.4f\n" % (epoch, float(epoch_loss))
+                )
+
+            for callback in self.callbacks_:
+                callback.on_epoch_end(epoch, {"loss": float(epoch_loss)})
+
+        for callback in self.callbacks_:
+            callback.on_train_end()
+
+        self.history = history
+
+        try:
+            self.model_ = tf.keras.models.load_model(
+                os.path.join(self.file_path, self.file_name_ + ".keras"), compile=False
+            )
+            if not self.save_best_model:
+                os.remove(os.path.join(self.file_path, self.file_name_ + ".keras"))
+        except FileNotFoundError:
+            self.model_ = deepcopy(self.training_model_)
+
+        if self.save_last_model:
+            self.save_last_model_to_file(file_path=self.file_path)
+
+        gc.collect()
+        return self
+
+    def _transform(self, X, y=None):
+        """Transform input time series using TRILITE.
+
+        Parameters
+        ----------
+        X : 3D np.ndarray of shape = (n_cases, n_channels, n_timepoints)
+            collection of time series to transform
+        y : ignored argument for interface compatibility
+
+        Returns
+        -------
+        np.ndarray (n_cases, latent_space_dim), transformed features
+        """
+        X = X.transpose(0, 2, 1)
+        X_ref_pos_neg_transformed = self.model_.predict([X, X, X], self.batch_size)
+
+        X_transformed_ = np.delete(X_ref_pos_neg_transformed, obj=[1, 2], axis=2)
+
+        X_transformed = np.reshape(
+            X_transformed_, (len(X_transformed_), self.latent_space_dim)
+        )
+
+        return X_transformed
+
+    def build_model(self, input_shape):
+        """Construct a compiled, un-trained, keras model that is ready for training.
+
+        In aeon, time series are stored in numpy arrays of shape (d,m), where d
+        is the number of dimensions, m is the series length. Keras/tensorflow assume
+        data is in shape (m,d). This method also assumes (m,d). Transpose should
+        happen in fit.
+
+        Parameters
+        ----------
+        input_shape : tuple[int, int]
+            The shape of the data fed into the input layer, should be (m, d).
+
+        Returns
+        -------
+        output : a compiled Keras Model
+        """
+        import numpy as np
+        import tensorflow as tf
+
+        rng = check_random_state(self.random_state)
+        self.random_state_ = rng.randint(0, np.iinfo(np.int32).max)
+        tf.keras.utils.set_random_seed(self.random_state_)
+
+        input_ref_layer = tf.keras.layers.Input(input_shape)
+        input_pos_layer = tf.keras.layers.Input(input_shape)
+        input_neg_layer = tf.keras.layers.Input(input_shape)
+
+        input_layer, gap_layer = self._backbone_network.build_network(
+            input_shape=input_shape
+        )
+        output_layer = tf.keras.layers.Dense(
+            units=self.latent_space_dim, activation=self.latent_space_activation
+        )(gap_layer)
+
+        encoder_model = tf.keras.models.Model(inputs=input_layer, outputs=output_layer)
+
+        output_layer_ref = tf.keras.layers.Reshape(target_shape=(-1, 1))(
+            encoder_model(input_ref_layer)
+        )
+        output_layer_pos = tf.keras.layers.Reshape(target_shape=(-1, 1))(
+            encoder_model(input_pos_layer)
+        )
+        output_layer_neg = tf.keras.layers.Reshape(target_shape=(-1, 1))(
+            encoder_model(input_neg_layer)
+        )
+
+        encoder_output_layer = tf.keras.layers.Concatenate(axis=-1)(
+            [output_layer_ref, output_layer_pos, output_layer_neg]
+        )
+
+        model = tf.keras.models.Model(
+            inputs=[input_ref_layer, input_pos_layer, input_neg_layer],
+            outputs=encoder_output_layer,
+        )
+
+        self.optimizer_ = (
+            tf.keras.optimizers.Adam() if self.optimizer is None else self.optimizer
+        )
+
+        # compile but won't be used
+        model.compile(loss="mse", optimizer=self.optimizer_)
+
+        return model
+
+    def _triplet_loss_function(self, alpha, ref_pos_neg):
+        """Create a triplet loss function for triplet-based training."""
+        import tensorflow as tf
+
+        ref = ref_pos_neg[:, :, 0]
+        pos = ref_pos_neg[:, :, 1]
+        neg = ref_pos_neg[:, :, 2]
+
+        ref = tf.cast(ref, dtype=ref.dtype)
+        pos = tf.cast(pos, dtype=ref.dtype)
+        neg = tf.cast(neg, dtype=ref.dtype)
+
+        loss_pos_ref = tf.reduce_sum(tf.square(ref - pos), axis=1)
+        loss_neg_ref = tf.reduce_sum(tf.square(ref - neg), axis=1)
+        loss_add_sub = tf.math.subtract(tf.math.add(loss_pos_ref, alpha), loss_neg_ref)
+        loss = tf.maximum(loss_add_sub, 0)
+
+        return loss
+
+    def _triplet_generation(self, X):
+        """Generate triplet samples (ref, pos, neg) for triplet loss training."""
+        n_channels = int(X.shape[-1])
+        length_TS = int(X.shape[1])
+
+        # define mask length
+        self.mask_length = int(length_TS * self.percentage_mask_length)
+
+        # define weight for each sample in the mixing up
+        w_ref = np.random.choice(
+            np.linspace(start=self.weight_ref_min, stop=1, num=1000), size=1
+        )
+        w_ts = (1 - w_ref) / 2
+
+        # define your ref as random permutation of X
+        ref = np.random.permutation(X[:])
+
+        n = int(ref.shape[0])
+
+        # define positive and negative sample arrays
+        _pos = np.zeros(shape=ref.shape)
+        _neg = np.zeros(shape=ref.shape)
+
+        all_indices = np.arange(start=0, stop=n)
+
+        for i_ref in range(n):
+            # remove the sample ref from the random choice of pos-neg
+            all_indices_without_ref = np.delete(arr=all_indices, obj=i_ref)
+
+            # choose a random sample used for the negative generation
+            index_neg = int(np.random.choice(all_indices_without_ref, size=1))
+
+            _ref = ref[i_ref].copy()
+
+            # remove the index_neg from choices
+            all_indices_without_ref_and_not_ref = np.delete(
+                arr=all_indices, obj=[i_ref, index_neg]
+            )
+
+            # choose samples used for the mixing up
+            index_ts1_pos = int(
+                np.random.choice(all_indices_without_ref_and_not_ref, size=1)
+            )
+            index_ts2_pos = int(
+                np.random.choice(all_indices_without_ref_and_not_ref, size=1)
+            )
+
+            index_ts1_neg = int(
+                np.random.choice(all_indices_without_ref_and_not_ref, size=1)
+            )
+            index_ts2_neg = int(
+                np.random.choice(all_indices_without_ref_and_not_ref, size=1)
+            )
+
+            _not_ref = ref[index_neg].copy()
+
+            _ts1_pos = ref[index_ts1_pos].copy()
+            _ts2_pos = ref[index_ts2_pos].copy()
+
+            _ts1_neg = ref[index_ts1_neg].copy()
+            _ts2_neg = ref[index_ts2_neg].copy()
+
+            # MixingUp
+
+            if self.use_mixing_up and self.use_masking:
+                # mix up the selected series with ref to obtain pos
+                _pos[i_ref] = w_ref * _ref + w_ts * _ts1_pos + w_ts * _ts2_pos
+                # mix up the selected series with neg ref to obtain neg
+                _neg[i_ref] = w_ref * _not_ref + w_ts * _ts1_neg + w_ts * _ts2_neg
+
+                # apply masking
+                _pos[i_ref], _neg[i_ref] = self._apply_masking(
+                    pos=_pos[i_ref],
+                    neg=_neg[i_ref],
+                    n_channels=n_channels,
+                    length_TS=length_TS,
+                    mask_length=self.mask_length,
+                )
+
+            elif self.use_mixing_up and not self.use_masking:
+                # mix up the selected series with ref to obtain pos
+                _pos[i_ref] = w_ref * _ref + w_ts * _ts1_pos + w_ts * _ts2_pos
+                # mix up the selected series with neg ref to obtain neg
+                _neg[i_ref] = w_ref * _not_ref + w_ts * _ts1_neg + w_ts * _ts2_neg
+
+            elif self.use_masking and not self.use_mixing_up:
+                # apply masking
+                _pos[i_ref], _neg[i_ref] = self._apply_masking(
+                    pos=_pos[i_ref],
+                    neg=_neg[i_ref],
+                    n_channels=n_channels,
+                    length_TS=length_TS,
+                    mask_length=self.mask_length,
+                )
+
+            else:
+                raise ValueError(
+                    "At least masking or mixing up",
+                    "should be chosen to generate",
+                    "the triplets.",
+                )
+        if self.z_normalize_pos_neg:
+            # z_normalize pos and neg
+            _pos_normalized = z_normalization(_pos)
+            _neg_normalized = z_normalization(_neg)
+
+            return ref, _pos_normalized, _neg_normalized
+        else:
+            return ref, _pos, _neg
+
+    def _apply_masking(self, pos, neg, n_channels, length_TS, mask_length):
+        """Apply masking phase on pos and neg."""
+        # select a random start for the mask
+        start_mask = int(np.random.randint(low=0, high=length_TS - mask_length, size=1))
+        stop_mask = start_mask + mask_length
+
+        # define noise on replacement on the left side of the mask
+        noise_pos_left = np.random.random(size=(start_mask, n_channels))
+        # normalize noise
+        noise_pos_left /= 5
+        noise_pos_left -= 0.1
+
+        # define noise on replacement on the left side of the mask
+        noise_pos_right = np.random.random(size=(length_TS - stop_mask, n_channels))
+        # normalize noise
+        noise_pos_right /= 5
+        noise_pos_right -= 0.1
+
+        # replace left and right side of the mask by normalized noise
+        pos[0:start_mask, :] = noise_pos_left
+        pos[stop_mask:length_TS, :] = noise_pos_right
+
+        # repeat the same procedure for the negative sample
+        noise_neg_left = np.random.random(size=(start_mask, n_channels))
+        noise_neg_left /= 5
+        noise_neg_left -= 0.1
+        noise_neg_right = np.random.random(size=(length_TS - stop_mask, n_channels))
+        noise_neg_right /= 5
+        noise_neg_right -= 0.1
+
+        neg[0:start_mask, :] = noise_neg_left
+        neg[stop_mask:length_TS, :] = noise_neg_right
+
+        return pos, neg
+
+    def save_last_model_to_file(self, file_path="./"):
+        """Save the last epoch of the trained deep learning model.
+
+        Parameters
+        ----------
+        file_path : str, default = "./"
+            The directory where the model will be saved
+
+        Returns
+        -------
+        None
+        """
+        self.model_.save(os.path.join(file_path, self.last_file_name + ".keras"))
+
+    def load_model(self, model_path):
+        """Load a pre-trained keras model instead of fitting.
+
+        When calling this function, all functionalities can be used
+        such as predict, predict_proba etc. with the loaded model.
+
+        Parameters
+        ----------
+        model_path : str (path including model name and extension)
+            The directory where the model will be saved including the model
+            name with a ".keras" extension.
+            Example: model_path="path/to/file/best_model.keras"
+
+        Returns
+        -------
+        None
+        """
+        import tensorflow as tf
+
+        self.model_ = tf.keras.models.load_model(model_path)
+        self.is_fitted = True
+
+    def _get_model_checkpoint_callback(self, callbacks, file_path, file_name):
+        import tensorflow as tf
+
+        model_checkpoint_ = tf.keras.callbacks.ModelCheckpoint(
+            filepath=os.path.join(file_path, file_name + ".keras"),
+            monitor="loss",
+            save_best_only=True,
+        )
+
+        if isinstance(callbacks, list):
+            return callbacks + [model_checkpoint_]
+        else:
+            return [callbacks] + [model_checkpoint_]
+
+    @classmethod
+    def _get_test_params(cls, parameter_set="default"):
+        """Return testing parameter settings for the transformer.
+
+        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.
+
+
+        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.
+        """
+        from aeon.networks import FCNNetwork
+
+        params = {
+            "latent_space_dim": 2,
+            "backbone_network": FCNNetwork(n_layers=1, n_filters=2, kernel_size=2),
+            "n_epochs": 3,
+        }
+
+        return [params]
diff --git a/aeon/utils/repr.py b/aeon/utils/repr.py
new file mode 100644
index 00000000..67b3b751
--- /dev/null
+++ b/aeon/utils/repr.py
@@ -0,0 +1,55 @@
+"""Utilities for class __repr__ presentation."""
+
+import inspect
+
+from aeon.testing.utils.deep_equals import deep_equals
+
+
+def get_unchanged_and_required_params_as_str(obj):
+    """
+    Get object parameters as a comma delimited string.
+
+    Collects the parameters of an object that are either required
+    (no default) or different from the __init__ default value. Returns
+    the parameter names and values as a comma delimited string.
+
+    Parameters
+    ----------
+    obj : object
+        The object to inspect.
+
+    Returns
+    -------
+    str
+        A string representation of the objects parameters and values.
+    """
+    cls = obj.__class__
+    signature = inspect.signature(cls.__init__)
+
+    params = {}
+    for name, param in signature.parameters.items():
+        if name == "self":
+            continue
+
+        has_default = param.default is not inspect.Parameter.empty
+        current_val = getattr(obj, name, None)
+
+        if not has_default:
+            # No default = always include
+            params[name] = current_val
+        else:
+            # Default exists = include if unchanged
+            if not deep_equals(current_val, param.default):
+                params[name] = current_val
+
+    if len(params) == 0:
+        return ""
+
+    param_str = []
+    for k, v in params.items():
+        if isinstance(v, str):
+            param_str.append(f"{k}='{v}'")
+        else:
+            param_str.append(f"{k}={v}")
+
+    return ", ".join(param_str)
diff --git a/aeon/utils/self_supervised/__init__.py b/aeon/utils/self_supervised/__init__.py
new file mode 100644
index 00000000..8de476b2
--- /dev/null
+++ b/aeon/utils/self_supervised/__init__.py
@@ -0,0 +1,1 @@
+"""Utils for self_supervised."""
diff --git a/aeon/utils/self_supervised/general.py b/aeon/utils/self_supervised/general.py
new file mode 100644
index 00000000..b3d2da7b
--- /dev/null
+++ b/aeon/utils/self_supervised/general.py
@@ -0,0 +1,28 @@
+"""General utils for self_supervised."""
+
+__all__ = ["z_normalization"]
+
+import numpy as np
+
+
+def z_normalization(X, axis=1):
+    """Z-Normalize collection of time series.
+
+    Parameters
+    ----------
+    X : np.ndarray
+        The input collection of time series of shape
+        (n_cases, n_channels, n_timepoints).
+    axis : int, default = 1
+        The axis of time, on which z-normalization
+        is performed.
+
+    Returns
+    -------
+    Normalized version of X.
+    """
+    stds = np.std(X, axis=axis, keepdims=True)
+    if len(stds[stds == 0.0]) > 0:
+        stds[stds == 0.0] = 1.0
+        return (X - X.mean(axis=axis, keepdims=True)) / stds
+    return (X - X.mean(axis=axis, keepdims=True)) / (X.std(axis=axis, keepdims=True))
diff --git a/docs/api_reference/transformations.rst b/docs/api_reference/transformations.rst
index 138ca4ff..d24aa471 100644
--- a/docs/api_reference/transformations.rst
+++ b/docs/api_reference/transformations.rst
@@ -126,6 +126,17 @@ Interval based
     SupervisedIntervals
     QUANTTransformer
 
+Self Supervised
+~~~~~~~~~~~~~~~
+
+.. currentmodule:: aeon.transformations.collection.self_supervised
+
+.. autosummary::
+    :toctree: auto_generated/
+    :template: class.rst
+
+    TRILITE
+
 Shapelet based
 ~~~~~~~~~~~~~~
 
diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md
index 0c49c2b0..65e3835a 100644
--- a/docs/code_of_conduct.md
+++ b/docs/code_of_conduct.md
@@ -37,7 +37,7 @@ If you believe someone is violating the CoC, we ask that you report it by
 contacting the COCW. All reports will be kept confidential. You can make a report by
 emailing the general `aeon` contact address [contact@aeon-toolkit.org](mailto:contact@aeon-toolkit.org),
 or by messaging [members of the COCW](./about.md#code-of-conduct-workgroup)
-directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA).
+directly on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw).
 
 Thank you for reading and helping make this a welcoming, friendly community for
 everyone.
diff --git a/docs/conf.py b/docs/conf.py
index 76203f98..89da0515 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -166,23 +166,55 @@ def linkcode_resolve(domain, info):
     def find_source():
         # try to find the file and line number, based on code from numpy:
         # https://github.com/numpy/numpy/blob/main/doc/source/conf.py#L286
-        obj = sys.modules[info["module"]]
-        for part in info["fullname"].split("."):
-            obj = getattr(obj, part)
+
         import inspect
         import os
 
-        obj = inspect.unwrap(obj)
+        # Get the top-level object from the module name
+        obj = sys.modules[info["module"]]
+
+        # Traverse dotted path (e.g., module.submodule.Class.method)
+        for part in info["fullname"].split("."):
+            obj = getattr(obj, part)
 
-        fn = inspect.getsourcefile(obj)
-        fn = os.path.relpath(fn, start=os.path.dirname(aeon.__file__))
+        # Unwrapping decorators (if any), so we can get the true
+        # source function
+        if inspect.isfunction(obj):
+            obj = inspect.unwrap(obj)
+
+        # Get the source filename
+        try:
+            fn = inspect.getsourcefile(obj)
+        except TypeError:
+            fn = None
+
+        # If no source file is found, return None (no link)
+        if not fn:
+            return None
+
+        # Make filename relative to the aeon source directory
+        startdir = Path(aeon.__file__).parent.parent
+        try:
+            fn = os.path.relpath(fn, start=startdir).replace(os.path.sep, "/")
+        except ValueError:
+            return None
+
+        # Filter out files not in the aeon package
+        # (e.g., inherited from sklearn)
+        if not fn.startswith("aeon/"):
+            return None
+
+        # Get line range of the object
         source, lineno = inspect.getsourcelines(obj)
         return fn, lineno, lineno + len(source) - 1
 
     if domain != "py" or not info["module"]:
         return None
     try:
-        filename = "aeon/%s#L%d-L%d" % find_source()
+        result = find_source()
+        if not result:
+            return None
+        filename = "%s#L%d-L%d" % result
     except Exception:
         filename = info["module"].replace(".", "/") + ".py"
     return "https://github.com/aeon-toolkit/aeon/blob/{}/{}".format(
@@ -217,7 +249,7 @@ html_theme_options = {
     "footer_icons": [
         {
             "name": "Slack",
-            "url": "https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA",  # noqa: E501
+            "url": "https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw",  # noqa: E501
             "html": """
             <svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
                 <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zM361.5 580.2c0 27.8-22.5 50.4-50.3 50.4-13.3 0-26.1-5.3-35.6-14.8-9.4-9.5-14.7-22.3-14.7-35.6 0-27.8 22.5-50.4 50.3-50.4h50.3v50.4zm134 134.4c0 27.8-22.5 50.4-50.3 50.4-27.8 0-50.3-22.6-50.3-50.4V580.2c0-27.8 22.5-50.4 50.3-50.4 13.3 0 26.1 5.3 35.6 14.8s14.7 22.3 14.7 35.6v134.4zm-50.2-218.4h-134c-27.8 0-50.3-22.6-50.3-50.4 0-27.8 22.5-50.4 50.3-50.4h134c27.8 0 50.3 22.6 50.3 50.4-.1 27.9-22.6 50.4-50.3 50.4zm0-134.4c-13.3 0-26.1-5.3-35.6-14.8S395 324.8 395 311.4c0-27.8 22.5-50.4 50.3-50.4 27.8 0 50.3 22.6 50.3 50.4v50.4h-50.3zm83.7-50.4c0-27.8 22.5-50.4 50.3-50.4 27.8 0 50.3 22.6 50.3 50.4v134.4c0 27.8-22.5 50.4-50.3 50.4-27.8 0-50.3-22.6-50.3-50.4V311.4zM579.3 765c-27.8 0-50.3-22.6-50.3-50.4v-50.4h50.3c27.8 0 50.3 22.6 50.3 50.4 0 27.8-22.5 50.4-50.3 50.4zm134-134.4h-134c-13.3 0-26.1-5.3-35.6-14.8S529 593.6 529 580.2c0-27.8 22.5-50.4 50.3-50.4h134c27.8 0 50.3 22.6 50.3 50.4 0 27.8-22.5 50.4-50.3 50.4zm0-134.4H663v-50.4c0-27.8 22.5-50.4 50.3-50.4s50.3 22.6 50.3 50.4c0 27.8-22.5 50.4-50.3 50.4z"></path>
diff --git a/docs/contributing.md b/docs/contributing.md
index a090e1d3..1b100327 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -38,7 +38,7 @@ stuck, please don’t hesitate to talk with us or raise an issue.
 Recommended steps for first time contributors, or to get started with regular
 contributions:
 
-1. Say hello in the `introductions` or `contributors` channel on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA)
+1. Say hello in the `introductions` or `contributors` channel on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw)
 and mention which places you are interested in contributing to.
 2. Get setup for development, see the [developer install instructions](developer_guide/dev_installation.md)
 for creating a fork of `aeon`.
@@ -84,7 +84,7 @@ contributing, but if your issue to related to a specific topic i.e. documentatio
 testing you may find it useful.
 
 If your intended method of contribution does not fit into the above steps, please
-reach out to us on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA)
+reach out to us on [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw)
 for discussion. While GitHub contributions are the most common, it is not the only
 way to contribute to `aeon`.
 
@@ -171,7 +171,7 @@ Mentoring and Projects
 
 +++
 
-```{button-ref} mentoring
+```{button-ref} projects
 :color: primary
 :click-parent:
 :expand:
diff --git a/docs/index.md b/docs/index.md
index 11b55883..e19c1814 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -21,7 +21,7 @@ segmentation and similarity search.
 
 **GitHub**: [github.com/aeon-toolkit/aeon](https://github.com/aeon-toolkit/aeon)
 
-**Slack**: [aeon slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA)
+**Slack**: [aeon slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw)
 
 **Twitter**: [twitter/aeon-toolkit](https://twitter.com/aeon_toolkit)
 
@@ -276,6 +276,7 @@ experimental modules are:
 - `segmentation`
 - `similarity_search`
 - `visualisation`
+- `transformations.collection.self_supervised`
 
 ```{toctree}
 :caption: Using aeon
@@ -293,7 +294,7 @@ examples.md
 
 contributing.md
 developer_guide.md
-mentoring.md
+projects.md
 ```
 
 ```{toctree}
diff --git a/docs/mentoring.md b/docs/mentoring.md
deleted file mode 100644
index a937b429..00000000
--- a/docs/mentoring.md
+++ /dev/null
@@ -1,815 +0,0 @@
-
-# Mentoring and Projects
-
-`aeon` runs a range of short to medium duration projects interacting with the community
-and the code
-base. These projects are designed for internships, usage as part of
-undergraduate/postgraduate projects at academic institutions, and as options for
-programs such as [Google Summer of Code (GSoC)](https://summerofcode.withgoogle.com/).
-
-For those interested in undertaking a project outside these scenarios, we recommend
-joining the [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA)
-and discussing with the project mentors. We aim to run schemes to
-help new contributors to become more familiar with `aeon`, time series machine learning
-research, and open-source software development.
-
-All the projects listed will require knowledge of Python 3 and Git/GitHub. The
-majority of them will require some knowledge of machine learning and time series.
-
-## Current aeon projects
-
-This is a list of some of the projects we are interested in running in 2024. Feel
-free to propose your own project ideas, but please discuss them with us first. We have
-an active community of researchers and students who work on `aeon`. Please get in touch
-via Slack if you are interested in any of these projects or have any questions.
-
-We will more widely advertise funding opportunities as and when they become available.
-
-Most projects can be extended, possibly into a research project that may lead to
-publication. These projects are for anyone, from core devs to those completely new
-to open source. We list projects by time series task
-
-[Classification](#classification)
-1. Optimizing the Shapelet Transform for classification and similarity search
-2. EEG classification with aeon-neuro (Listed for GSoC 2024)
-3. Improved Proximity Forest for classification (listed for GSoC 2024)
-4. Improved HIVE-COTE implementation.
-5. Compare distance based classification.
-
-[Forecasting](#forecasting)
-1. Machine Learning for Time Series Forecasting (listed in GSoC 2024)
-2. Deep Learning for Time Series Forecasting
-3. Implement ETS forecasters in aeon
-
-[Clustering](#clustering)
-1. Density peaks clustering algorithm
-2. Deep learning based clustering algorithms
-
-[Anomaly Detection](#anomaly-detection)
-1. Anomaly detection with the Matrix Profile and MERLIN
-
-[Segmentation](#segmentation)
-1. Time series segmentation
-
-[Transformation](#transformation)
-1. Improve ROCKET family of transformers
-2. Implement channel selection algorithms
-
-[Visualisation](#visualisation)
-1. Explainable AI with the shapelet transform (Southampton intern project).
-
-[Regression](#regression)
-1. Adapt forecasting regressors to time series extrinsic regression.
-2. Adapt HIVE-COTE for regression.
-
-[Documentation](#documentation)
-1. Improve automated API documentation
-
-### Classification
-
-#### 1. Optimizing the Shapelet Transform for Classification and Similarity Search (listed for GSoC 2024)
-
-Mentors : Antoine Guillaume ({user}`baraline`) and Tony Bagnall ({user}`TonyBagnall`)
-
-##### Related Issues
-[#186](https://github.com/aeon-toolkit/aeon/issues/186)
-[#324](https://github.com/aeon-toolkit/aeon/issues/324)
-[#894](https://github.com/aeon-toolkit/aeon/issues/894)
-[#973](https://github.com/aeon-toolkit/aeon/issues/973)
-[#1184](https://github.com/aeon-toolkit/aeon/issues/1184)
-[#1322](https://github.com/aeon-toolkit/aeon/issues/1322)
-
-
-##### Description
-
-A shapelet is defined as a time series subsequence representing a pattern of interest
-that we wish to search for in time series data. Shapelet-based algorithms can be used
-for a wide range of time series tasks. In this project, we will focus on its core
-application, which is to create an embedding of the input time series.
-
-Our goal in this project will be to optimize the code related to the shapelet
-transform method, which takes as input a set of shapelets and a time series dataset,
-and give as output a tabular dataset containing the features characterizing the
-presence (or absence) of each shapelet in the input time series (more information
-in [1] and [2]).
-
-Similarity search is another field of time series, which has proposed greatly optimized
-algorithms (see [3] and [4]) for the task of finding the best matches of a subsequence
-inside another time series. As this task is extremely similar to what is done in the
-shapelet transform, we want to adapt these algorithms to the context of shapelets,
-in order to achieve significant speed-ups.
-
-##### Project stages
-
-To achieve this goal, with the assistance of the mentor, we identify the following
-steps for the mentee:
-
-1. Learn about aeon best practices, coding standards and testing policies.
-2. Study the shapelet transform algorithm and how it is related to the task of
-similarity search.
-3. Study the similarity search algorithms for the Euclidean distance and the
-computational optimization they use.
-4. Propose a candidate implementation for to increase the performance of the
-computations made by a single shapelet. This can be made with the help of the existing
-implementation of the similarity search module in `aeon`.
-5. Measure the performance of this first candidate implementation against the current
-approach.
-6. Implement this solution to the shapelet transform algorithm, which uses multiple
-shapelets.
-7. Benchmark the implementation against the original shapelet transform algorithm.
-8. If time, generalize this new algorithm to the case of dilated shapelets (see [5]).
-
-##### Expected Outcomes
-
-We expect the mentee to engage with the aeon community and produce a more performant
-implementation for the shapelet transform that gets accepted into the toolkit.
-
-##### References
-
-1. Hills, J., Lines, J., Baranauskas, E., Mapp, J. and Bagnall, A., 2014.
-Classification of time series by shapelet transformation. Data mining and knowledge
-discovery, 28, pp.851-881.
-2. Bostrom, A. and Bagnall, A., 2017. Binary shapelet transform for multiclass time
-series classification. Transactions on Large-Scale Data-and Knowledge-Centered Systems
-XXXII: Special Issue on Big Data Analytics and Knowledge Discovery, pp.24-46.
-3. Yeh, C.C.M., Zhu, Y., Ulanova, L., Begum, N., Ding, Y., Dau, H.A., Silva, D.F.,
-Mueen, A. and Keogh, E., 2016, December. Matrix profile I: all pairs similarity joins
-for time series: a unifying view that includes motifs, discords and shapelets. In 2016
-IEEE 16th international conference on data mining (ICDM) (pp. 1317-1322). Ieee.
-4. Zhu, Y., Zimmerman, Z., Senobari, N.S., Yeh, C.C.M., Funning, G., Mueen, A., Brisk,
-P. and Keogh, E., 2016, December. Matrix profile ii: Exploiting a novel algorithm and
-gpus to break the one hundred million barrier for time series motifs and joins. In 2016
-IEEE 16th international conference on data mining (ICDM) (pp. 739-748). IEEE.
-5. Guillaume, A., Vrain, C. and Elloumi, W., 2022, June. Random dilated shapelet
-transform: A new approach for time series shapelets. In International Conference on
-Pattern Recognition and Artificial Intelligence (pp. 653-664). Cham: Springer
-International Publishing.
-
-#### 2. EEG classification with aeon-neuro (Listed for GSoC 2024)
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`) and Aiden Rushbrooke
-
-##### Related Issues
-[#18](https://github.com/aeon-toolkit/aeon-neuro/issues/18)
-[#19](https://github.com/aeon-toolkit/aeon-neuro/issues/19)
-[#24](https://github.com/aeon-toolkit/aeon-neuro/issues/24)
-
-
-
-##### Description
-
-EEG (Electroencephalogram) data are high dimensional time series that are used in
-medical, psychology and brain computer interface research. For example, EEG are
-used to detect epilepsy and to control devices such as mice. There is a huge body
-of work on analysing and learning from EEG, but there is a wide disparity of
-tools, practices and systems used. This project will help members of the `aeon`
-team who are currently researching techniques for EEG classification [1] and
-developing an aeon sister toolkit, [``aeon-neuro``](https://github.com/aeon-toolkit/aeon-neuro). We will work together to
-improve the structure and documentation for aeon-neuro, help integrate the
-toolkit with existing EEG toolkits such as MNE [2], provide interfaces to standard data
-formats such as BIDS [3] and help develop and assess a range of EEG classification
-algorithms.
-
-##### Project stages
-
-1. Learn about aeon best practices, coding standards and testing policies.
-2. Study the existing techniques for EEG classification.
-3. Implement or wrap standard EEG processing algorithms.
-4. Evaluate aeon classifiers for EEG problems.
-5. Implement alternatives transformations for preprocessing EEG data.
-6. Help write up results for a technical report/academic paper (depending on outcomes).
-
-##### Expected Outcomes
-
-We would expect a better documented and more integrated aeon-neuro toolkit with
-better functionality and a wider appeal.
-
-##### References
-
-1. Aiden Rushbrooke, Jordan Tsigarides, Saber Sami, Anthony Bagnall,
-Time Series Classification of Electroencephalography Data, IWANN 2023.
-2. MNE Toolkit, https://mne.tools/stable/index.html
-3. The Brain Imaging Data Structure (BIDS) standard, https://bids.neuroimaging.io/
-
-#### 3. Improved Proximity Forest for classification (listed for GSoC 2024)
-
-Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`) and Tony Bagnall
-({user}`TonyBagnall`)
-
-##### Related Issues
-[#159](https://github.com/aeon-toolkit/aeon/issues/159)
-[#428](https://github.com/aeon-toolkit/aeon/issues/428)
-
-
-##### Description
-
-Distance-based classifiers such as k-Nearest Neighbours are popular approaches to time
-series classification. They primarily use elastic distance measures such as Dynamic Time
-Warping (DTW) to compare two series. The Proximity Forest algorithm [1] is a
-distance-based classifier for time series. The classifier creates a forest of decision
-trees, where the tree splits are based on the distance between time series using
-various distance measures. A recent review of time series classification algorithms [2]
-found that Proximity Forest was the most accurate distance-based algorithm of those
-compared.
-
-`aeon` previously had an implementation of the Proximity Forest algorithm, but it was
-not as accurate as the original implementation (the one used in the study) and was
-unstable on benchmark datasets. The goal of this project is to significantly overhaul
-the previous implementation or completely re-implement Proximity Forest in `aeon` to
-match the accuracy of the original algorithm. This will involve comparing against the
-authors' Java implementation of the algorithm as well as alternate Python versions.
-The mentors will provide results for both for alternative methods. While knowing
-Java is not a requirement for this project, it could be beneficial.
-
-Recently, the group which published the algorithm has proposed a new version of the
-Proximity Forest algorithm, Proximity Forest 2.0 [3]. This algorithm is more accurate
-than the original Proximity Forest algorithm, and does not currently have an
-implementation in `aeon` or elsewhere in Python. If time allows, the project could also
-involve implementing and evaluating the Proximity Forest 2.0 algorithm.
-
-##### Project stages
-
-1. Learn about `aeon` best practices, coding standards and testing policies.
-2. Study the Proximity Forest algorithm and previous `aeon` implementation.
-3. Improve/re-implement the Proximity Forest implementation in `aeon`, with
-the aim being to have an implementation that is as accurate as the original algorithm,
-while remaining feasible to run.
-4. Evaluate the improved implementation against the original `aeon` Proximity Forest
-and the authors' Java implementation.
-5. If time, implement the Proximity Forest 2.0 algorithm and repeat the above
-evaluation.
-
-##### Expected Outcomes
-
-We expect the mentee engage with the aeon community and produce a high quality
-implementation of the Proximity Forest algorithm(s) that gets accepted into the toolkit.
-
-##### References
-
-1. Lucas, B., Shifaz, A., Pelletier, C., O’Neill, L., Zaidi, N., Goethals,
-B., Petitjean, F. and Webb, G.I., 2019. Proximity forest: an effective and scalable
-distance-based classifier for time series. Data Mining and Knowledge Discovery, 33(3),
-pp.607-635.
-2. Middlehurst, M., Schäfer, P. and Bagnall, A., 2023. Bake off redux: a review and
-experimental evaluation of recent time series classification algorithms. arXiv preprint
-arXiv:2304.13029.
-3. Herrmann, M., Tan, C.W., Salehi, M. and Webb, G.I., 2023. Proximity Forest 2.0: A
-new effective and scalable similarity-based classifier for time series. arXiv
-preprint arXiv:2304.05800.
-
-#### 4. Improved HIVE-COTE implementation
-
-Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`) and Tony Bagnall
-({user}`TonyBagnall`)
-
-##### Related Issues
-[#663](https://github.com/aeon-toolkit/aeon/issues/663)
-
-##### Description
-
-The Hierarchical Vote Collective of Transformation-based Ensembles (HIVE-COTE) [1,2,3]
-is a time series classifier that has claims to be state-of-the-art,
-particularly in terms of probabilistic estimates [4]. There have been several iterations
-that use different base classifiers but they all share the same design basic:
-classifiers using different representations are combined in a weighted meta-ensemble
-[4]. There are two HIVE-COTE implementations currently in `aeon`: HIVE-COTEV1 [2]
-and HIVECOTEV2 [3]. This project will involve combining these into a single
-estimator, modularising the ensemble stage and possibly experimenting with
-alternative structures. This can easily develop into a research project.
-
-##### Project stages
-
-1. Learn about `aeon` best practices, coding standards and testing policies.
-2. Study the HIVE-COTE algorithm and previous `aeon` implementation.
-3. Combine the HIVE-COTEV1 and HIVE-COTEV2 classifiers into a single classifier
-   configurable into different versions through the constructor.
-4. Restructure the ensemble stage to allow easy experimentation with variants.
-
-##### References
-
-1. A Bagnall, J Lines, J Hills, A Bostrom, Time-series classification with COTE: the
-   collective of transformation-based ensembles, IEEE Transactions on Knowledge and
-   Data Engineering 27 (9), 2522-2535
-2. M Middlehurst, J Large, M Flynn, J Lines, A Bostrom, A Bagnall, HIVE-COTE 2.0: a
-   new meta ensemble for time series classification, Machine Learning 110 (11),
-   3211-3243
-3. J Lines, S Taylor, A Bagnall, Time series classification with HIVE-COTE: The
-   hierarchical vote collective of transformation-based ensembles, ACM Transactions
-   on Knowledge Discovery from Data (TKDD) 12 (5), 1-35
-4. Middlehurst, M., Schäfer, P. and Bagnall, A., 2023. Bake off redux: a review and
-experimental evaluation of recent time series classification algorithms. arXiv preprint
-arXiv:2304.13029.
-
-#### 5. Compare distance based classification and regression
-
-Mentors: Chris Holder ({user}`cholder`) and Tony Bagnall
-({user}`TonyBagnall`)
-
-##### Related Issues
-[#423](https://github.com/aeon-toolkit/aeon/issues/423)
-[#424](https://github.com/aeon-toolkit/aeon/issues/424)
-[#425](https://github.com/aeon-toolkit/aeon/issues/425)
-[#426](https://github.com/aeon-toolkit/aeon/issues/426)
-[#427](https://github.com/aeon-toolkit/aeon/issues/427)
-[#488](https://github.com/aeon-toolkit/aeon/issues/488)
-
-##### Description
-Distance based algorithms are popular for time series classification and regression.
-However, the evaluation of distance functions for classification have not
-comprehensively covered all possible uses. For example, there has not been a proper
-bake off for using elastic distance with support vector machines or with tuning
-distance functions and classifiers in combination. This project will combine
-implementing alternative distance functions and comparing performance on the UCR
-datasets.
-
-
-### Forecasting
-
-#### 1. Machine Learning for Time Series Forecasting (listed in GSoC 2024)
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`) and Matthew Middlehurst (@MatthewMiddlehurst).
-
-##### Related Issues
-[#265](https://github.com/aeon-toolkit/aeon/issues/265)
-
-
-##### Description
-
-This project will investigate algorithms for forecasting based on traditional machine
-learning (tree based) and time series machine learning (transformation based). Note
-this project will not involve deep learning based forecasting. It will involve
-helping develop the `aeon` framework to work more transparently with ML algorithms,
-evaluating regression algorithms already in `aeon`[1] for forecasting problems and
-implementing at least one algorithm from the literature not already in aeon, such as
-SETAR-Tree [3].
-
-##### Project Stages
-1. Learn about aeon best practices, coding standards and testing policies.
-2. Adapt the M competition set up [2] for ML experimental framework to assess time
-   series regression algorithms [1].
-3. Implement a machine learning forecasting algorithm [3]
-
-##### Expected Outcomes
-
-1. Contributions to the aeon forecasting module.
-2. Implementation of a machine learning forecasting algorithms.
-3. Help write up results for a technical report/academic paper (depending on outcomes).
-
-##### Skills Required
-
-1. Python 3
-2. Git and GitHub
-3. Some machine learning and/or forecasting background (e.g. taught courses or
-   practical experience)
-
-##### References
-
-1. Guijo-Rubio, D.,Middlehurst, M., Arcencio, G., Furtado, D. and Bagnall, A.
-Unsupervised Feature Based Algorithms for Time Series Extrinsic Regression,
-arXiv2305.01429, 2023
-2. https://forecasters.org/resources/time-series-data/
-3. Godahewa, R., Webb, G.I., Schmidt, D. et al. SETAR-Tree: a novel and accurate
-tree algorithm for global time series forecasting. Mach Learn 112, 2555–2591 (2023).
-https://link.springer.com/article/10.1007/s10994-023-06316-x
-
-#### 2. Deep Learning for Time Series Forecasting
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`)  and Ali Ismail-Fawaz ({user}
-`hadifawaz1999`)
-
-##### Description
-
-Deep learning has become incredibly popular for forecasting, see [1] for an
-introduction. This project will involve taking one or more recently proposed
-algorithms, implementing them in aeon, then performing an extensive experimental
-comparison against traditional and machine learning algorithms. As part of this, we
-will collate results from the M Competitions [2]
-
-##### Project Stages
-1. Learn about aeon best practices, coding standards and testing policies.
-2. Adapt the M competition set up [2] for deep learning.
-3. Implement a deep learning forecasting algorithm after discussion with mentors.
-
-##### Expected Outcomes
-
-1. Collated M competition results and partial reproduction.
-2. Extend the forecasting module to include at least one deep forecaster.
-
-##### References
-
-1. [ECML 2024 Tutorial](https://lovvge.github.io/Forecasting-Tutorial-ECML-2023/)
-2. [M Competitions](https://forecasters.org/resources/time-series-data/)
-
-
-#### 3. Implement ETS forecasters
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`)  and Leo Tsaprounis ({user}`ltsaprounis`)
-Exponential smoothing (ETS) is a popular family of algorithms for forecasting, and
-the ETS framework by Hyndman et al. [1] covers 30 possible models for time series
-with different types of Error, Trend, and Seasonal components.
-we already have an (Auto)ETS model in aeon, but it’s wrapping statsmodels. We would
-like our own bespoke, optimised implementation based on the R implementation.
-
-##### Project Stages
-1. Learn about aeon best practices, coding standards and testing policies.
-2. Survey and benchmark existing implementations of ETS forecasting.
-3. Implement basic implementations optimised for numba.
-4. Extended implementation to include modern refinements.
-
-
-##### References
-
-1. Hydman et al. [Forecasting with Exponential Smoothing The State Space Approach](https://link.springer.com/book/10.1007/978-3-540-71918-2)
-2. [Smooth R Package](https://github.com/config-i1/smooth)
-3. Svetunkov, [Forecasting and Analytics with the Augmented Dynamic Adaptive Model
-   (ADAM)](https://openforecast.org/adam/)
-
-### Clustering
-
-#### 1. Density peaks clustering algorithm
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`) and Chris  Holder (`@chrisholder`).
-
-##### Description
-
-The clustering module in `aeon`, up until now, clusters using time series
-specific distance functions with partitional clustering algorithms such as k-means and
-k-medoids. An alternative clustering algorithm is density peaks (DP) [1]. This
-clustering
-algorithm has the benefit of not having to label all cases as cluster members, which
-means it can easily be adapted to anomaly detection [2]. It is
-a general purpose clustering algorithm that is not available in scikit learn. This
-project will implement the algorithm based on Java and matlab implementations then
-compare performance against partitional clustering for time series clustering.
-
-##### Project Stages
-
-1. Research and understand how DP works.
-2. Implement DP as an aeon estimator.
-3. Test the implementation against other implementations for correctness.
-4. Compare against alternative TSCL algorithms
-
-##### Expected Outcomes
-
-1. A well documented, tested and efficient implementation of DP
-2. Possible extensions to reflect recent research [2] with specific time series
-   components [3].
-2. Contributions to a comparative study and paper.
-
-##### References
-1. Rodriguez, A., & Laio, A. Clustering by Fast Search and Find of
-Density Peaks. [Science](https://www.science.org/doi/10.1126/science.1242072), 344
-   (6191), 1492-1496, 2014.
-2. Chen, L., Gao, S. & Liu, B. An improved density peaks clustering algorithm
-based on grid screening and mutual neighborhood degree for network anomaly detection.
-Sci Rep 12, 1409 (2022) [DOI](https://doi.org/10.1038/s41598-021-02038-z)
-3. Begum et al. A General Framework for Density Based  Time Series Clustering
-   Exploiting a Novel Admissible Pruning Strategy, [arXiv](https://arxiv.org/ftp/arxiv/papers/1612/1612.00637.pdf)
-
-
-#### 2. Deep learning for clustering
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`)  and Ali Ismail-Fawaz ({user}
-`hadifawaz1999`)
-
-The clustering module in `aeon`, up until now, primarily consists of distance-based
-partitional clustering algorithms. Recently, we introduced a deep clustering module,
-incorporating distance-based algorithms in the latent space.
-
-The objective of this project is to enhance `aeon` by incorporating more deep learning
-approaches for time series clustering. The specific goal is to implement and assess
-InceptionTime [1] and its recent variants as a clustering algorithm, and contribute to
-an ongoing collaborative effort into a bake off for clustering. More widely, there
-are a broad range of deep learning clustering approaches we could consider [2].
-
-##### Project Stages
-
-1. Research and understand clustering time series and deep learning based approaches.
-2. Implement inception time as an aeon clusterer.
-3. Compare performance of deep learning clusterers to distance based algorithms.
-
-[1] Fawaz et al. InceptionTime: Finding AlexNet for time series classification
-Published: 07 September 2020 Volume 34, pages 1936–1962, (2020)
-[2] Deep learning forecasting [tutorial](https://lovvge.github.io/Forecasting-Tutorial-ECML-2023/)
-
-### Anomaly detection
-
-
-#### 1. Anomaly detection with the Matrix Profile and MERLIN
-
-Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`)
-
-##### Description
-
-`aeon` is looking to extend its module for time series anomaly detection. The
-end goal of this project is to implement the Matrix Profile [1][2] and MERLIN [3]
-algorithms, but suitable framework for anomaly detection in `aeon` will need to be
-designed first. The mentee will help design the API for the anomaly detection module
-and implement the Matrix Profile and MERLIN algorithms.
-
-Usage of external libraries such as `stumpy` [4] is possible for the algorithm
-implementations, or the mentee can implement the algorithms from scratch using `numba`.
-There is also scope to benchmark the implementations, but as there is no existing
-anomaly detection module in `aeon`, this will require some infrastructure to be
-developed and is subject to time and interest.
-
-##### Project stages
-
-1. Learn about `aeon` best practices, coding standards and testing policies.
-2. Familiarise yourself with similar single series experimental modules in `aeon` such
-as segmentation and similarity search.
-3. Help design the API for the anomaly detection module.
-4. Study and implement the Matrix Profile for anomaly detection and MERLIN algorithms
-using the new API.
-5. If time allows and there is interest, benchmark the implementations against the
-original implementations or other anomaly detection algorithms.
-
-##### Project Outcome
-
-As the anomaly detection is a new module in `aeon`, there is very little existing code
-to compare against and little infrastructure to evluate anomaly detection algorithms.
-The success of the project will be evaluated by the quality of the code produced and
-engagement with the project and the `aeon` community.
-
-##### References
-
-1. Yeh, C.C.M., Zhu, Y., Ulanova, L., Begum, N., Ding, Y., Dau, H.A., Silva, D.F.,
-Mueen, A. and Keogh, E., 2016, December. Matrix profile I: all pairs similarity joins
-for time series: a unifying view that includes motifs, discords and shapelets. In 2016
-IEEE 16th international conference on data mining (ICDM) (pp. 1317-1322). Ieee.
-2. Lu, Y., Wu, R., Mueen, A., Zuluaga, M.A. and Keogh, E., 2022, August.
-Matrix profile XXIV: scaling time series anomaly detection to trillions of datapoints
-and ultra-fast arriving data streams. In Proceedings of the 28th ACM SIGKDD Conference
-on Knowledge Discovery and Data Mining (pp. 1173-1182).
-3. Nakamura, T., Imamura, M., Mercer, R. and Keogh, E., 2020, November. Merlin:
-Parameter-free discovery of arbitrary length anomalies in massive time series archives.
-In 2020 IEEE international conference on data mining (ICDM) (pp. 1190-1195). IEEE.
-4. Law, S.M., 2019. STUMPY: A powerful and scalable Python library for time series data
-mining. Journal of Open Source Software, 4(39), p.1504.
-
-### Segmentation
-
-#### 1. Time series segmentation
-
-Mentors: Tony Bagnall ({user}`TonyBagnall`) and TBC
-
-##### Description
-
-The time series segmentation module contains a range of algorithms for segmenting time
-series. The goal of this project is to extend the functionality of segmentation in
-`aeon` and develop tools for comparing segmentation algorithms.
-
-##### Project stages
-
-1. Learn about `aeon` best practices, coding standards and testing policies.
-2. Study the existing segmentation algorithms in `aeon`.
-3. Implement existing segmentation algorithms, e.g.
-https://github.com/aeon-toolkit/aeon/issues/948
-4. Implement tools for comparing segmentation algorithms
-5. Conduct a bake off of segmentation algorithms on a range of datasets.
-
-##### Project Outcome
-
-As with all research programming based projects, progress can be hindered by many
-unforseen circumstances. Success will be measured by engagement, effort and
-willingness to join the community rather than performance of the algorithms.
-
-##### References
-
-1. Allegra, M., Facco, E., Denti, F., Laio, A. and Mira, A., 2020. Data segmentation
-based on the local intrinsic dimension. Scientific Reports, 10(1), p.16449.
-2. Ermshaus, A., Schäfer, P. and Leser, U., 2023. ClaSP: parameter-free time series
-segmentation. Data Mining and Knowledge Discovery, 37(3), pp.1262-1300.
-3. Hallac, D., Nystrup, P. and Boyd, S., 2019. Greedy Gaussian segmentation of
-multivariate time series. Advances in Data Analysis and Classification, 13(3),
-pp.727-751.
-4. Matteson, D.S. and James, N.A., 2014. A nonparametric approach for multiple change
-point analysis of multivariate data. Journal of the American Statistical Association,
-109(505), pp.334-345.
-5. Sadri, A., Ren, Y. and Salim, F.D., 2017. Information gain-based metric for
-recognizing transitions in human activities. Pervasive and Mobile Computing, 38,
-pp.92-109.
-
-### Transformation
-
-#### 1. Improve ROCKET family of transformers
-
-Mentors: Ali Ismail-Fawaz ({user}`hadifawaz1999`) and Matthew Middlehurst
-({user}`MatthewMiddlehurst`)
-[#208](https://github.com/aeon-toolkit/aeon/issues/208)
-[#214](https://github.com/aeon-toolkit/aeon/issues/214)
-[#313](https://github.com/aeon-toolkit/aeon/issues/313)
-[#1126](https://github.com/aeon-toolkit/aeon/issues/1126)
-[#1248](https://github.com/aeon-toolkit/aeon/issues/1248)
-
-
-##### Description
-
-The ROCKET algorithm [1] is a very fast and accurate transformation designed for time
-series classification. It is based on a randomly initialised convolutional kernels that
-are applied to the time series and used to extract summary statistics. ROCKET has
-applications to time series classification, extrinsic regression and anomaly detection,
-but as a fast and unsupervised transformation, it has potential to a wide range of
-other time series tasks.
-
-`aeon` has implementations of the ROCKET transformation and its variants, including
-MiniROCKET [2] and MultiROCKET [3]. However, these implementations have room for
-improvement ([#208](https://github.com/aeon-toolkit/aeon/issues/208)). There is scope
-to speed up the implementations, and the amount of varients is likely unnecessary and
-could be condensed into higher quality estimators.
-
-This projects involves improving the existing ROCKET implementations in `aeon` or
-implementing new ROCKET variants. The project will involve benchmarking to ensure that
-the new implementations are as fast and accurate as the original ROCKET algorithm and
-potentially to compare to other implementations ([#214](https://github.com/aeon-toolkit/aeon/issues/214)).
-Besides improving the existing implementations, there is scope to implement the HYDRA
-algorithm [4] or implement GPU compatible versions of the algorithms.
-
-##### Project Stages
-
-1. Learn about `aeon` best practices, coding standards and testing policies.
-2. Study the ROCKET, MiniROCKET, MultiROCKET algorithms.
-3. Study the existing ROCKET implementations in `aeon`.
-4. Merge and tidy the ROCKET implementations, with the aim being to familiarise the
-mentee with the `aeon` pull request process.
-5. Implement one (or more) of the proposed ROCKET implementation improvements:
-   * Significantly alter the current ROCKET implementations with the goal of
-   speeding up the implementation on CPU processing.
-   * Implement a GPU version of some of the ROCKET transformers, using either
-   `tensorflow` or `pytorch`.
-   * Extend the existing ROCKET implementations to allow for the use of unequal length
-   series.
-   * Implement the HYDRA algorithm.
-6. Benchmark the implementation against the original ROCKET implementations, looking at
-booth speed of the transform and accuracy in a classification setting.
-
-##### Project Outcomes
-
-Success of the project will be assessed by the quality of the code produced and an
-evaluation of the transformers in a classification setting. None of the implementations
-should significantly degrade the performance of the original ROCKET algorithm in terms
-of accuracy and speed. Regardless, effort and engagement with the project and the
-`aeon` community are more important factors in evaluating success.
-
-##### References
-
-1. Dempster, A., Petitjean, F. and Webb, G.I., 2020. ROCKET: exceptionally fast and
-accurate time series classification using random convolutional kernels.
-Data Mining and Knowledge Discovery, 34(5), pp.1454-1495.
-2. Dempster, A., Schmidt, D.F. and Webb, G.I., 2021, August. Minirocket: A very fast
-(almost) deterministic transform for time series classification. In Proceedings of the
-27th ACM SIGKDD conference on knowledge discovery & data mining (pp. 248-257).
-3. Tan, C.W., Dempster, A., Bergmeir, C. and Webb, G.I., 2022. MultiRocket: multiple
-pooling operators and transformations for fast and effective time series classification.
-Data Mining and Knowledge Discovery, 36(5), pp.1623-1646.
-4. Dempster, A., Schmidt, D.F. and Webb, G.I., 2023. Hydra: Competing convolutional
-kernels for fast and accurate time series classification. Data Mining and Knowledge
-Discovery, pp.1-27.
-
-#### 2. Implement channel selection algorithms
-
-Related issues:
-[#1270](https://github.com/aeon-toolkit/aeon/issues/1270)
-[#1467](https://github.com/aeon-toolkit/aeon/issues/1467)
-
-Channel selection in this context is the process of reducing the number of channels
-in a collection of time series for classification, clustering or regression. This
-project looks at filter based approaches to speed up multivariate time series
-classification (MTSC) of high dimensional series. Standard approaches for
-classifying high dimensional data are to
-employ a filter to select a subset of attributes or to transform the data into a lower
-dimensional feature space using, for example, principal component analysis. Our
-focus is on dimensionality reduction through filtering. For MTSC, filtering is
-generally accepted to be selecting the most important dimensions to use before
-training the classifier. Dimension selection can, on average, either increase, not
-change or decrease the accuracy of classification. The first case implies that the
-higher dimensionality is confounding the classifier’s discriminatory power. In the
-second case it is often still desirable to filter due to improved training time. In
-the third case, filtering may still be desirable, depending on the trade-off between
-performance (e.g. accuracy) and efficiency (e.g. train time): a small reduction in
-accuracy may be acceptable if build time reduces by an order of magnitude. We
-address the task of how best to select a subset of dimensions for high dimensional
-data so that we can speed up and possibly improve HC2 on high dimensional
-MTSC problems.
-Detecting the best subset of dimensions is not a straightforward problem,
-since the number of combinations to consider increases exponentially with the
-number of dimensions. Selection is also made more complex by the fact that
-the objective function used to assess a set of features may not generalise well
-to unseen data. Furthermore, since the primary reason for filtering the dimensions
-is improving the efficiency of the classifier, dimension selection strategies
-themselves need to be fast.
-
-Currently we have the channel selection algorithms describe in [1,2] in aeon. It would
-be great to include those in [3] and further work. This project will involve
-experimental evaluation in addition to implementing
-algorithms. We can co-ordinate the experiments with the candidate through our HPC
-facilities.
-
-1. Implement a channel selection wrapper for the aeon toolkit (see [#1270](https://github.com/aeon-toolkit/aeon/issues/1270))
-2. Explore alternative ways of selecting channels after scoring (e.g. forward selection)
-3. Use a fast classifier that can find train estimates through e.g. bagging and avoid the cross validation
-4. Research, implement and evaluate alternative channel selection algorithms
-
-##### References
-[1] Dhariyal, B. et al. Fast Channel Selection for Scalable Multivariate Time
-Series Classification. AALTD, ECML-PKDD, Springer, 2021
-[2] Dhariyal, B. et al. Scalable Classifier-Agnostic Channel Selection
-    for Multivariate Time Series Classification", DAMI, 2023
-[3] Ruiz, A.P., Bagnall, A. Dimension Selection Strategies for Multivariate
-   Time Series Classification with HIVE-COTEv2.0. AALTD,ECML-PKDD 2022.
-   (https://doi.org/10.1007/978-3-031-24378-3_9)
-
-### Visualisation
-
-#### 1. Explainable AI with the shapelet transform (Southampton intern project).
-
-Mentors: TonyBagnall ({user}`TonyBagnall`) and David Guijo-Rubio
-({user}`dguijo`)
-
-This project will focus on explainable AI for time series classification (TSC) [1],
-specifically the family of algorithms based on shapelets [2]. Shapelets are small sub
- certain shape of heartbeat, perhaps a short irregularity, might be useful in
-predicting the medical condition. We will look at the shapelet transform classifier
-[3]. This finds a large set of shapelets from the training data and uses them to
-build a classifier. We want to develop tools to help us visualise the output of the
-search for good shapelets to help explain why predictions are made. This project is
-not tied to a specific data set. It is to develop tools to help any user of the
-toolkit.  It will involve learning about aeon and making contributions to open
-source toolkits, familiarisation with the shapelet code and the development of a
-visualisation tool to help relate shapelets back to the training data. An outline
-for the project is
-
-Weeks 1-2: Familiarisation with open source, aeon and the visualisation module. Make
-contribution for a good first issue.
-Weeks 3-4: Understand the shapelet transfer algorithm, engage in ongoing discussions
-for possible improvements, run experiments to create predictive models for a test data set
-Weeks 5-6: Design and prototype visualisation tools for shapelets, involving a range
-of summary measures and visualisation techniques, including plotting shapelets on training data, calculating frequency, measuring similarity between
-Weeks 7-8: Debug, document and make PRs to merge contributions into the aeon toolkit.
-
-[1] Bagnall, A., Lines, J., Bostrom, A., Large, J. and Keogh, E. The great time series classification bake off: a review and experimental evaluation of recent algorithmic advances. Data Mining and Knowledge Discovery, Volume 31, pages 606–660, (2017)
-[2] Ye, L., Keogh, E. Time series shapelets: a novel technique that allows accurate, interpretable and fast classification. Data Min Knowl Disc 22, 149–182 (2011). https://doi.org/10.1007/s10618-010-0179-5
-[3] Lines, L., Davis, L., Hills, J. and Bagnall, A. A shapelet transform for time series classification, KDD '12: Proceedings of the 18th ACM SIGKDD international conference on Knowledge discovery and data mining (2012) https://doi.org/10.1145/2339530.2339579
-
-### Regression
-
-#### 1. Adapt forecasting regressors to time series extrinsic regression.
-
-Mentors: TonyBagnall ({user}`TonyBagnall`) and David Guijo-Rubio
-({user}`dguijo`)
-
-Forecasting is often reduced to regression through the application of a sliding
-window. This is a large research field that is distinct to time series extrinsic
-regression, where each series is assumed to be independent. This is more of a
-research project to investigate what techniques are used in forecasting for
-regression based forecasting and to compare them to the time series specific
-algorithms in aeon. This project would require further working up with the mentors.
-
-
-#### 2. Adapt HIVE-COTE for regression
-
-Mentors: TonyBagnall ({user}`TonyBagnall`) and David Guijo-Rubio
-({user}`dguijo`)
-
-HIVE-COTE [1] is a state of the art classifier. Adapting it for regression is an
-ongoing research project for which we would welcome collaborators. Ongoing, this
-needs working up.
-
-
-### Documentation
-
-#### 1. Improve automated API documentation
-
-Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`)
-
-##### Description
-
-`aeon` uses `sphinx` and `numpydoc` to generate API documentation from docstrings.
-Many of the docstrings are incomplete or missing sections, and could be improved to
-make the API documentation more useful. The goal of this project is to generally
-improve the API documentation. A specific goal is to automatically generate links to
-examples which use the function/class, similar to the `scikit-learn` documentation.
-The way this is achieved is up to the mentee, but should include a new section in the
-relevant API page. I.e., the API page for
-`aeon.transformers.collection.convolution_based.Rocket` should have a section called
-"Examples" which links to the examples which use the class (such as the Rocket
-notebook).
-
-##### Project Stages
-
-1. Learn about `aeon` best practices and project documentation.
-2. Familiarise with `sphinx` documentation generation and `numpydoc` docstring
-standards.
-3. Improve the API documentation for a few classes/functions and go through the Pull
-Request and review process.
-4. Implement a function or improve the API template to automatically generate links
-to examples which use the function/class.
-5. The main bulk of work is done, but the API documentation is vast and can always be
-improved! If time allows, continue to enhance the API documentation through individual
-docstrings, API landing page and template improvements at the mentees discretion.
-
-##### Project Outcomes
-
-Success of the project will be assessed by the quality of the documentation produced
-and engagement with the project and the `aeon` community. Automatically generating
-links to examples is the primary goal.
diff --git a/docs/papers_using_aeon.md b/docs/papers_using_aeon.md
index 30dba0fd..ee2c142f 100644
--- a/docs/papers_using_aeon.md
+++ b/docs/papers_using_aeon.md
@@ -1,87 +1,127 @@
-# Papers using Aeon
+
+[//]: # (Try to put references in harvard style for consistency.)
+
+# Papers using aeon
 
 This is a list of papers that use `aeon`. If you have a paper that uses `aeon`,
 please add it to this list by making a pull request. Please include a hyperlink to
 the paper and a link to the code in your personal GitHub or other repository.
 
-## Challenge
+If you want to reference `aeon` please reference this paper.
 
-- Ermshaus, A., Schäfer, P., Bagnall, A., Guyet, T., Ifrim, G., Lemaire, V., ... &
-  Malinowski, S. (2023, September). Human Activity Segmentation Challenge@ ECML/PKDD’23.
-  In International Workshop on Advanced Analytics and Learning on Temporal Data
-  (pp. 3-13). Cham: Springer Nature Switzerland.
-  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_1) [Webpage/Code](https://github.com/patrickzib/human_activity_segmentation_challenge)
+Middlehurst, M., Ismail-Fawaz, A., Guillaume, A., Holder, C., Guijo-Rubio, D.,
+Bulatova, G., Tsaprounis, L., Mentel, L., Walter, M., Schäfer, P. and Bagnall, A., 2024.
+aeon: a Python toolkit for learning from time series. Journal of Machine Learning 2025.
+Research, 25(289), pp.1-10.
+[Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_1)
 
-## Classification
+## 2025
 
-- Middlehurst, M. and Schäfer, P. and  Bagnall, A. (2024). Bake off redux: a review
-  and experimental evaluation of recent time series classification algorithms.
-  Data Mining and Knowledge Discovery, online first, open access.
+- Ismail-Fawaz, A., Devanne, M., Berretti, S., Weber, J. and Forestier, G., 2025.
+  Establishing a unified evaluation framework for human motion generation: A
+  comparative analysis of metrics. Computer Vision and Image Understanding, 254,
+  p.104337.
+  [Paper](https://www.sciencedirect.com/science/article/pii/S1077314225000608) [Code](https://github.com/MSD-IRIMAS/Evaluating-HMG)
+- Rewicki, F,Denzler J. and Niebling, J., Anomalous Agreement: How to find the
+  Ideal Number of Anomaly Classes in Correlated, Multivariate Time Series Data.
+  arXiv:2501.07172
+  [Paper](https://arxiv.org/pdf/2501.07172) [Code](https://gitlab.com/dlr-dw/saai)
+- Serramazza, D., Nguyen, T. and Ifrim, G. A short tutorial for multivariate time
+  series explanation using tsCaptum. Software Impacts, 22.
+  [Paper](https://doi.org/10.1016/j.simpa.2024.100723)
+-
+## 2024
+
+- Bagnall, A.,Middlehurst, M., Forestier, G., Schäfer, P., Ismail-Fawaz, A.,
+  Guillaume, A., Guijo-Rubio, D., Wei Tan, C., Dempster A. and Webb, G.I. A
+  hands-on introduction to time series classification and regression. Proceedings of
+  the 30th ACM SIGKDD Conference on Knowledge Discovery and Data Mining
+ [Paper](https://dl.acm.org/doi/abs/10.1145/3637528.3671443) [Webpage/Code](https://aeon-tutorials.github.io/KDD-2024/)
+- Dempster, A., Tan, C.W., Miller, L., Foumani, N.M., Schmidt, D.F. and Webb, G.I.
+  Highly Scalable Time Series Classification for Very Large Datasets. In
+  International Workshop on Advanced Analytics and Learning on Temporal Data. pp.
+  80-95. [Paper](https://ecml-aaltd.github.io/aaltd2024/articles/Dempster_AALTD24.pdf)
+- Dempster, A., Schmidt, D.F. and Webb, G.I. Quant: A minimalist interval method
+  for time series classification. Data Mining and Knowledge Discovery, 38(4),
+  pp.2377-2402.
+  [Paper](https://link.springer.com/article/10.1007/s10618-024-01036-9)
+- Serramazza, D.I., Nguyen, T.L. and Ifrim, G. Improving the evaluation
+  and actionability of explanation methods for multivariate time series classification.
+  In Joint European Conference on Machine Learning and Knowledge Discovery in
+  Databases, pp. 177-195.
+  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-70359-1_11)
+- Middlehurst, M., Schäfer, P. and Bagnall, A. Bake off redux: a review and
+  experimental evaluation of recent time series classification algorithms. Data Mining
+  and Knowledge Discovery, 38(4), pp.1958-2031.
   [Paper](https://link.springer.com/article/10.1007/s10618-024-01022-1) [Webpage/Code](https://tsml-eval.readthedocs.io/en/stable/publications/2023/tsc_bakeoff/tsc_bakeoff_2023.html)
-- Spinnato, F. and Guidotti, R. and Monreale, A. and Nanni, M. (2024). Fast,
-  Interpretable, and Deterministic Time Series Classification With a
-  Bag-of-Receptive-Fields. IEEE Access, vol. 12, (pp. 137893-137912).
+- Spinnato, F., Guidotti, R., Monreale, A. and Nanni, M. Fast, Interpretable and
+  Deterministic Time Series Classification with a Bag-Of-Receptive-Fields. IEEE Access.
   [Paper](https://ieeexplore.ieee.org/document/10684604) [Code](https://github.com/fspinna/borf)
-- Schäfer, P, and Leser, U. (2023). WEASEL 2.0: a random dilated dictionary transform
-  for fast, accurate and memory constrained time series classification.
-  Machine Learning, 112(12), pp.4763-4788.
-  [Paper](https://link.springer.com/content/pdf/10.1007/s10994-023-06395-w.pdf) [Webpage/Code](https://github.com/patrickzib/dictionary)
-
-## Clustering
-
-- Holder, C., Middlehurst, M. and Bagnall, A., (2024). A review and evaluation of
-  elastic distance functions for time series clustering. Knowledge and Information
-  Systems, 66(2), pp.765-809.
+- Holder, C., Middlehurst, M. and Bagnall, A. A review and evaluation of elastic
+  distance functions for time series clustering. Knowledge and Information Systems,
+  66(2), pp.765-809.
   [Paper](https://link.springer.com/article/10.1007/s10115-023-01952-0) [Webpage/Code](https://tsml-eval.readthedocs.io/en/stable/publications/2023/distance_based_clustering/distance_based_clustering.html)
-- Holder, C., Guijo-Rubio, D. and Bagnall, A., (2023), September. Clustering time series
-  with k-medoids based algorithms. In International Workshop on Advanced Analytics and
-  Learning on Temporal Data (pp. 39-55).
-  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_4)
-- Holder, Christopher & Bagnall, Anthony. (2024).
-  Rock the KASBA: Blazingly Fast and Accurate Time Series Clustering.
-  10.48550/arXiv.2411.17838.
-[Paper](https://arxiv.org/abs/2411.17838)
+- Ayllón-Gavilán, R., Guijo-Rubio, D., Gutiérrez, P.A., Bagnall, A. and Hervás-Martínez,
+  C. Convolutional-and Deep Learning-Based Techniques for Time Series Ordinal
+  Classification. IEEE Transactions on Cybernetics, 55(2), pp. 537-549.
+  [Paper](https://ieeexplore.ieee.org/abstract/document/10769513).
+- Guijo-Rubio, D., Middlehurst, M., Arcencio, G., Silva, D.F. and Bagnall, A..
+  Unsupervised feature based algorithms for time series extrinsic regression. Data
+  Mining and Knowledge Discovery, 38(4), pp.2141-2185.
+  [Paper](https://link.springer.com/article/10.1007/s10618-024-01027-w)
+  [Webpage/Code](https://tsml-eval.readthedocs.io/en/stable/publications/2023/tser_archive_expansion/tser_archive_expansion.html)
+- Miao, B., Dong, Y, Theissler, A, Lesh, A., Loftus, D.
+  and Lepech, M. BioSys: Efficient Quality Control System for
+  Manufacturing of Sustainable Biopolymer Composites. In Proceedings of the 11th ACM
+  International Conference on Systems for Energy-Efficient Buildings (BuildSys '24)
+  [Paper](https://doi.org/10.1145/3671127.3698165)
+- Moore, J., Stackhouse, H. Fulcher, B. and Mahmoodian, S. Using matrix-product
+  states for time-series machine learning. 	arXiv:2412.15826
+  [Paper](https://arxiv.org/abs/2412.15826)
+- Holder, C. and Bagnall, B. 2024. Rock the KASBA: Blazingly Fast and Accurate Time
+  Series Clustering. arXiv: 2411.17838
+  [Paper](https://arxiv.org/abs/2411.17838)
+- Holder, C., Bagnall, B. and Lines, J. 2024. On time series clustering with k-means.
+  arXiv: 2410.14269
+  [Paper](https://arxiv.org/abs/2410.14269)
+- Ismail-Fawaz, A. 2024. Deep Learning For Time Series Analysis With Application On
+  Human Motion arXiv: 2502.19364
+  [Paper](https://arxiv.org/abs/2502.19364)
 
-## Regression
+## 2023
 
-- Guijo-Rubio, D., Middlehurst, M., Arcencio, G., Silva, D. and Bagnall, A. (2024).
-  Unsupervised Feature Based Algorithms for Time Series Extrinsic Regression. Data
-  Mining and Knowledge Discovery, online first, open access.
-  [Paper](https://arxiv.org/abs/2305.01429) [Webpage/Code](https://tsml-eval.readthedocs.io/en/stable/publications/2023/tser_archive_expansion/tser_archive_expansion.html)
-- Middlehurst, M. and Bagnall, A., (2023), September. Extracting Features from Random
-  Subseries: A Hybrid Pipeline for Time Series Classification and Extrinsic Regression.
+- Ayllón-Gavilán, R., Guijo-Rubio, D., Gutiérrez, P.A. and Hervás-Martínez, C., 2023,
+  June. A dictionary-based approach to time series ordinal classification. In
+  International Work-Conference on Artificial Neural Networks (pp. 541-552). Cham:
+  Springer Nature Switzerland.
+  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-43078-7_44)
+- Ermshaus, A., Schäfer, P., Bagnall, A., Guyet, T., Ifrim, G., Lemaire, V., ... &
+  Malinowski, S. (2023, September). Human Activity Segmentation Challenge@ ECML/PKDD’23.
   In International Workshop on Advanced Analytics and Learning on Temporal Data
-  (pp. 113-126).
+  (pp. 3-13). Cham: Springer Nature Switzerland.
+  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_1) [Webpage/Code](https://github.com/patrickzib/human_activity_segmentation_challenge)
+- Schäfer, P. and Leser, U., 2023. WEASEL 2.0: a random dilated dictionary transform for
+  fast, accurate and memory constrained time series classification. Machine Learning,
+  112(12), pp.4763-4788.
+  [Paper](https://link.springer.com/content/pdf/10.1007/s10994-023-06395-w.pdf) [Webpage/Code](https://github.com/patrickzib/dictionary)
+- Middlehurst, M. and Bagnall, A., (2023), September. Middlehurst, M. and Bagnall, A.,
+  2023, September. Extracting features from random subseries: A hybrid pipeline for
+  time series classification and extrinsic regression. In International Workshop on
+  Advanced Analytics and Learning on Temporal Data (pp. 113-126). Cham: Springer Nature
+  Switzerland.
   [Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_8) [Webpage/Code](https://tsml-eval.readthedocs.io/en/stable/publications/2023/rist_pipeline/rist_pipeline.html)
-
-## Ordinal classification
-
-- Ayllón-Gavilán, R., Guijo-Rubio, D., Gutiérrez, P.A., Bagnall, A., and
-  Hervás-Martínez, C. Convolutional and Deep Learning based techniques for Time Series
-  Ordinal Classification. [ArXiV](https://arxiv.org/abs/2306.10084).
-- Ayllón-Gavilán, R., Guijo-Rubio, D., Gutiérrez, P. A., and Hervás-Martínez, C.
-  (2024). O-Hydra: A Hybrid Convolutional and Dictionary-Based Approach to Time Series
-  Ordinal Classification. In Conference of the Spanish Association for Artificial
-  Intelligence (pp. 50-60). [Paper](https://link.springer.com/chapter/10.1007/978-3-031-62799-6_6).
-- Ayllón-Gavilán, R., Guijo-Rubio, D., Gutiérrez, P.A., and Hervás-Martínez, C. (2023).
-  A Dictionary-Based Approach to Time Series Ordinal Classification. In: Rojas, I.,
-  Joya, G., Catala, A. (eds) Advances in Computational Intelligence. IWANN 2023.
-  Lecture Notes in Computer Science, vol 14135. [Paper](https://link.springer.com/chapter/10.1007/978-3-031-43078-7_44).
-
-## Prototyping
-
-- Ismail-Fawaz, A. and Ismail Fawaz, H. and Petitjean, F. and Devanne, M. and Weber,
-  J. and Berretti, S. and Webb, GI. and Forestier, G. (2023 December "ShapeDBA:
-  Generating Effective Time Series Prototypes Using ShapeDTW Barycenter Averaging."
-  ECML/PKDD Workshop on Advanced Analytics and Learning on Temporal Data. [Paper](https://doi.org/10.1007/978-3-031-49896-1_9)
-  [code](https://github.com/MSD-IRIMAS/ShapeDBA)
+- Ismail-Fawaz, A., Ismail Fawaz, H., Petitjean, F., Devanne, M., Weber, J., Berretti,
+  S., Webb, G.I. and Forestier, G., 2023, September. Shapedba: Generating effective time
+  series prototypes using shapedtw barycenter averaging. In International Workshop on
+  Advanced Analytics and Learning on Temporal Data (pp. 127-142). Cham: Springer Nature
+  Switzerland.
+  [Paper](https://doi.org/10.1007/978-3-031-49896-1_9) [Code](https://github.com/MSD-IRIMAS/ShapeDBA)
 - Holder, C., Guijo-Rubio, D., & Bagnall, A. J. (2023). Barycentre Averaging for the
   Move-Split-Merge Time Series Distance Measure. In Proceedings of the 15th
   International Joint Conference on Knowledge Discovery, Knowledge Engineering and
-  Knowledge Management-Volume 1:, 51-62, pp. 51-62. [Paper](https://www.scitepress.org/Link.aspx?doi=10.5220/0012164900003598)
-
-## Generation Evaluation
-
-- Ismail-Fawaz, A. and Devanne, M. and Berretti, S. and Weber, J. and Forestier, G.
-  (2024) May "Establishing a Unified Evaluation Framework for Human Motion
-  Generation: A Comparative Analysis of Metrics" [Paper](https://arxiv.org/abs/2405.07680) [code](https://github.com/MSD-IRIMAS/Evaluating-HMG)
+  Knowledge Management (pp. 51-62).
+  [Paper](https://www.scitepress.org/Link.aspx?doi=10.5220/0012164900003598)
+- Holder, C., Guijo-Rubio, D. and Bagnall, A., 2023, September. Clustering time series
+  with k-medoids based algorithms. In International Workshop on Advanced Analytics and
+  Learning on Temporal Data (pp. 39-55). Cham: Springer Nature Switzerland.
+  [Paper](https://link.springer.com/chapter/10.1007/978-3-031-49896-1_4)
diff --git a/docs/projects.md b/docs/projects.md
new file mode 100644
index 00000000..1337f015
--- /dev/null
+++ b/docs/projects.md
@@ -0,0 +1,488 @@
+
+[//]: # (Try to put references in harvard style for consistency.)
+
+# aeon Projects
+
+`aeon` runs a range of short to medium duration projects that involve
+developing or using `aeon` and interacting with the community and the code
+base. These projects are designed for internships, usage as part of
+undergraduate/postgraduate projects at academic institutions, options for
+programs such as [Google Summer of Code (GSoC)](https://summerofcode.withgoogle.com/) or just for personal side projects.
+For those interested in undertaking a project outside these scenarios, we recommend
+joining the [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-36dlmbouu-vajTShUYAHopSXUUVtHGzw)and discussing with the community.
+
+Feel free to propose your own project ideas, but please discuss them with us first.
+We have an active community of researchers and students who work on `aeon`.
+Please get in touch via Slack if you are interested in any of these projects or have
+any questions. We will more widely advertise funding opportunities as and when they
+become available.
+
+All the projects listed will require knowledge of Python and Git/GitHub. The
+majority of them will require some knowledge of machine learning and time series.
+
+## Current `aeon` projects
+
+This is a list of some of the projects we are interested in running (last updated
+25/05/2025):
+
+[Classification](#classification)
+1. Optimizing the Shapelet Transform for classification and similarity search
+2. Improved HIVE-COTE implementation
+3. Compare distance-based classification.
+
+[Clustering](#clustering)
+1. Density peaks clustering algorithm
+2. Hierarchical clustering for time series
+
+[Transformation](#transformation)
+1. Improve ROCKET family of transformers
+
+[Visualisation](#visualisation)
+1. Explainable AI with the shapelet transform
+
+[Documentation](#documentation)
+1. Improve automated API documentation
+2. Improve the documentation tag interactivity and testing
+
+[Maintenance](#maintenance)
+1. Modernising the `aeon` linting and type checking workflows
+
+[Multi-module](#multi-module)
+1. Implementing multithreading for `aeon` estimators and tools for evaluating multithreading performance
+
+## Classification
+
+### 1. Optimizing the Shapelet Transform for Classification and Similarity Search
+
+Contact: Antoine Guillaume ({user}`baraline`) and Tony Bagnall ({user}`TonyBagnall`)
+
+#### Related Issues
+
+[#186](https://github.com/aeon-toolkit/aeon/issues/186)
+[#973](https://github.com/aeon-toolkit/aeon/issues/973)
+[#1322](https://github.com/aeon-toolkit/aeon/issues/1322)
+
+#### Description
+
+A shapelet is defined as a time series subsequence representing a pattern of interest
+that we wish to search for in time series data. Shapelet-based algorithms can be used
+for a wide range of time series tasks. In this project, we will focus on its core
+application, which is to create an embedding of the input time series.
+
+Our goal in this project will be to optimize the code related to the shapelet
+transform method, which takes as input a set of shapelets and a time series dataset,
+and give as output a tabular dataset containing the features characterizing the
+presence (or absence) of each shapelet in the input time series (more information
+in [1] and [2]).
+
+Similarity search is another field of time series, which has proposed greatly optimized
+algorithms (see [3] and [4]) for the task of finding the best matches of a subsequence
+inside another time series. As this task is extremely similar to what is done in the
+shapelet transform, we want to adapt these algorithms to the context of shapelets,
+in order to achieve significant speed-ups.
+
+#### Project stages
+
+To achieve this goal, with the assistance of the mentor, we identify the following
+steps for the mentee:
+
+1. Learn about aeon best practices, coding standards and testing policies.
+2. Study the shapelet transform algorithm and how it is related to the task of
+similarity search.
+3. Study the similarity search algorithms for the Euclidean distance and the
+computational optimization they use.
+4. Propose a candidate implementation for to increase the performance of the
+computations made by a single shapelet. This can be made with the help of the existing
+implementation of the similarity search module in `aeon`.
+5. Measure the performance of this first candidate implementation against the current
+approach.
+6. Implement this solution to the shapelet transform algorithm, which uses multiple
+shapelets.
+7. Benchmark the implementation against the original shapelet transform algorithm.
+8. If time, generalize this new algorithm to the case of dilated shapelets (see [5]).
+
+#### References
+
+1. Hills, J., Lines, J., Baranauskas, E., Mapp, J. and Bagnall, A., 2014.
+Classification of time series by shapelet transformation. Data mining and knowledge
+discovery, 28, pp.851-881.
+2. Bostrom, A. and Bagnall, A., 2017. Binary shapelet transform for multiclass time
+series classification. Transactions on Large-Scale Data-and Knowledge-Centered Systems
+XXXII: Special Issue on Big Data Analytics and Knowledge Discovery, pp.24-46.
+3. Yeh, C.C.M., Zhu, Y., Ulanova, L., Begum, N., Ding, Y., Dau, H.A., Silva, D.F.,
+Mueen, A. and Keogh, E., 2016, December. Matrix profile I: all pairs similarity joins
+for time series: a unifying view that includes motifs, discords and shapelets. In 2016
+IEEE 16th international conference on data mining (ICDM) (pp. 1317-1322). Ieee.
+4. Zhu, Y., Zimmerman, Z., Senobari, N.S., Yeh, C.C.M., Funning, G., Mueen, A., Brisk,
+P. and Keogh, E., 2016, December. Matrix profile ii: Exploiting a novel algorithm and
+gpus to break the one hundred million barrier for time series motifs and joins. In 2016
+IEEE 16th international conference on data mining (ICDM) (pp. 739-748). IEEE.
+5. Guillaume, A., Vrain, C. and Elloumi, W., 2022, June. Random dilated shapelet
+transform: A new approach for time series shapelets. In International Conference on
+Pattern Recognition and Artificial Intelligence (pp. 653-664). Cham: Springer
+International Publishing.
+
+### 2. Improved HIVE-COTE implementation
+
+Contact: Matthew Middlehurst ({user}`MatthewMiddlehurst`) and Tony Bagnall
+({user}`TonyBagnall`)
+
+#### Related Issues
+
+[#663](https://github.com/aeon-toolkit/aeon/issues/663)
+[#1646](https://github.com/aeon-toolkit/aeon/issues/1646)
+
+#### Description
+
+The Hierarchical Vote Collective of Transformation-based Ensembles (HIVE-COTE) [1,2,3]
+is a time series classifier that has claims to be state-of-the-art,
+particularly in terms of probabilistic estimates [4]. There have been several iterations
+that use different base classifiers but they all share the same design basic:
+classifiers using different representations are combined in a weighted meta-ensemble
+[4]. There are two HIVE-COTE implementations currently in `aeon`: HIVE-COTEV1 [2]
+and HIVECOTEV2 [3]. This project will involve combining these into a single
+framework, modularising the ensemble stage and possibly experimenting with
+alternative structures.
+
+#### Project stages
+
+1. Learn about `aeon` best practices, coding standards and testing policies.
+2. Study the HIVE-COTE algorithm and previous `aeon` implementation.
+3. Implement a modular HIVE-COTE or composable classification ensemble framework.
+4. Convert HIVE-COTEV1 and HIVE-COTEV2 classifiers to use this framework ensuring
+   results remain the same.
+5. Restructure the ensemble stage to allow easy experimentation with variants.
+
+#### References
+
+1. A Bagnall, J Lines, J Hills, A Bostrom, Time-series classification with COTE: the
+   collective of transformation-based ensembles, IEEE Transactions on Knowledge and
+   Data Engineering 27 (9), 2522-2535
+2. M Middlehurst, J Large, M Flynn, J Lines, A Bostrom, A Bagnall, HIVE-COTE 2.0: a
+   new meta ensemble for time series classification, Machine Learning 110 (11),
+   3211-3243
+3. J Lines, S Taylor, A Bagnall, Time series classification with HIVE-COTE: The
+   hierarchical vote collective of transformation-based ensembles, ACM Transactions
+   on Knowledge Discovery from Data (TKDD) 12 (5), 1-35
+4. Middlehurst, M., Schäfer, P. and Bagnall, A., 2024. Bake off redux: a review and
+   experimental evaluation of recent time series classification algorithms. Data Mining
+   and Knowledge Discovery, 38(4), pp.1958-2031.
+
+### 3. Compare distance-based classification and regression
+
+Contact: Chris Holder ({user}`chrisholder`) and Tony Bagnall
+({user}`TonyBagnall`)
+
+#### Related Issues
+
+[#424](https://github.com/aeon-toolkit/aeon/issues/424)
+[#425](https://github.com/aeon-toolkit/aeon/issues/425)
+[#426](https://github.com/aeon-toolkit/aeon/issues/426)
+[#427](https://github.com/aeon-toolkit/aeon/issues/427)
+[#488](https://github.com/aeon-toolkit/aeon/issues/488)
+
+#### Description
+
+Distance-based algorithms are popular for time series classification and regression.
+However, the evaluation of distance functions for classification have not
+comprehensively covered all possible uses. For example, there has not been a proper
+bake off for using elastic distance with support vector machines or with tuning
+distance functions and classifiers in combination. This project will combine
+implementing alternative distance functions and comparing performance on the UCR
+datasets.
+
+#### Project stages
+
+1. Learn about `aeon` best practices, coding standards and testing policies.
+2. Study the distance model and the distance-based classifiers in `aeon`.
+3. Read and implement alternative distance functions in the format of the
+   `aeon.distances` module.
+4. Test the distances against previous results if available.
+5. Run a mini-bakeoff of distance-based classifiers on the UCR datasets, comparing
+   distances using existing distance-based classifiers in `aeon` and `scikit-learn`.
+
+## Clustering
+
+### 1. Density peaks clustering algorithm
+
+Contact: Tony Bagnall ({user}`TonyBagnall`) and Chris Holder (`@chrisholder`).
+
+#### Description
+
+The clustering module in `aeon`, up until now, clusters using time series
+specific distance functions with partitional clustering algorithms such as k-means and
+k-medoids. An alternative clustering algorithm is density peaks [1]. This clustering
+algorithm has the benefit of not having to label all cases as cluster members, which
+means it can easily be adapted to anomaly detection [2]. It is
+a general purpose clustering algorithm that is not available in scikit learn. This
+project will implement the algorithm based on Java and matlab implementations then
+compare performance against partitional clustering for time series clustering.
+
+#### Project Stages
+
+1. Research and understand how density peaks works.
+2. Implement density peaks as an `aeon` estimator using the `aeon` distances module.
+3. Test the implementation against other implementations for correctness and on
+   synthetic data used in the publication.
+4. Compare against alternative TSCL algorithms.
+5. Possible extensions to reflect recent research [2] with specific time series
+   components [3].
+
+#### References
+
+1. Rodriguez, A., & Laio, A. Clustering by Fast Search and Find of
+   Density Peaks. [Science](https://www.science.org/doi/10.1126/science.1242072), 344 (6191), 1492-1496, 2014.
+2. Chen, L., Gao, S. & Liu, B. An improved density peaks clustering algorithm
+   based on grid screening and mutual neighborhood degree for network anomaly detection.
+   Sci Rep 12, 1409 (2022) [DOI](https://doi.org/10.1038/s41598-021-02038-z)
+3. Begum et al. A General Framework for Density Based  Time Series Clustering
+   Exploiting a Novel Admissible Pruning Strategy, [arXiv](https://arxiv.org/ftp/arxiv/papers/1612/1612.00637.pdf)
+
+### 2. Hierarchical clustering for time series
+
+Contact: Tony Bagnall ({user}`TonyBagnall`) and Chris Holder (`@chrisholder`).
+
+#### Description
+
+While the aeon distances module is already extensive, there are still clusterers that
+could be implemented. aeon currently has common algorithms such as KMeans and KMedoids,
+but is missing Hierarchical clustering approaches. The project will involve implementing
+and evaluating some of these, and ensuring they are properly integrated to use the
+wide variety of functions in the distances module.
+
+#### Project Stages
+
+1. Research and understand hierarchical clustering algorithms work and different
+   methods.
+2. Implement hierarchical clustering algorithms as an `aeon` estimator using the
+   `aeon` distances module.
+3. Implement a dendrogram visualisation for the clustering.
+4. Test the implementation visualisations and results against other implementations
+   for correctness
+
+## Transformation
+
+### 1. Improve ROCKET family of transformers
+
+Contact: Ali Ismail-Fawaz ({user}`hadifawaz1999`) and Matthew Middlehurst
+({user}`MatthewMiddlehurst`)
+
+#### Related Issues
+
+[#313](https://github.com/aeon-toolkit/aeon/issues/313)
+[#1126](https://github.com/aeon-toolkit/aeon/issues/1126)
+[#1248](https://github.com/aeon-toolkit/aeon/issues/1248)
+[#2179](https://github.com/aeon-toolkit/aeon/issues/2179)
+
+#### Description
+
+The ROCKET algorithm [1] is a very fast and accurate transformation designed for time
+series classification. It is based on a randomly initialised convolutional kernels that
+are applied to the time series and used to extract summary statistics. ROCKET has
+applications to time series classification, extrinsic regression and anomaly detection,
+but as a fast and unsupervised transformation, it has potential to a wide range of
+other time series tasks.
+
+`aeon` has implementations of the ROCKET transformation and its variants, including
+MiniROCKET [2] and MultiROCKET [3]. However, these implementations have room for
+improvement. There is scope to speed up the implementations, and the amount of variants
+is likely unnecessary and could be condensed into higher quality estimators.
+
+This projects involves improving the existing ROCKET implementations in `aeon` or
+implementing new ROCKET variants. The project will involve benchmarking to ensure that
+the new implementations are as fast and accurate as the original ROCKET algorithm and
+potentially to compare to other implementations.
+Besides improving the existing implementations, there is scope to implement a
+probabilistic ridge classifier [4] for the algorithms to use or implement GPU compatible
+versions of the algorithms.
+
+#### Project Stages
+
+1. Learn about `aeon` best practices, coding standards and testing policies.
+2. Study the ROCKET, MiniROCKET, MultiROCKET algorithms and existing
+   implementations in `aeon`.
+3. Merge and tidy the ROCKET implementations, with the aim being to familiarise the
+mentee with the `aeon` pull request process.
+4. Implement one (or more) of the proposed ROCKET implementation improvements:
+   * Significantly alter the current ROCKET implementations with the goal of
+   speeding up the implementation on CPU processing.
+   * Implement a GPU version of some of the ROCKET transformers, using either
+   `tensorflow` or `pytorch`.
+   * Implement probabilistic ridge classifier as a `scikit-learn` estimator.
+5. Benchmark the implementation against the original ROCKET implementations, looking at
+booth speed of the transform and accuracy in a classification setting.
+
+#### References
+
+1. Dempster, A., Petitjean, F. and Webb, G.I., 2020. ROCKET: exceptionally fast and
+accurate time series classification using random convolutional kernels.
+Data Mining and Knowledge Discovery, 34(5), pp.1454-1495.
+2. Dempster, A., Schmidt, D.F. and Webb, G.I., 2021, August. Minirocket: A very fast
+(almost) deterministic transform for time series classification. In Proceedings of the
+27th ACM SIGKDD conference on knowledge discovery & data mining (pp. 248-257).
+3. Tan, C.W., Dempster, A., Bergmeir, C. and Webb, G.I., 2022. MultiRocket: multiple
+pooling operators and transformations for fast and effective time series classification.
+Data Mining and Knowledge Discovery, 36(5), pp.1623-1646.
+4. Dempster, A., Webb, G.I. and Schmidt, D.F., 2024. Prevalidated ridge regression is a
+highly-efficient drop-in replacement for logistic regression for high-dimensional data.
+arXiv preprint arXiv:2401.15610.
+
+## Visualisation
+
+### 1. Explainable AI with the shapelet transform.
+
+Contact: TonyBagnall ({user}`TonyBagnall`) and David Guijo-Rubio
+({user}`dguijo`)
+
+#### Description
+
+This project will focus on explainable AI for time series classification (TSC) [1],
+specifically the family of algorithms based on shapelets [2]. Shapelets are small sub
+ certain shape of heartbeat, perhaps a short irregularity, might be useful in
+predicting the medical condition. We will look at the shapelet transform classifier
+[3]. This finds a large set of shapelets from the training data and uses them to
+build a classifier. We want to develop tools to help us visualise the output of the
+search for good shapelets to help explain why predictions are made. This project is
+not tied to a specific data set. It is to develop tools to help any user of the
+toolkit.  It will involve learning about aeon and making contributions to open
+source toolkits, familiarisation with the shapelet code and the development of a
+visualisation tool to help relate shapelets back to the training data. An outline
+for the project is
+
+#### Project Stages
+
+1. Familiarisation with open source, aeon and the visualisation module. Make
+contribution for a good first issue.
+2. Understand the shapelet transfer algorithm, engage in ongoing discussions for
+possible improvements, run experiments to create predictive models for a test data set
+3. Design and prototype visualisation tools for shapelets, involving a range
+of summary measures and visualisation techniques, including plotting shapelets on
+training data, calculating frequency, measuring similarity between
+4. Debug, document and make PRs to merge contributions into the aeon toolkit.
+
+#### References
+
+1. Bagnall, A., Lines, J., Bostrom, A., Large, J. and Keogh, E. The great time series
+classification bake off: a review and experimental evaluation of recent algorithmic
+advances. Data Mining and Knowledge Discovery, Volume 31, pages 606–660, (2017)
+2. Ye, L., Keogh, E. Time series shapelets: a novel technique that allows accurate,
+interpretable and fast classification. Data Min Knowl Disc 22, 149–182 (2011).
+https://doi.org/10.1007/s10618-010-0179-5
+3. Lines, L., Davis, L., Hills, J. and Bagnall, A. A shapelet transform for time
+series classification, KDD '12: Proceedings of the 18th ACM SIGKDD international
+conference on Knowledge discovery and data mining (2012)
+https://doi.org/10.1145/2339530.2339579
+
+## Documentation
+
+### 1. Improve the documentation codebase interactions and testing
+
+Contact: Matthew Middlehurst ({user}`MatthewMiddlehurst`)
+
+#### Description
+
+The `aeon` documentation is a key resource for users of the toolkit. It provides
+information on how to install the toolkit, how to use the toolkit, and how to
+contribute to the toolkit. The `aeon` documentation is built using `sphinx` and hosted
+on `readthedocs`.
+
+While there are always improvements that can be made to the general documentation itself
+(e.g., improving the clarity of the text, adding more examples, etc.) for both webpages
+and estimator docstrings, this project focuses on implementing functions to
+automatically link relevant API pages together and ensure new pull requests are
+accompanied by the appropriate documentation. Some examples of improvements that could
+be made include:
+
+- Linking to examples to in API pages where the function/class is used
+- Improving the [estimator overview page](https://www.aeon-toolkit.org/en/stable/estimator_overview.html)
+by further integrating the tags system or adding search and filtering functionality
+- Implementing workflows to ensure that new public functionality includes a valid
+docstring (i.e. has a description, parameters, returns, etc. sections where relevant)
+
+There is a lot of potential for additional functionality, so feel free to suggest
+improvements or new features outside the examples provided.
+
+#### Project Stages
+
+1. Learn about `aeon` best practices and project documentation.
+2. Familiarise with `sphinx` documentation generation and `numpydoc` docstring
+standards.
+3. Improve the API documentation for a few classes/functions and go through the Pull
+Request and review process.
+4. Implement at least one new feature or improvement to the aeon documentation webpage
+(outside of general text improvements) And/Or improve the aeon testing suite to ensure
+that new PRs are accompanied by the appropriate documentation.
+
+## Maintenance
+
+### Modernising the `aeon` linting and type checking workflows
+
+Contact: Matthew Middlehurst ({user}`MatthewMiddlehurst`)
+
+#### Description
+
+This project involves updating the `aeon` linting and type checking workflows to
+use modern tools and ensure that the codebase is up to date with the latest
+Python standards.
+
+The `aeon` toolkit uses `pre-commit` to run code quality checks on all code changes
+and ensure that they meet the project's standards. This includes a number of checks and
+formatting tools, such as `black`, `flake8`, and `isort` (see [here](https://github.com/aeon-toolkit/aeon/blob/main/.pre-commit-config.yaml)).
+Over time new tools have been released such as `ruff` and tools we previously used such
+as `pydocstyle` have been deprecated. The first part of this project will involve
+modernising the `pre-commit` configuration to use the latest tools.
+
+`aeon` contributors have been encouraged to add type hints to the codebase, but this
+is a gradual process and there are still many parts of the codebase that are not fully
+typed. A big issue we face in this is the current lack of automated testing to ensure
+that implemented type hints are accurate. This second part project will involve
+implementing robust testing utilities to help contributors and reviewers ensure that
+new type hints are correct.
+
+Other ideas to improve the code quality testing in `aeon` pull requests or deliver
+feedback from tests to contributors are welcome.
+
+#### Expected Outcome(s)
+
+1. Learn about used for code quality checks and type checking in Python.
+2. Familiarise yourself with the `aeon` CI including pre-commit and GitHub Actions
+workflows.
+3. Update workflows for checking code quality in `aeon` pull requests
+4. Implement automated testing and utilities to help contributors implement accurate
+type hints for `aeon` code.
+
+## Multi-module
+
+### Implementing multithreading for `aeon` estimators and tools for evaluating multithreading performance
+
+Contact: Matthew Middlehurst ({user}`MatthewMiddlehurst`)
+
+#### Description
+
+Multithreading in `aeon` for estimators does not have a set structure or library that must
+be used. Most algorithms which have an `n_jobs` parameter available use a mix of `Joblib` and
+`numba` multithreading. Algorithms which do have the capability for multithreading have not
+been thoroughly tested, and as such the efficiency of these implementations is unknown.
+
+As well as expanding the amount of estimators which can use multiple threads, we would
+like to develop tools to evaluate whether this threading is efficient and develop
+documentation for contributors which want to add multithreading to `aeon` estimators.
+
+#### Project Stages
+
+1. Investigate Python multithreading libraries and `numba` multithreading.
+2. Learn about `aeon` best practices, coding standards and how current estimators use
+multiple threads.
+3. Write tools and testing for evaluating the efficiency of multithreaded code while
+maintaining single-threaded performance.
+4. If any estimators are performing poorly with multithreading, implement
+improvements to the multithreading implementation.
+5. Implement multithreading as a capability for currently lacking `aeon` estimators
+(preferably in the `classification`, `regression` and `clustering` modules to start).
+
+
+```{toctree}
+:hidden:
+
+projects/previous_projects.md
+```
diff --git a/docs/projects/previous_projects.md b/docs/projects/previous_projects.md
new file mode 100644
index 00000000..ae1825e4
--- /dev/null
+++ b/docs/projects/previous_projects.md
@@ -0,0 +1,112 @@
+# Previous aeon Projects
+
+A list of projects that have been completed in the past or are currently ongoing.
+
+## 2025
+
+### Implementing and Evaluating Machine Learning Forecasters
+
+Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`) and Tony Bagnall
+({user}`TonyBagnall`)
+
+Mentee: Tina Jin ({user}`TinaJin0228`)
+
+https://summerofcode.withgoogle.com/organizations/numfocus/projects/details/MPYRSOTi
+
+https://medium.com/@jintina48/list/gsoc25-blog-11a0081fc6e2
+
+GSoC 2025 project
+
+### Deep Learning for Forecasting
+
+Mentors: Tony Bagnall ({user}`TonyBagnall`) and Ali Ismail-Fawaz
+({user}`hadifawaz1999`) and Matthew Middlehurst ({user}`MatthewMiddlehurst`)
+
+Mentee: Balgopal Moharana ({user}`lucifer4073`)
+
+https://summerofcode.withgoogle.com/organizations/numfocus/projects/details/arjEn266
+
+https://medium.com/@lucifer4073/gsoc-25-journey-af8e3e0c2621
+
+GSoC 2025 project
+
+## 2024
+
+### Developing Deep Learning Framework and Implementations for Time Series Clustering
+
+Mentors: Ali Ismail-Fawaz ({user}`hadifawaz1999`) and Tony Bagnall
+({user}`TonyBagnall`) and Matthew Middlehurst ({user}`MatthewMiddlehurst`)
+
+Mentee: Aadya Chinubhai ({user}`aadya940`)
+
+GSoC 2024 project
+
+https://summerofcode.withgoogle.com/programs/2024/projects/Hvd0DfkD
+
+https://medium.com/@aadyachinubhai
+
+#### Project Summary
+
+Time series clustering involves grouping similar time series data together based on
+specific features or patterns. Deep learning algorithms have become increasingly
+popular for clustering. However, the aeon's deep clustering module currently lacks
+several deep learning-based algorithms. In this project the aim is to implement some of
+the top performing and interesting algorithms from a recent comparison of deep learning
+for time series clustering and benchmark them. This project includes further developing
+the aeon deep learning networks module, making the package publicly documented for user
+to explore and well tested to help the maintenance of the deep learning implemented in
+the future.
+
+### Implement the Proximity Forest Algorithm for Time Series Classification
+
+Mentors: Matthew Middlehurst ({user}`MatthewMiddlehurst`) and Tony Bagnall
+({user}`TonyBagnall`) and Antoine Guillaume ({user}`baraline`)
+
+Mentee: Divya Tiwari ({user}`itsdivya1309`)
+
+https://summerofcode.withgoogle.com/programs/2024/projects/8TYGhJjy
+
+https://medium.com/@Divya2003/
+
+#### Project Summary
+
+This project will implement and benchmark the Proximity Forest Algorithm for Time Series
+Classification in aeon. With the ever-increasing data, the applications of time series
+classification are also increasing. Hence, we need classification algorithms that are
+both efficient and scalable. The Proximity Forest Algorithm is the current
+state-of-the-art distance-based classifier that creates an ensemble of decision trees,
+where the splits are based on the similarity between time series measured using various
+parameterised distance measures. Currently, a version of Proximity Forest which can
+match the performance of the original implementation has not been implemented in Python.
+This project aims to implement Proximity Forest in aeon for the classification of
+univariate time series datasets of equal length and make it accessible for a greater
+variety of users. The implementation will be benchmarked on the UCR archive to match
+the results of the original Java implementation in terms of run time and accuracy.
+
+### Machine learning from EEG with aeon-neuro
+
+Mentors: Tony Bagnall ({user}`TonyBagnall`) and Matthew Middlehurst
+({user}`MatthewMiddlehurst`) and Aiden Rushbrooke ({user}`AidenRushbrooke`)
+
+Mentee: Gabriel Riegner ({user}`griegner`)
+
+GSoC 2024 project
+
+https://summerofcode.withgoogle.com/programs/2024/projects/htrPCGOM
+
+https://gist.github.com/griegner/c414f77d957dea73b84dcd80d580b602
+
+#### Project Summary
+
+Develop aeon-neuro to provide structured tools for machine learning from neural data.
+This project will focus on implementing algorithms for EEG classification by building
+on the multivariate classification algorithms outlined in Rushbrooke 2023. This paper
+demonstrates that existing time series models implemented in aeon can successfully
+classify patients from healthy individuals using frequency domain features alone,
+eliminating the need for detailed time domain feature selection. In addition to
+applying existing machine learning models to EEG datasets, we will further develop
+aeon-neuro to be more accessible to the scientific research community by interfacing
+it with existing data formatting standards (BIDs) and EEG analysis libraries (MNE).
+Alongside these primary outcomes, we will adhere to best practices in research software
+development, including writing well-test code, consistent documentation, and user-facing
+examples/notebooks.
diff --git a/pyproject.toml b/pyproject.toml
index 4c4981a9..815aa9a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -83,8 +83,6 @@ unstable_extras = [
     # requires gcc and fftw to be installed for Windows and some other OS (see http://www.fftw.org/index.html)
     "mrsqm>=0.0.7,<0.1.0; platform_system != 'Windows' and python_version < '3.12'",
     "mrseql>=0.0.4,<0.1.0; platform_system != 'Windows' and python_version < '3.12'",
-    # very outdated and used code is deprecated
-    "prts>=1.0.0.0",
     # Upper bound set as <1.0.0 as 1.0 dropped support for python 3.9. We will remove
     # the upper bound once we also drop support for python 3.9 later in 2025.
     "esig>=0.9.7,<1.0.0; platform_system != 'Darwin' and python_version < '3.11'",


Test patch:
diff --git a/aeon/benchmarking/tests/test_results_loaders.py b/aeon/benchmarking/tests/test_results_loaders.py
index b6deaf1d..8c449124 100644
--- a/aeon/benchmarking/tests/test_results_loaders.py
+++ b/aeon/benchmarking/tests/test_results_loaders.py
@@ -85,7 +85,7 @@ data_path = os.path.join(
 )
 @pytest.mark.xfail(raises=CONNECTION_ERRORS)
 @pytest.mark.parametrize(
-    "path", [data_path, "http://timeseriesclassification.com/results/ReferenceResults"]
+    "path", [data_path, "https://timeseriesclassification.com/results/ReferenceResults"]
 )
 def test_get_estimator_results(path):
     """Test loading results returned in a dict."""
@@ -120,7 +120,7 @@ def test_get_estimator_results(path):
 )
 @pytest.mark.xfail(raises=CONNECTION_ERRORS)
 @pytest.mark.parametrize(
-    "path", [data_path, "http://timeseriesclassification.com/results/ReferenceResults"]
+    "path", [data_path, "https://timeseriesclassification.com/results/ReferenceResults"]
 )
 def test_get_estimator_results_as_array(path):
     """Test loading results returned in an array."""
diff --git a/aeon/forecasting/tests/test_ets.py b/aeon/forecasting/tests/test_ets.py
index ce7513a9..f4f5f865 100644
--- a/aeon/forecasting/tests/test_ets.py
+++ b/aeon/forecasting/tests/test_ets.py
@@ -1,27 +1,104 @@
-"""Test ETS forecaster."""
+"""Test ETS."""
 
+__maintainer__ = []
+__all__ = []
+
+import numpy as np
 import pytest
 
 from aeon.forecasting import ETSForecaster
-from aeon.testing.data_generation import make_example_1d_numpy
-
-
-def test_ets_params():
-    """Test ETS forecaster."""
-    y = make_example_1d_numpy(n_timepoints=100)
-    forecaster = ETSForecaster(error_type=3)
-    with pytest.raises(
-        ValueError, match="Error must be either additive or " "multiplicative"
-    ):
-        forecaster.fit(y)
-    forecaster = ETSForecaster(seasonality_type=-3)
-    forecaster.fit(y)
-    assert forecaster._seasonal_period == 1
-    forecaster = ETSForecaster(trend_type=None, seasonality_type=0, beta=1.0, gamma=1.0)
-    forecaster.fit(y)
-    assert forecaster._beta == 0
-    assert forecaster._gamma == 0
-
-    forecaster = ETSForecaster(error_type=2, phi=1.0)
-    pred = forecaster.forecast(y)
-    assert isinstance(pred, float)
+from aeon.forecasting._ets import _validate_parameter
+
+
+def test_ets_forecaster_additive():
+    """TestETSForecaster."""
+    data = np.array(
+        [3, 10, 12, 13, 12, 10, 12, 3, 10, 12, 13, 12, 10, 12]
+    )  # Sample seasonal data
+    forecaster = ETSForecaster(
+        alpha=0.5,
+        beta=0.3,
+        gamma=0.4,
+        phi=1,
+        horizon=1,
+        error_type="additive",
+        trend_type="additive",
+        seasonality_type="additive",
+        seasonal_period=4,
+    )
+    forecaster.fit(data)
+    p = forecaster.predict()
+    assert np.isclose(p, 9.191190608800001)
+
+
+def test_ets_forecaster_mult_error():
+    """TestETSForecaster."""
+    data = np.array(
+        [3, 10, 12, 13, 12, 10, 12, 3, 10, 12, 13, 12, 10, 12]
+    )  # Sample seasonal data
+    forecaster = ETSForecaster(
+        alpha=0.7,
+        beta=0.6,
+        gamma=0.1,
+        phi=0.97,
+        horizon=1,
+        error_type="multiplicative",
+        trend_type="additive",
+        seasonality_type="additive",
+        seasonal_period=4,
+    )
+    forecaster.fit(data)
+    p = forecaster.predict()
+    assert np.isclose(p, 16.20176819429869)
+
+
+def test_ets_forecaster_mult_compnents():
+    """TestETSForecaster."""
+    data = np.array(
+        [3, 10, 12, 13, 12, 10, 12, 3, 10, 12, 13, 12, 10, 12]
+    )  # Sample seasonal data
+    forecaster = ETSForecaster(
+        alpha=0.4,
+        beta=0.2,
+        gamma=0.5,
+        phi=0.8,
+        horizon=1,
+        error_type="additive",
+        trend_type="multiplicative",
+        seasonality_type="multiplicative",
+        seasonal_period=4,
+    )
+    forecaster.fit(data)
+    p = forecaster.predict()
+    assert np.isclose(p, 12.301259229712382)
+
+
+def test_ets_forecaster_multiplicative():
+    """TestETSForecaster."""
+    data = np.array(
+        [3, 10, 12, 13, 12, 10, 12, 3, 10, 12, 13, 12, 10, 12]
+    )  # Sample seasonal data
+    forecaster = ETSForecaster(
+        alpha=0.7,
+        beta=0.5,
+        gamma=0.2,
+        phi=0.85,
+        horizon=1,
+        error_type="multiplicative",
+        trend_type="multiplicative",
+        seasonality_type="multiplicative",
+        seasonal_period=4,
+    )
+    forecaster.fit(data)
+    p = forecaster.predict()
+    assert np.isclose(p, 16.811888294476528)
+
+
+def test_incorrect_parameters():
+    _validate_parameter(0, True)
+    _validate_parameter(None, True)
+    with pytest.raises(ValueError):
+        _validate_parameter(0, False)
+        _validate_parameter(None, True)
+        _validate_parameter(10, False)
+        _validate_parameter("Foo", True)
diff --git a/aeon/networks/tests/test_network_base.py b/aeon/networks/tests/test_network_base.py
index 922d3cd5..542583ef 100644
--- a/aeon/networks/tests/test_network_base.py
+++ b/aeon/networks/tests/test_network_base.py
@@ -20,7 +20,8 @@ class DummyDeepNetwork(BaseDeepLearningNetwork):
         import tensorflow as tf
 
         input_layer = tf.keras.layers.Input(input_shape)
-        output_layer = tf.keras.layers.Dense(units=10)(input_layer)
+        flatten_layer = tf.keras.layers.Flatten()(input_layer)
+        output_layer = tf.keras.layers.Dense(units=10)(flatten_layer)
 
         return input_layer, output_layer
 
diff --git a/aeon/transformations/collection/self_supervised/tests/__init__.py b/aeon/transformations/collection/self_supervised/tests/__init__.py
new file mode 100644
index 00000000..4bd29e9e
--- /dev/null
+++ b/aeon/transformations/collection/self_supervised/tests/__init__.py
@@ -0,0 +1,1 @@
+"""Self-Supervised tests."""
diff --git a/aeon/transformations/collection/self_supervised/tests/test_trilite.py b/aeon/transformations/collection/self_supervised/tests/test_trilite.py
new file mode 100644
index 00000000..03f0e407
--- /dev/null
+++ b/aeon/transformations/collection/self_supervised/tests/test_trilite.py
@@ -0,0 +1,251 @@
+"""Test TRILITE Self-supervised transformer."""
+
+import tempfile
+
+import numpy as np
+import pytest
+
+from aeon.networks import LITENetwork
+from aeon.networks.tests.test_network_base import DummyDeepNetwork
+from aeon.transformations.collection.self_supervised import TRILITE
+from aeon.utils.validation._dependencies import _check_soft_dependencies
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("use_mixing_up", [True, False])
+def test_trilite_use_mixing_up(use_mixing_up):
+    """Test TRILITE with possible mixing up setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            use_mixing_up=use_mixing_up,
+            latent_space_dim=2,
+            backbone_network=DummyDeepNetwork(),
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("use_masking", [True, False])
+def test_trilite_use_masking(use_masking):
+    """Test TRILITE with possible masking setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            use_masking=use_masking,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("z_normalize_pos_neg", [True, False])
+def test_trilite_z_normalize_pos_neg(z_normalize_pos_neg):
+    """Test TRILITE with possible znorm pos and neg setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            z_normalize_pos_neg=z_normalize_pos_neg,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("alpha", [1e-1, 1e-2])
+def test_trilite_alpha(alpha):
+    """Test TRILITE with possible alpha setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            alpha=alpha,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("weight_ref_min", [0.5, 0.6])
+def test_trilite_weight_ref_min(weight_ref_min):
+    """Test TRILITE with possible weight_ref_min setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            weight_ref_min=weight_ref_min,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("percentage_mask_length", [0.2, 0.3])
+def test_trilite_percentage_mask_length(percentage_mask_length):
+    """Test TRILITE with possible percentage_mask_length setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            percentage_mask_length=percentage_mask_length,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("latent_space_dim", [2, 3])
+def test_trilite_latent_space_dim(latent_space_dim):
+    """Test TRILITE with possible latent_space_dim setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            latent_space_dim=latent_space_dim,
+            backbone_network=DummyDeepNetwork(),
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == latent_space_dim
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("latent_space_activation", ["linear", "relu"])
+def test_trilite_latent_space_activation(latent_space_activation):
+    """Test TRILITE with possible latent_space_activation setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        ssl = TRILITE(
+            latent_space_activation=latent_space_activation,
+            backbone_network=DummyDeepNetwork(),
+            latent_space_dim=2,
+            n_epochs=3,
+            file_path=tmp,
+        )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2
+
+
+@pytest.mark.skipif(
+    not _check_soft_dependencies(["tensorflow"], severity="none"),
+    reason="Tensorflow soft dependency unavailable.",
+)
+@pytest.mark.parametrize("backbone_network", [None, DummyDeepNetwork, LITENetwork])
+def test_trilite_backbone_network(backbone_network):
+    """Test TRILITE with possible backbone_network setups."""
+    X = np.random.random((100, 2, 5))
+    with tempfile.TemporaryDirectory() as tmp:
+
+        if backbone_network is not None:
+            ssl = TRILITE(
+                backbone_network=backbone_network(),
+                latent_space_dim=2,
+                n_epochs=3,
+                file_path=tmp,
+            )
+        else:
+            ssl = TRILITE(
+                backbone_network=backbone_network,
+                latent_space_dim=2,
+                n_epochs=3,
+                file_path=tmp,
+            )
+
+        ssl.fit(X=X)
+
+        X_transformed = ssl.transform(X=X)
+
+        assert len(X_transformed.shape) == 2
+        assert int(X_transformed.shape[-1]) == 2