Repository: aeon-toolkit/aeon
Pull request #1294 (2024-03-10 22:09:19)

[MNT] Distance test refactor
<!--
Thanks for contributing a pull request! Please ensure you have taken a look at our
contribution guide: https://www.aeon-toolkit.org/en/stable/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.
-->

#### Reference Issues/PRs

<!--
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 refactors the distance tests to use the new testing utils for test time series generation. Specifically the '_create_test_distance_numpy' has been removed and where it was called replaced with make_series or make_example_3d_numpy which is consistent with the rest of the package.

<!--
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?
The test results have been updated because new time series were generated compared to the old.

<!--
Please be aware that we are a loose team of volunteers so patience is
necessary; assistance handling other issues is very welcome. We value all
user contributions, no matter how minor they are. If we are slow to
review, either the pull request needs some benchmarking, tinkering,
convincing, etc. or more likely the reviewers are simply busy. In either
case, we ask for your understanding during the review process.
-->

### PR checklist

<!--
Please go through the checklist below. Please feel free to remove points if they are not applicable.
-->

##### For all contributions
- [x] 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.
- [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
- [x] I've added the estimator to the online [API documentation](https://www.aeon-toolkit.org/en/latest/api_reference.html).
- [x] (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.

##### For developers with write access
- [x] (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 have added the following labels to this PR based on the title: **[ $\\color{#EC843A}{\\textsf{maintenance}}$ ]**.
I have added the following labels to this PR based on the changes made: **[ $\\color{#4011F3}{\\textsf{clustering}}$, $\\color{#5209C9}{\\textsf{distances}}$ ]**. Feel free to change these if they do not properly represent the PR.

The [Checks](https://github.com/aeon-toolkit/aeon/pull/1294/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.
    

Patch:
diff --git a/aeon/distances/_distance.py b/aeon/distances/_distance.py
index e2a9c394..07286d95 100644
--- a/aeon/distances/_distance.py
+++ b/aeon/distances/_distance.py
@@ -138,7 +138,7 @@ def distance(
     elif metric == "manhattan":
         return manhattan_distance(x, y)
     elif metric == "minkowski":
-        return minkowski_distance(x, y)
+        return minkowski_distance(x, y, kwargs.get("p", 2.0), kwargs.get("w", None))
     elif metric == "dtw":
         return dtw_distance(x, y, kwargs.get("window"), kwargs.get("itakura_max_slope"))
     elif metric == "ddtw":
@@ -301,7 +301,9 @@ def pairwise_distance(
     elif metric == "manhattan":
         return manhattan_pairwise_distance(x, y)
     elif metric == "minkowski":
-        return minkowski_pairwise_distance(x, y)
+        return minkowski_pairwise_distance(
+            x, y, kwargs.get("p", 2.0), kwargs.get("w", None)
+        )
     elif metric == "dtw":
         return dtw_pairwise_distance(
             x, y, kwargs.get("window"), kwargs.get("itakura_max_slope")
diff --git a/aeon/distances/_utils.py b/aeon/distances/_utils.py
index a5a15329..ad92b81c 100644
--- a/aeon/distances/_utils.py
+++ b/aeon/distances/_utils.py
@@ -2,7 +2,6 @@ from typing import Tuple
 
 import numpy as np
 from numba import njit
-from sklearn.utils import check_random_state
 
 
 @njit(cache=True, fastmath=True)
@@ -52,6 +51,14 @@ def reshape_pairwise_to_multiple(
         if y.ndim == 3 and x.ndim == 2:
             _x = x.reshape((1, x.shape[0], x.shape[1]))
             return _x, y
+        if x.ndim == 3 and y.ndim == 1:
+            _x = x
+            _y = y.reshape((1, 1, y.shape[0]))
+            return _x, _y
+        if x.ndim == 1 and y.ndim == 3:
+            _x = x.reshape((1, 1, x.shape[0]))
+            _y = y
+            return _x, _y
         if x.ndim == 2 and y.ndim == 1:
             _x = x.reshape((x.shape[0], 1, x.shape[1]))
             _y = y.reshape((1, 1, y.shape[0]))
@@ -61,76 +68,3 @@ def reshape_pairwise_to_multiple(
             _y = y.reshape((y.shape[0], 1, y.shape[1]))
             return _x, _y
         raise ValueError("x and y must be 2D or 3D arrays")
-
-
-def _create_test_distance_numpy(
-    n_case: int,
-    n_channels: int = None,
-    n_timepoints: int = None,
-    random_state: int = 1,
-):
-    """Create a test numpy distance.
-
-    Parameters
-    ----------
-    n_case: int
-        Number of instances to create.
-    n_channels: int
-        Number of channels to create.
-    n_timepoints: int, default=None
-        Number of timepoints to create in each channel.
-    random_state: int, default=1
-        Random state to initialise with.
-
-    Returns
-    -------
-    np.ndarray 2D or 3D numpy
-        Numpy array of shape specific. If 1 instance then 2D array returned,
-        if > 1 instance then 3D array returned.
-    """
-    rng = check_random_state(random_state)
-    # Generate data as 3d numpy array
-    if n_timepoints is None and n_channels is None:
-        return rng.normal(scale=0.5, size=(1, n_case))
-    if n_timepoints is None:
-        return rng.normal(scale=0.5, size=(n_case, n_channels))
-    return rng.normal(scale=0.5, size=(n_case, n_channels, n_timepoints))
-
-
-def _make_3d_series(x: np.ndarray) -> np.ndarray:
-    """Check a series being passed into pairwise is 3d.
-
-    Pairwise assumes it has been passed two sets of series, if passed a single
-    series this function reshapes.
-
-    If given a 1d array the time series is reshaped to (m, 1, 1). This is so when
-    looped over x[i] = (1, m).
-
-    If given a 2d array then the time series is reshaped to (d, 1, m). The dimensions
-    are put to the start so the ts can be looped through correctly. When looped over
-    the time series x[i] = (d, m).
-
-    Parameters
-    ----------
-    x: np.ndarray, 2d or 3d
-
-    Returns
-    -------
-    np.ndarray, 3d
-    """
-    n_channels = x.ndim
-    if n_channels == 1:
-        shape = x.shape
-        _x = np.reshape(x, (1, 1, shape[0]))
-    elif n_channels == 2:
-        shape = x.shape
-        _x = np.reshape(x, (shape[0], 1, shape[1]))
-    elif n_channels > 3:
-        raise ValueError(
-            "The matrix provided has more than 3 dimensions. This is not"
-            "supported. Please provide a matrix with less than "
-            "3 dimensions"
-        )
-    else:
-        _x = x
-    return _x
diff --git a/aeon/utils/conversion/__init__.py b/aeon/utils/conversion/__init__.py
index d315f191..49842067 100644
--- a/aeon/utils/conversion/__init__.py
+++ b/aeon/utils/conversion/__init__.py
@@ -1,7 +1,6 @@
 """Conversion utilities."""
 
 __all__ = [
-    "equal_length",
     "resolve_equal_length_inner_type",
     "resolve_unequal_length_inner_type",
     "convert_collection",
diff --git a/aeon/utils/conversion/_convert_collection.py b/aeon/utils/conversion/_convert_collection.py
index 09e6c9bb..c4e776ce 100644
--- a/aeon/utils/conversion/_convert_collection.py
+++ b/aeon/utils/conversion/_convert_collection.py
@@ -3,7 +3,7 @@
 This contains all functions to convert supported collection data types.
 
 String identifier meanings (from aeon.utils.conversion import COLLECTIONS_DATA_TYPES) :
-numpy3D : 2D numpy array of time series shape (n_cases,  n_channels, n_timepoints)
+numpy3D : 3D numpy array of time series shape (n_cases,  n_channels, n_timepoints)
 np-list : list of 2D numpy arrays shape (n_channels, n_timepoints_i)
 df-list : list of 2D pandas dataframes shape (n_channels, n_timepoints_i)
 numpy2D : 2D numpy array of univariate time series shape (n_cases, n_timepoints)
@@ -12,11 +12,13 @@ nested_univ : pd.DataFrame shape (n_cases, n_channels) each cell a pd.Series
 pd-multiindex : pd.DataFrame with multi-index,
 
 For the seven supported, this gives 42 different converters.
-Rather than use them directly, we recommend using the conversion dictionary
-convert_dictionary in the collections file.
-legacy code supported "dask_panel" but it is not actually used anywhere.
+Rather than using them directly, we recommend using the conversion function
+convert_collection.
+Legacy code supported "dask_panel" but it is not actually used anywhere; thus, removed.
 """
 
+from typing import Sequence
+
 import numpy as np
 import pandas as pd
 
@@ -34,11 +36,15 @@ def convert_identity(X):
 
 
 NUMPY3D_ERROR = (
-    "Error: Input should be 3-dimensional NumPy array with shape ("
+    "Input should be 3-dimensional NumPy array with shape ("
     "n_cases, n_channels, n_timepoints)."
 )
-NUMPY2D_OUTPUT_ERROR = "Error: Cannot convert multivariate series to numpy 2D arrays."
-NUMPY2D_INPUT_ERROR = "Error: Input numpy not of type numpy2D."
+NUMPY2D_OUTPUT_ERROR = "Cannot convert multivariate series to numpy 2D arrays."
+NUMPY2D_INPUT_ERROR = "Input numpy not of type numpy2D."
+NP_LIST_ERROR = "Input should be a list of 2D np.ndarray."
+DF_LIST_ERROR = (
+    "Input should be 2-dimensional np.ndarray with shape (n_cases, n_timepoints)."
+)
 
 
 def _from_numpy3d_to_np_list(X):
@@ -59,14 +65,12 @@ def _from_numpy3d_to_np_list(X):
     """
     if X.ndim != 3:
         raise TypeError(NUMPY3D_ERROR)
-    np_list = []
-    for arr in X:
-        np_list.append(arr)
+    np_list = [x for x in X]
     return np_list
 
 
 def _from_numpy3d_to_df_list(X):
-    """Convert 3D np.darray to a list of dataframes in wide format.
+    """Convert 3D np.ndarray to a list of dataframes in wide format.
 
     Converts 3D numpy array (n_cases, n_channels, n_timepoints) to
     a 2D list length [n_cases] of pd.DataFrames shape (n_channels, n_timepoints)
@@ -86,14 +90,12 @@ def _from_numpy3d_to_df_list(X):
     """
     if X.ndim != 3:
         raise TypeError(NUMPY3D_ERROR)
-    df_list = []
-    for x in X:
-        df_list.append(pd.DataFrame(np.transpose(x)))
+    df_list = [pd.DataFrame(np.transpose(x)) for x in X]
     return df_list
 
 
 def _from_numpy3d_to_pd_wide(X):
-    """Convert 3D np.darray to a list of dataframes in wide format.
+    """Convert 3D np.ndarray to a list of dataframes in wide format.
 
     Only valid with univariate time series. Converts 3D numpy array (n_cases, 1,
     n_timepoints) to a dataframe (n_cases, n_timepoints)
@@ -167,13 +169,10 @@ def _from_numpy3d_to_nested_univ(X):
     return df
 
 
-np_list_error = "input should be a list of 2D np.ndarray"
-
-
 def _from_np_list_to_numpy3d(X):
     """Convert from a list of 2D numpy to 3d numpy."""
     if not isinstance(X, list):
-        raise TypeError(np_list_error)
+        raise TypeError(NP_LIST_ERROR)
     for i in range(1, len(X)):
         if len(X[i][0]) != len(X[0][0]):
             raise TypeError("Cannot convert unequal length to numpy3D")
@@ -183,7 +182,7 @@ def _from_np_list_to_numpy3d(X):
 def _from_np_list_to_df_list(X):
     """Convert from a list of 2D numpy to list of dataframes."""
     if not isinstance(X, list) or not isinstance(X[0], np.ndarray) or X[0].ndim != 2:
-        raise TypeError(np_list_error)
+        raise TypeError(NP_LIST_ERROR)
     n_cases = len(X)
     df_list = []
     for i in range(n_cases):
@@ -192,9 +191,9 @@ def _from_np_list_to_df_list(X):
 
 
 def _from_np_list_to_nested_univ(X, store=None):
-    """Convert from a a list of 2D numpy to nested pd.DataFrame."""
+    """Convert from a list of 2D numpy to nested pd.DataFrame."""
     if not isinstance(X, list) or not isinstance(X[0], np.ndarray) or X[0].ndim != 2:
-        raise TypeError(np_list_error)
+        raise TypeError(NP_LIST_ERROR)
     n_cases = len(X)
     n_channels = X[0].shape[0]
     df = pd.DataFrame(index=range(n_cases), columns=range(n_channels))
@@ -207,7 +206,7 @@ def _from_np_list_to_nested_univ(X, store=None):
 
 def _from_np_list_to_numpy2d(X):
     if not isinstance(X, list) or not isinstance(X[0], np.ndarray):
-        raise TypeError(np_list_error)
+        raise TypeError(NP_LIST_ERROR)
     X_3d = _from_np_list_to_numpy3d(X)
     if X_3d.shape[1] > 1:
         raise TypeError(NUMPY2D_OUTPUT_ERROR)
@@ -224,16 +223,9 @@ def _from_np_list_to_pd_multiindex(X):
     return _from_df_list_to_pd_multiindex(X_df)
 
 
-df_list_error = (
-    "Input should be 2-dimensional np.ndarray with shape (n_cases, " "n_timepoints).",
-)
-
-
 def _from_df_list_to_np_list(X):
-    list = []
     n_cases = len(X)
-    for i in range(n_cases):
-        list.append(np.transpose(np.array(X[i])))
+    list = [np.transpose(np.array(X[i])) for i in range(n_cases)]
     return list
 
 
@@ -290,9 +282,7 @@ def _from_numpy2d_to_np_list(X):
     if not isinstance(X, np.ndarray) or X.ndim != 2:
         raise TypeError(NUMPY2D_INPUT_ERROR)
     X_3d = X.reshape(X.shape[0], 1, X.shape[1])
-    X_list = []
-    for x in X_3d:
-        X_list.append(x)
+    X_list = [x for x in X_3d]
     return X_list
 
 
@@ -300,9 +290,7 @@ def _from_numpy2d_to_df_list(X):
     if not isinstance(X, np.ndarray) or X.ndim != 2:
         raise TypeError(NUMPY2D_INPUT_ERROR)
     X_3d = X.reshape(X.shape[0], 1, X.shape[1])
-    X_list = []
-    for x in X_3d:
-        X_list.append(pd.DataFrame(np.transpose(x)))
+    X_list = [pd.DataFrame(np.transpose(x)) for x in X_3d]
     return X_list
 
 
@@ -540,24 +528,26 @@ convert_dictionary[("pd-multiindex", "nested_univ")] = (
 def convert_collection(X, output_type):
     """Convert from one of collections compatible data structure to another.
 
-    See aeon.utils.conversion.COLLECTIONS_DATA_TYPE for the list.
+    See :obj:`aeon.utils.conversion.COLLECTIONS_DATA_TYPE` for the list.
 
     Parameters
     ----------
-    X : collection.
+    X : collection
+        The input collection to be converted.
     output_type : string
-        one of COLLECTIONS_DATA_TYPES
+        Name of the target collection data type, must be one of COLLECTIONS_DATA_TYPES.
 
     Returns
     -------
-    Data structure conforming to "to_type"
+    X : collection
+        Data structure conforming to `output_type`.
 
     Raises
     ------
     TypeError if
-        X pd.ndarray but wrong dimension
-        X is list but not of np.ndarray or p.DataFrame.
-        X is a pd.DataFrame of non float primitives.
+        X np.ndarray but wrong dimension
+        X is list but not of np.ndarray or pd.DataFrame
+        X is a pd.DataFrame of non float primitives
 
     Example
     -------
@@ -580,51 +570,51 @@ def convert_collection(X, output_type):
     return convert_dictionary[(input_type, output_type)](X)
 
 
-def resolve_equal_length_inner_type(inner_type):
+def resolve_equal_length_inner_type(inner_types: Sequence[str]) -> str:
     """Hierarchy of preference for internal supported types for equal length.
 
     Parameter
     ---------
-    inner_type: str
-        The inner type to be resolved.
+    inner_types: Sequence[str]
+        The inner types to be resolved to a single type.
     """
-    if "numpy3D" in inner_type:
+    if "numpy3D" in inner_types:
         return "numpy3D"
-    if "np-list" in inner_type:
+    if "np-list" in inner_types:
         return "np-list"
-    if "numpy2D" in inner_type:
+    if "numpy2D" in inner_types:
         return "numpy2D"
-    if "pd-multiindex" in inner_type:
+    if "pd-multiindex" in inner_types:
         return "pd-multiindex"
-    if "df-list" in inner_type:
+    if "df-list" in inner_types:
         return "df-list"
-    if "pd-wide" in inner_type:
+    if "pd-wide" in inner_types:
         return "pd-wide"
-    if "nested_univ" in inner_type:
+    if "nested_univ" in inner_types:
         return "nested_univ"
     raise ValueError(
-        f"Error, no valid inner types in {inner_type} must be one of "
+        f"Error, no valid inner types in {inner_types} must be one of "
         f"{COLLECTIONS_DATA_TYPES}"
     )
 
 
-def resolve_unequal_length_inner_type(inner_type):
+def resolve_unequal_length_inner_type(inner_types: Sequence[str]) -> str:
     """Hierarchy of preference for internal supported types for unequal length.
 
     Parameter
     ---------
-    inner_type: str
-        The inner type to be resolved.
+    inner_types: Sequence[str]
+        The inner types to be resolved to a single type.
     """
-    if "np-list" in inner_type:
+    if "np-list" in inner_types:
         return "np-list"
-    if "df-list" in inner_type:
+    if "df-list" in inner_types:
         return "df-list"
-    if "pd-multiindex" in inner_type:
+    if "pd-multiindex" in inner_types:
         return "pd-multiindex"
-    if "nested_univ" in inner_type:
+    if "nested_univ" in inner_types:
         return "nested_univ"
     raise ValueError(
-        f"Error, no valid inner types for unequal series in {inner_type} "
+        f"Error, no valid inner types for unequal series in {inner_types} "
         f"must be one of np-list, df-list, pd-multiindex or nested_univ"
     )


Test patch:
diff --git a/aeon/clustering/averaging/tests/test_dba.py b/aeon/clustering/averaging/tests/test_dba.py
index 8888665e..19c82dea 100644
--- a/aeon/clustering/averaging/tests/test_dba.py
+++ b/aeon/clustering/averaging/tests/test_dba.py
@@ -4,8 +4,8 @@ import numpy as np
 import pytest
 
 from aeon.clustering.averaging import elastic_barycenter_average
-from aeon.distances.tests.test_utils import _create_test_distance_numpy
 from aeon.testing.test_config import PR_TESTING
+from aeon.testing.utils.data_gen import make_example_3d_numpy
 
 expected_dba = np.array(
     [
@@ -139,7 +139,7 @@ expected_dba = np.array(
 )
 def test_dba():
     """Test dba functionality."""
-    X_train = _create_test_distance_numpy(10, 10, 10)
+    X_train = make_example_3d_numpy(10, 10, 10, random_state=1)
 
     average_ts = elastic_barycenter_average(X_train)
 
@@ -169,7 +169,7 @@ def test_dba():
 )
 def test_elastic_dba_variations(distance):
     """Test dba functionality with different distance measures."""
-    X_train = _create_test_distance_numpy(4, 2, 10)
+    X_train = make_example_3d_numpy(4, 2, 10, random_state=1)
 
     average_ts = elastic_barycenter_average(
         X_train, distance=distance, window=0.2, independent=False
diff --git a/aeon/distances/tests/test_alignment_path.py b/aeon/distances/tests/test_alignment_path.py
index bf37cb66..5b20358a 100644
--- a/aeon/distances/tests/test_alignment_path.py
+++ b/aeon/distances/tests/test_alignment_path.py
@@ -4,16 +4,20 @@ from numpy.testing import assert_almost_equal
 
 from aeon.distances import alignment_path as compute_alignment_path
 from aeon.distances._distance import DISTANCES
-from aeon.distances.tests.test_utils import _create_test_distance_numpy
+from aeon.distances.tests.test_utils import SINGLE_POINT_NOT_SUPPORTED_DISTANCES
+from aeon.testing.utils.data_gen import make_series
 
 
-def _validate_cost_matrix_result(
+def _validate_alignment_path_result(
     x: np.ndarray,
     y: np.ndarray,
     name,
     distance,
     alignment_path,
+    check_xy_permuted=True,
 ):
+    original_x = x.copy()
+    original_y = y.copy()
     alignment_path_result = alignment_path(x, y)
 
     assert isinstance(alignment_path_result, tuple)
@@ -24,52 +28,98 @@ def _validate_cost_matrix_result(
     distance_result = distance(x, y)
     assert_almost_equal(alignment_path_result[1], distance_result)
 
+    # If unequal length swap where x and y are to ensure it works both ways around
+    if original_x.shape[-1] != original_y.shape[-1] and check_xy_permuted:
+        _validate_alignment_path_result(
+            original_y,
+            original_x,
+            name,
+            distance,
+            alignment_path,
+            check_xy_permuted=False,
+        )
+
 
 @pytest.mark.parametrize("dist", DISTANCES)
-def test_cost_matrix(dist):
+def test_alignment_path(dist):
     if "alignment_path" not in dist:
         return
 
-    # Test univariate
-    if dist["name"] != "ddtw" and dist["name"] != "wddtw" and dist["name"] != "lcss":
-        _validate_cost_matrix_result(
-            np.array([10.0]),
-            np.array([15.0]),
-            dist["name"],
-            dist["distance"],
-            dist["alignment_path"],
-        )
+    # ================== Test equal length ==================
+    # Test univariate of shape (n_timepoints,)
+    _validate_alignment_path_result(
+        make_series(10, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        dist["alignment_path"],
+    )
 
-    _validate_cost_matrix_result(
-        _create_test_distance_numpy(10),
-        _create_test_distance_numpy(10, random_state=2),
+    # Test univariate of shape (1, n_timepoints)
+    _validate_alignment_path_result(
+        make_series(10, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["alignment_path"],
     )
 
-    # Test multivariate
-    _validate_cost_matrix_result(
-        _create_test_distance_numpy(10, 10),
-        _create_test_distance_numpy(10, 10, random_state=2),
+    # Test multivariate of shape (n_channels, n_timepoints)
+    _validate_alignment_path_result(
+        make_series(10, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["alignment_path"],
     )
 
-    # Test unequal length
-    _validate_cost_matrix_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(10, random_state=2),
+    # ================== Test unequal length ==================
+    # Test univariate unequal length of shape (n_timepoints,)
+    _validate_alignment_path_result(
+        make_series(5, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["alignment_path"],
     )
 
-    _validate_cost_matrix_result(
-        _create_test_distance_numpy(10, 5),
-        _create_test_distance_numpy(10, 10, random_state=2),
+    # Test univariate unequal length of shape (1, n_timepoints)
+    _validate_alignment_path_result(
+        make_series(5, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["alignment_path"],
     )
+
+    # Test multivariate unequal length of shape (n_channels, n_timepoints)
+    _validate_alignment_path_result(
+        make_series(5, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        dist["alignment_path"],
+    )
+
+    # ============== Test single point series ==============
+    if (
+        dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES
+        and dist["name"] != "lcss"
+    ):
+        # Test singe point univariate of shape (1,)
+        _validate_alignment_path_result(
+            np.array([10.0]),
+            np.array([15.0]),
+            dist["name"],
+            dist["distance"],
+            dist["alignment_path"],
+        )
+
+        # Test singe point univariate of shape (1, 1)
+        _validate_alignment_path_result(
+            np.array([[10.0]]),
+            np.array([[15.0]]),
+            dist["name"],
+            dist["distance"],
+            dist["alignment_path"],
+        )
diff --git a/aeon/distances/tests/test_bounding.py b/aeon/distances/tests/test_bounding.py
index 7e500bdc..0350c5f2 100644
--- a/aeon/distances/tests/test_bounding.py
+++ b/aeon/distances/tests/test_bounding.py
@@ -1,5 +1,3 @@
-__maintainer__ = []
-
 import numpy as np
 import pytest
 
diff --git a/aeon/distances/tests/test_cost_matrix.py b/aeon/distances/tests/test_cost_matrix.py
index f7e5e7a9..24925eb9 100644
--- a/aeon/distances/tests/test_cost_matrix.py
+++ b/aeon/distances/tests/test_cost_matrix.py
@@ -4,7 +4,8 @@ from numpy.testing import assert_almost_equal
 
 from aeon.distances import cost_matrix as compute_cost_matrix
 from aeon.distances._distance import DISTANCES
-from aeon.distances.tests.test_utils import _create_test_distance_numpy
+from aeon.distances.tests.test_utils import SINGLE_POINT_NOT_SUPPORTED_DISTANCES
+from aeon.testing.utils.data_gen import make_series
 
 
 def _validate_cost_matrix_result(
@@ -13,7 +14,10 @@ def _validate_cost_matrix_result(
     name,
     distance,
     cost_matrix,
+    check_xy_permuted=True,
 ):
+    original_x = x.copy()
+    original_y = y.copy()
     cost_matrix_result = cost_matrix(x, y)
 
     assert isinstance(cost_matrix_result, np.ndarray)
@@ -30,17 +34,30 @@ def _validate_cost_matrix_result(
 
     if name == "lcss":
         if x.ndim != 3:
-            distance = 1 - (
+            curr_distance = 1 - (
                 float(cost_matrix_result[-1, -1] / min(x.shape[-1], y.shape[-1]))
             )
-            assert_almost_equal(distance, distance_result)
+            assert_almost_equal(curr_distance, distance_result)
     elif name == "edr":
         if x.ndim != 3:
-            distance = float(cost_matrix_result[-1, -1] / max(x.shape[-1], y.shape[-1]))
-            assert_almost_equal(distance, distance_result)
+            curr_distance = float(
+                cost_matrix_result[-1, -1] / max(x.shape[-1], y.shape[-1])
+            )
+            assert_almost_equal(curr_distance, distance_result)
     else:
         assert_almost_equal(cost_matrix_result[-1, -1], distance_result)
 
+    # If unequal length swap where x and y are to ensure it works both ways around
+    if original_x.shape[-1] != original_y.shape[-1] and check_xy_permuted:
+        _validate_cost_matrix_result(
+            original_y,
+            original_x,
+            name,
+            distance,
+            cost_matrix,
+            check_xy_permuted=False,
+        )
+
 
 @pytest.mark.parametrize("dist", DISTANCES)
 def test_cost_matrix(dist):
@@ -50,46 +67,78 @@ def test_cost_matrix(dist):
     if "cost_matrix" not in dist:
         return
 
-    # Test univariate
-    if dist["name"] != "ddtw" and dist["name"] != "wddtw":
-        _validate_cost_matrix_result(
-            np.array([10.0]),
-            np.array([15.0]),
-            dist["name"],
-            dist["distance"],
-            dist["cost_matrix"],
-        )
+    # ================== Test equal length ==================
+    # Test univariate of shape (n_timepoints,)
+    _validate_cost_matrix_result(
+        make_series(10, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        dist["cost_matrix"],
+    )
+
+    # Test univariate of shape (1, n_timepoints)
+    _validate_cost_matrix_result(
+        make_series(10, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        dist["cost_matrix"],
+    )
 
+    # Test multivariate of shape (n_channels, n_timepoints)
     _validate_cost_matrix_result(
-        _create_test_distance_numpy(10),
-        _create_test_distance_numpy(10, random_state=2),
+        make_series(10, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["cost_matrix"],
     )
 
-    # Test multivariate
+    # ================== Test unequal length ==================
+    # Test univariate unequal length of shape (n_timepoints,)
     _validate_cost_matrix_result(
-        _create_test_distance_numpy(10, 10),
-        _create_test_distance_numpy(10, 10, random_state=2),
+        make_series(5, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["cost_matrix"],
     )
 
-    # Test unequal length
+    # Test univariate unequal length of shape (1, n_timepoints)
     _validate_cost_matrix_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(10, random_state=2),
+        make_series(5, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["cost_matrix"],
     )
 
+    # Test multivariate unequal length of shape (n_channels, n_timepoints)
     _validate_cost_matrix_result(
-        _create_test_distance_numpy(10, 5),
-        _create_test_distance_numpy(10, 10, random_state=2),
+        make_series(5, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["cost_matrix"],
     )
+
+    # ============== Test single point series ==============
+    if dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES:
+        # Test singe point univariate of shape (1,)
+        _validate_cost_matrix_result(
+            np.array([10.0]),
+            np.array([15.0]),
+            dist["name"],
+            dist["distance"],
+            dist["cost_matrix"],
+        )
+
+        # Test singe point univariate of shape (1, 1)
+        _validate_cost_matrix_result(
+            np.array([[10.0]]),
+            np.array([[15.0]]),
+            dist["name"],
+            dist["distance"],
+            dist["cost_matrix"],
+        )
diff --git a/aeon/distances/tests/test_distance_correctness.py b/aeon/distances/tests/test_distance_correctness.py
index c14c6823..470a3856 100644
--- a/aeon/distances/tests/test_distance_correctness.py
+++ b/aeon/distances/tests/test_distance_correctness.py
@@ -4,8 +4,6 @@ Compare the distance calculations on the 1D and 2D (d,m) format input against th
 results generated with tsml, in distances.tests.TestDistances.
 """
 
-__maintainer__ = []
-
 from numpy.testing import assert_almost_equal
 
 from aeon.datasets import load_basic_motions, load_unit_test
diff --git a/aeon/distances/tests/test_distances.py b/aeon/distances/tests/test_distances.py
index 13a5282b..e7e08b74 100644
--- a/aeon/distances/tests/test_distances.py
+++ b/aeon/distances/tests/test_distances.py
@@ -12,10 +12,15 @@ from aeon.distances._distance import (
     _resolve_key_from_distance,
 )
 from aeon.distances.tests.test_expected_results import _expected_distance_results
-from aeon.distances.tests.test_utils import _create_test_distance_numpy
+from aeon.distances.tests.test_utils import SINGLE_POINT_NOT_SUPPORTED_DISTANCES
+from aeon.testing.utils.data_gen import make_series
 
 
-def _validate_distance_result(x, y, name, distance, expected_result=10):
+def _validate_distance_result(
+    x, y, name, distance, expected_result=10, check_xy_permuted=True
+):
+    original_x = x.copy()
+    original_y = y.copy()
     if expected_result is None:
         return
 
@@ -29,53 +34,96 @@ def _validate_distance_result(x, y, name, distance, expected_result=10):
     dist_result_to_self = distance(x, x)
     assert isinstance(dist_result_to_self, float)
 
+    # If unequal length swap where x and y are to ensure it works both ways around
+    if original_x.shape[-1] != original_y.shape[-1] and check_xy_permuted:
+        _validate_distance_result(
+            original_y,
+            original_x,
+            name,
+            distance,
+            expected_result,
+            check_xy_permuted=False,
+        )
+
 
 @pytest.mark.parametrize("dist", DISTANCES)
 def test_distances(dist):
-    # Test univariate
-    if dist["name"] != "ddtw" and dist["name"] != "wddtw":
-        _validate_distance_result(
-            np.array([10.0]),
-            np.array([15.0]),
-            dist["name"],
-            dist["distance"],
-            _expected_distance_results[dist["name"]][0],
-        )
+    # ================== Test equal length ==================
+    # Test univariate of shape (n_timepoints,)
+    _validate_distance_result(
+        make_series(10, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        _expected_distance_results[dist["name"]][1],
+    )
 
+    # Test univariate of shape (1, n_timepoints)
     _validate_distance_result(
-        _create_test_distance_numpy(10),
-        _create_test_distance_numpy(10, random_state=2),
+        make_series(10, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         _expected_distance_results[dist["name"]][1],
     )
 
-    # Test multivariate
+    # Test multivariate of shape (n_channels, n_timepoints)
     _validate_distance_result(
-        _create_test_distance_numpy(10, 10),
-        _create_test_distance_numpy(10, 10, random_state=2),
+        make_series(10, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         _expected_distance_results[dist["name"]][2],
     )
 
-    # Test unequal length
+    # ================== Test unequal length ==================
+    # Test univariate unequal length of shape (n_timepoints,)
     _validate_distance_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(10, random_state=2),
+        make_series(5, return_numpy=True, random_state=1),
+        make_series(10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         _expected_distance_results[dist["name"]][3],
     )
 
+    # Test univariate unequal length of shape (1, n_timepoints)
     _validate_distance_result(
-        _create_test_distance_numpy(10, 5),
-        _create_test_distance_numpy(10, 10, random_state=2),
+        make_series(5, 1, return_numpy=True, random_state=1),
+        make_series(10, 1, return_numpy=True, random_state=2),
+        dist["name"],
+        dist["distance"],
+        _expected_distance_results[dist["name"]][3],
+    )
+
+    # Test multivariate unequal length of shape (n_channels, n_timepoints)
+    _validate_distance_result(
+        make_series(5, 10, return_numpy=True, random_state=1),
+        make_series(10, 10, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         _expected_distance_results[dist["name"]][4],
     )
 
+    # ============== Test single point series ==============
+    if dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES:
+        # Test singe point univariate of shape (1,)
+        _validate_distance_result(
+            np.array([10.0]),
+            np.array([15.0]),
+            dist["name"],
+            dist["distance"],
+            _expected_distance_results[dist["name"]][0],
+        )
+
+        # Test singe point univariate of shape (1, 1)
+        _validate_distance_result(
+            np.array([[10.0]]),
+            np.array([[15.0]]),
+            dist["name"],
+            dist["distance"],
+            _expected_distance_results[dist["name"]][0],
+        )
+
 
 def test_get_distance_function_names():
     assert get_distance_function_names() == sorted([dist["name"] for dist in DISTANCES])
diff --git a/aeon/distances/tests/test_expected_results.py b/aeon/distances/tests/test_expected_results.py
index 24de5ab2..b9c8675d 100644
--- a/aeon/distances/tests/test_expected_results.py
+++ b/aeon/distances/tests/test_expected_results.py
@@ -6,103 +6,103 @@ _expected_distance_results = {
     #   unequal univariate, multivariate unequal, dataset unequal]
     "euclidean": [
         5.0,
-        2.6329864895136623,
-        7.093596608755006,
-        2.3179478388647876,
-        4.938969178714248,
+        5.45622611835132,
+        14.285548782488558,
+        4.547634701488958,
+        9.938223910720454,
     ],
-    "minkowski": [
-        5.0,
-        2.6329864895136623,
-        7.093596608755006,
-        2.3179478388647876,
-        4.938969178714248,
+    "squared": [
+        25.0,
+        29.770403454579107,
+        204.07690401686034,
+        20.680981378186566,
+        98.76829449961575,
     ],
-    "erp": [
+    "manhattan": [
         5.0,
-        5.037672883414786,
-        20.724482073800456,
-        5.16666987535642,
-        18.353616712062177,
+        15.460501609188993,
+        108.935882528437,
+        8.513472312465874,
+        53.5752848256043,
     ],
-    "edr": [1.0, 0.6, 1.0, 0.4, 0.5],
-    "lcss": [1.0, 0.1, 1.0, 0.0, 1.0],
-    "squared": [
-        25.0,
-        6.932617853961479,
-        50.31911284774053,
-        5.37288218369794,
-        24.39341654828929,
+    "minkowski": [
+        5.0,
+        5.45622611835132,
+        14.285548782488558,
+        4.547634701488958,
+        9.938223910720454,
     ],
     "dtw": [
         25.0,
-        2.180365495972097,
-        47.59969618998147,
-        4.360373075383168,
-        44.86527164702194,
-    ],
-    "shape_dtw": [
-        25.0,
-        6.93261785396148,
-        50.319112847740534,
-        7.221098545890512,
-        47.6105369804373,
+        10.840209465045632,
+        184.1829978640948,
+        11.01992239481733,
+        98.76829449961573,
     ],
     "ddtw": [
         0.0,
-        2.0884818837222006,
-        34.837800040564005,
-        3.6475610211489875,
-        35.916981095128804,
+        8.353927534888799,
+        132.73682936639952,
+        14.590244084595948,
+        68.49854588751192,
     ],
     "wdtw": [
         12.343758137512241,
-        0.985380547171357,
-        21.265839226825413,
-        2.0040890166976926,
-        20.795690703034445,
+        4.901736795260025,
+        82.36559669136193,
+        5.000093573483088,
+        43.243080299363754,
     ],
     "wddtw": [
         0.0,
-        0.9736009365730778,
-        15.926194649221529,
-        1.7031094916423124,
-        16.967390011736825,
+        3.8944037462923102,
+        61.091183129252485,
+        6.812437966569249,
+        30.83571659208905,
     ],
-    "msm": [
+    "lcss": [1.0, 0.30000000000000004, 1.0, 0.0, 0.9],
+    "erp": [
         5.0,
-        6.828557434224288,
-        54.950486942429855,
-        9.155720688607646,
-        83.80645242153975,
+        13.782010409379064,
+        44.3099600330504,
+        16.362513700113617,
+        29.148827543490025,
     ],
+    "edr": [1.0, 0.6, 1.0, 0.4, 1.0],
     "twe": [
         5.0,
-        11.33529624872385,
-        40.346435059599386,
-        12.461233755089522,
-        36.0265974253265,
+        18.24328131880782,
+        78.81976840746147,
+        19.11304326116219,
+        54.77676226769671,
+    ],
+    "msm": [
+        5.0,
+        12.099213975730216,
+        92.75733240032741,
+        12.405615515950716,
+        47.01212744615127,
     ],
     "adtw": [
         25.0,
-        5.163699519307769,
-        49.59969618998147,
-        9.360373075383167,
-        49.86527164702194,
+        16.098799155006947,
+        186.1829978640948,
+        20.01992239481733,
+        98.76829449961573,
     ],
-    "manhattan": [
-        5.0,
-        7.399565602170839,
-        56.41541036195164,
-        4.788349892989078,
-        28.544576071968976,
+    "shape_dtw": [
+        25.0,
+        29.770403454579107,
+        204.0769040168604,
+        15.359727902635804,
+        98.76829449961573,
     ],
     "sbd": [
         0.0,
-        0.33449276,
-        0.53488311,
-        0.59674837,
-        0.51688041,
+        0.33449275676180856,
+        0.5108776877887851,
+        0.5967483689427071,
+        0.6010116433765647,
     ],
 }
 
@@ -110,54 +110,51 @@ _expected_distance_results_params = {
     # Result structure:
     # [univariate series, multivariate series]
     "dtw": [
-        [3.088712375990371, 47.59969618998147],
-        [3.2313355792547123, 48.59583629653524],
-    ],
-    "erp": [
-        [0.6648081862148058, 4.365472428062562],
-        [0.12468518773870504, 1.2934475102361824],
-        [5.279748833082764, 20.71830043391765],
-        [7.399565602170839, 21.90092421200079],
-    ],
-    "edr": [
-        [0.3, 0.3],
-        [0.1, 0.1],
-        [0.3, 1.0],
-    ],
-    "lcss": [
-        [0.09999999999999998, 1.0],
-        [0.19999999999999996, 1.0],
-        [0.30000000000000004, 1.0],
+        [13.875064266550797, 184.18299786409477],
+        [14.250134348814871, 197.8788694266691],
     ],
     "ddtw": [
-        [2.683958998434711, 34.837800040564005],
-        [6.107450358980301, 36.86313484277111],
+        [10.735835993738842, 132.73682936639955],
+        [24.429801435921203, 151.16013640991326],
     ],
     "wdtw": [
-        [1.364177898415523, 21.265839226825413],
-        [1.4215792172731838, 21.526286405547953],
-        [0.02752598656586074, 0.33677832093219406],
+        [6.127263888571422, 82.36559669136193],
+        [6.26486870580251, 87.68847937158377],
+        [0.11800092225918553, 1.3658562956744358],
     ],
     "wddtw": [
-        [1.223806382386346, 15.926194649221529],
-        [2.749366514714634, 16.59453005870139],
-        [0.08524969290653987, 0.663028083142974],
+        [4.895225529545384, 61.09118312925249],
+        [10.997466058858535, 68.04715437335123],
+        [0.3409987716261595, 2.7187979513671015],
     ],
+    "lcss": [[0.30000000000000004, 1.0], [0.4, 1.0], [0.4, 1.0]],
+    "edr": [[0.3, 0.3], [0.1, 0.1], [0.5, 1.0]],
     "twe": [
-        [4.045224987722827, 8.724529215084788],
-        [1.0754723019677446, 3.1555357525791856],
-        [8.892626757586168, 39.346435059599386],
-        [14.469312584616958, 41.0046466591961],
+        [5.087449975445656, 15.161815735222117],
+        [1.1499446039354893, 5.995665808293953],
+        [15.243281318807819, 77.81976840746147],
+        [27.97089924329228, 83.97624505343292],
     ],
     "msm": [
-        [3.4979588555649106, 36.44446666081217],
-        [1.0, 11.330243154019655],
-        [6.5373486105638134, 50.88045781961115],
-        [3.6922226886554435, 38.923480043064245],
+        [4.080245996952201, 43.583053575960584],
+        [1.0, 15.829914369482566],
+        [12.023580258367444, 88.80013932627139],
+        [7.115130579734542, 61.80633627614831],
     ],
     "adtw": [
-        [5.163699519307769, 49.59969618998147],
-        [5.231335579254712, 50.31911284774053],
-        [6.932617853961479, 50.31911284774053],
+        [16.098799155006947, 186.18299786409477],
+        [16.25013434881487, 199.8788694266691],
+        [24.098799155006947, 194.18299786409477],
+    ],
+    "sbd": [[0.13378563362841267, 0.12052110294129567]],
+    "erp": [
+        [6.1963403666089425, 23.958805888780923],
+        [2.2271884807416047, 9.205416143392629],
+        [12.782010409379064, 44.3099600330504],
+        [15.460501609188993, 44.3099600330504],
+    ],
+    "minkowski": [
+        [15.460501609188993, 108.935882528437],
+        [5.45622611835132, 14.285548782488558],
     ],
 }
diff --git a/aeon/distances/tests/test_mpdist.py b/aeon/distances/tests/test_mpdist.py
index 37d27776..ac1b1fd1 100644
--- a/aeon/distances/tests/test_mpdist.py
+++ b/aeon/distances/tests/test_mpdist.py
@@ -1,19 +1,19 @@
 """Test MPDist function."""
 
-import numpy as np
 import pytest
 
 from aeon.distances.mpdist import mpdist
+from aeon.testing.utils.data_gen import make_series
 
 
 def test_mpdist():
     """Minimal test for MPDist prior to redesign."""
-    x = np.random.rand(1, 10)
-    y = np.random.rand(2, 10)
+    x = make_series(10, return_numpy=True, random_state=1)
+    y = make_series(10, 2, return_numpy=True, random_state=2)
     with pytest.raises(ValueError, match="ts1 must be a 1D array"):
         mpdist(y, y)
     with pytest.raises(ValueError, match="ts2 must be a 1D array"):
         mpdist(x, y)
-    y = np.random.rand(1, 10)
+    y = make_series(10, 1, return_numpy=True, random_state=2)
     d = mpdist(x, y)
     assert d >= 0
diff --git a/aeon/distances/tests/test_numba_distance_parameters.py b/aeon/distances/tests/test_numba_distance_parameters.py
index e7cd0238..5d2e2f24 100644
--- a/aeon/distances/tests/test_numba_distance_parameters.py
+++ b/aeon/distances/tests/test_numba_distance_parameters.py
@@ -8,17 +8,18 @@ import pytest
 from aeon.distances import distance
 from aeon.distances._distance import DISTANCES
 from aeon.distances.tests.test_expected_results import _expected_distance_results_params
-from aeon.distances.tests.test_utils import _create_test_distance_numpy
+from aeon.testing.utils.data_gen import make_series
 
 
 def _test_distance_params(
     param_list: List[Dict], distance_func: Callable, distance_str: str
 ):
-    x_univ = _create_test_distance_numpy(10, 1).reshape((1, 10))
-    y_univ = _create_test_distance_numpy(10, 1, random_state=2).reshape((1, 10))
 
-    x_multi = _create_test_distance_numpy(10, 10)
-    y_multi = _create_test_distance_numpy(10, 10, random_state=2)
+    x_univ = make_series(10, return_numpy=True, random_state=1)
+    y_univ = make_series(10, return_numpy=True, random_state=2)
+
+    x_multi = make_series(10, 10, return_numpy=True, random_state=1)
+    y_multi = make_series(10, 10, return_numpy=True, random_state=2)
 
     test_ts = [[x_univ, y_univ], [x_multi, y_multi]]
     results_to_fill = []
@@ -33,7 +34,14 @@ def _test_distance_params(
         curr_results = []
         for x, y in test_ts:
             if g_none:
-                param_dict["g_arr"] = np.std([x, y], axis=0).sum(axis=1)
+                _x = x
+                if x.ndim == 1:
+                    _x = x.reshape(1, -1)
+                _y = y
+                if y.ndim == 1:
+                    _y = y.reshape(1, -1)
+
+                param_dict["g_arr"] = np.std([_x, _y], axis=0).sum(axis=1)
                 if "g" in param_dict:
                     del param_dict["g"]
             results = []
@@ -46,6 +54,7 @@ def _test_distance_params(
                         assert result == pytest.approx(
                             _expected_distance_results_params[distance_str][i][j]
                         )
+
             curr_results.append(results[0])
             j += 1
         i += 1
diff --git a/aeon/distances/tests/test_pairwise.py b/aeon/distances/tests/test_pairwise.py
index 72dfbcb4..a45c8cb9 100644
--- a/aeon/distances/tests/test_pairwise.py
+++ b/aeon/distances/tests/test_pairwise.py
@@ -4,7 +4,15 @@ from numpy.testing import assert_almost_equal
 
 from aeon.distances import pairwise_distance as compute_pairwise_distance
 from aeon.distances._distance import DISTANCES
-from aeon.distances.tests.test_utils import _create_test_distance_numpy, _make_3d_series
+from aeon.distances.tests.test_utils import (
+    SINGLE_POINT_NOT_SUPPORTED_DISTANCES,
+    _make_3d_series,
+)
+from aeon.testing.utils.data_gen import (
+    make_example_2d_numpy,
+    make_example_3d_numpy,
+    make_series,
+)
 
 
 def _validate_pairwise_result(
@@ -41,7 +49,10 @@ def _validate_multiple_to_multiple_result(
     name,
     distance,
     multiple_to_multiple_distance,
+    check_xy_permuted=True,
 ):
+    original_x = x.copy()
+    original_y = y.copy()
     multiple_to_multiple_result = multiple_to_multiple_distance(x, y)
 
     if x.ndim == 1 and y.ndim == 1:
@@ -73,6 +84,17 @@ def _validate_multiple_to_multiple_result(
 
     assert np.allclose(matrix, multiple_to_multiple_result)
 
+    # If unequal length swap where x and y are to ensure it works both ways around
+    if x.shape[-1] != y.shape[-1] and check_xy_permuted:
+        _validate_multiple_to_multiple_result(
+            original_y,
+            original_x,
+            name,
+            distance,
+            multiple_to_multiple_distance,
+            check_xy_permuted=False,
+        )
+
 
 def _validate_single_to_multiple_result(
     x,
@@ -99,178 +121,334 @@ def _validate_single_to_multiple_result(
     for i in range(single_to_multiple_result.shape[-1]):
         curr_y = y[i]
         curr = single_to_multiple_result[0, i]
-        dist = distance(x, curr_y)
+
+        curr_x = x
+        if curr_x.ndim > curr_y.ndim:
+            curr_y = curr_y.reshape((1, curr_y.shape[0]))
+        elif curr_x.ndim < curr_y.ndim:
+            curr_x = curr_x.reshape((1, curr_x.shape[0]))
+
+        dist = distance(curr_x, curr_y)
         assert_almost_equal(dist, curr)
 
 
 @pytest.mark.parametrize("dist", DISTANCES)
 def test_pairwise_distance(dist):
     """Test pairwise distance function."""
+
+    # ================== Test equal length ==================
+    # Test collection of univariate time series in the shape (n_cases, n_timepoints)
     _validate_pairwise_result(
-        _create_test_distance_numpy(5, 5),
+        make_example_2d_numpy(5, 5, random_state=1, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test collection of univariate time series in the shape
+    # (n_cases, n_channels, n_timepoints)
     _validate_pairwise_result(
-        _create_test_distance_numpy(5, 1, 5),
+        make_example_3d_numpy(5, 1, 5, random_state=1, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test collection of multivariate time series in the shape
+    # (n_cases, n_channels, n_timepoints)
     _validate_pairwise_result(
-        _create_test_distance_numpy(5, 5, 5),
+        make_example_3d_numpy(5, 5, 5, random_state=1, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-
-@pytest.mark.parametrize("dist", DISTANCES)
-def test_multiple_to_multiple_distances(dist):
-    """Test multiple to multiple distances."""
-    # Univariate tests
-    if dist["name"] != "ddtw" and dist["name"] != "wddtw":
-        _validate_multiple_to_multiple_result(
-            np.array([10.0]),
-            np.array([15.0]),
+    # TODO: Uncomment in PR #1287
+    # # ================== Test unequal length ==================
+    # # Test collection of unequal length univariate time series in the shape
+    # # (n_cases, n_timepoints)
+    # _validate_pairwise_result(
+    #     make_example_2d_unequal_length(5, random_state=1, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test collection of unequal length univariate time series in the shape
+    # # (n_cases, n_channels, n_timepoints)
+    # _validate_pairwise_result(
+    #     make_example_unequal_length(5, 1, random_state=1, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test collection of unequal length multivariate time series in the shape
+    # # (n_cases, n_channels, n_timepoints)
+    # _validate_pairwise_result(
+    #     make_example_unequal_length(5, 5, random_state=1, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+
+    # ============== Test single point series ==============
+    if dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES:
+        # Test singe point univariate of shape (1, 1)
+        _validate_pairwise_result(
+            np.array([[10.0]]),
             dist["name"],
             dist["distance"],
             dist["pairwise_distance"],
         )
 
-    _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(5, random_state=2),
-        dist["name"],
-        dist["distance"],
-        dist["pairwise_distance"],
-    )
-
-    # Multivariate tests
-    _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5, 5),
-        _create_test_distance_numpy(5, 5, random_state=2),
-        dist["name"],
-        dist["distance"],
-        dist["pairwise_distance"],
-    )
-
-    # Dataset tests
-    _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5, 1, 5),
-        _create_test_distance_numpy(5, 1, 5, random_state=2),
-        dist["name"],
-        dist["distance"],
-        dist["pairwise_distance"],
-    )
 
+@pytest.mark.parametrize("dist", DISTANCES)
+def test_multiple_to_multiple_distances(dist):
+    """Test multiple to multiple distances."""
+    # ================== Test equal length ==================
+    # Test passing two singular univariate time series of shape (n_timepoints,)
     _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5, 5, 5),
-        _create_test_distance_numpy(5, 5, 5, random_state=2),
+        make_series(5, return_numpy=True, random_state=1),
+        make_series(5, return_numpy=True, random_state=2),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-    # Unequal length tests
+    # Test passing two collections of univariate time series of shape
+    # (n_cases, n_timepoints)
     _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(3, random_state=2),
+        make_example_2d_numpy(5, 5, random_state=1, return_y=False),
+        make_example_2d_numpy(10, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test passing two collections of univariate time series of shape
+    # (n_cases, 1, n_timepoints)
     _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5, 5),
-        _create_test_distance_numpy(5, 3, random_state=2),
+        make_example_3d_numpy(5, 1, 5, random_state=1, return_y=False),
+        make_example_3d_numpy(10, 1, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test passing two collections of multivariate time series of shape
+    # (n_cases, n_channels, n_timepoints)
     _validate_multiple_to_multiple_result(
-        _create_test_distance_numpy(5, 5, 5),
-        _create_test_distance_numpy(5, 5, 3, random_state=2),
+        make_example_3d_numpy(5, 5, 5, random_state=1, return_y=False),
+        make_example_3d_numpy(10, 5, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-
-new_distance = ["euclidean", "dtw"]
-
-
-@pytest.mark.parametrize("dist", DISTANCES)
-def test_new_single_to_multiple_distances(dist):
-    # Univariate tests
-
-    if dist["name"] != "ddtw" and dist["name"] != "wddtw":
-        _validate_single_to_multiple_result(
+    # TODO: Uncomment in PR #1287
+    # # ==================== Unequal length tests ====================
+    # # Test passing two singular unequal length univariate time series of shape
+    # # (n_timepoints,) and (m_timepoints,)
+    # _validate_multiple_to_multiple_result(
+    #     make_series(5, return_numpy=True, random_state=1),
+    #     make_series(3, return_numpy=True, random_state=2),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing two collections of unequal length univariate time series of shape
+    # # (n_cases, n_timepoints) and (n_cases, m_timepoints)
+    # _validate_multiple_to_multiple_result(
+    #     make_example_2d_unequal_length(5, random_state=1, return_y=False),
+    #     make_example_2d_unequal_length(10, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing two collections of unequal length univariate time series of shape
+    # # (n_cases, 1, n_timepoints) and (n_cases, 1, m_timepoints)
+    # _validate_multiple_to_multiple_result(
+    #     make_example_unequal_length(5, 1, random_state=1, return_y=False),
+    #     make_example_unequal_length(10, 1, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing two collections of unequal length  multivariate time series of
+    # shape (n_cases, n_channels, m_timepoints) and (n_cases, n_channels, n_timepoints)
+    # _validate_multiple_to_multiple_result(
+    #     make_example_unequal_length(5, 5, random_state=1, return_y=False),
+    #     make_example_unequal_length(10, 5, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+
+    # ============== Test single point series ==============
+    if dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES:
+        # Test singe point univariate of shape (1,)
+        _validate_multiple_to_multiple_result(
             np.array([10.0]),
+            np.array([15.0]),
+            dist["name"],
+            dist["distance"],
+            dist["pairwise_distance"],
+        )
+
+        # Test singe point univariate of shape (1, 1)
+        _validate_multiple_to_multiple_result(
+            np.array([[10.0]]),
             np.array([[15.0]]),
             dist["name"],
             dist["distance"],
             dist["pairwise_distance"],
         )
 
-    _validate_single_to_multiple_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(3, 1, 5, random_state=2),
-        dist["name"],
-        dist["distance"],
-        dist["pairwise_distance"],
-    )
 
+@pytest.mark.parametrize("dist", DISTANCES)
+def test_new_single_to_multiple_distances(dist):
+    # ================== Test equal length ==================
+    # Test passing a singular univariate time series of shape (n_timepoints,) compared
+    # to a collection of univariate time series of shape (n_cases, n_timepoints)
     _validate_single_to_multiple_result(
-        _create_test_distance_numpy(3, 1, 5)[0],
-        _create_test_distance_numpy(5, 1, 5, random_state=2),
+        make_series(5, return_numpy=True, random_state=1),
+        make_example_2d_numpy(5, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-    # Multivariate tests
+    # Test passing a singular univariate time series of shape (1, n_timepoints) compared
+    # to a collection of univariate time series of shape (n_cases, n_timepoints)
     _validate_single_to_multiple_result(
-        _create_test_distance_numpy(5, 5),
-        _create_test_distance_numpy(5, 5, 5, random_state=2),
+        make_series(5, 1, return_numpy=True, random_state=1),
+        make_example_2d_numpy(5, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-    # Unequal length tests
+    # Test passing a singular univariate time series of shape (n_timepoints,) compared
+    # to a collection of univariate time series of shape (n_cases, 1, n_timepoints)
     _validate_single_to_multiple_result(
-        _create_test_distance_numpy(3),
-        _create_test_distance_numpy(3, 1, 5, random_state=2),
+        make_series(5, return_numpy=True, random_state=1),
+        make_example_3d_numpy(5, 1, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test passing a singular univariate time series of shape (1, n_timepoints) compared
+    # to a collection of univariate time series of shape (n_cases, 1, n_timepoints)
     _validate_single_to_multiple_result(
-        _create_test_distance_numpy(5),
-        _create_test_distance_numpy(3, 1, 3, random_state=2),
+        make_series(5, 1, return_numpy=True, random_state=1),
+        make_example_3d_numpy(5, 1, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
+    # Test passing a singular multivariate time series of shape
+    # (n_channels, n_timepoints) compared to a collection of multivariate time series of
+    # shape (n_cases, n_channels, n_timepoints)
     _validate_single_to_multiple_result(
-        _create_test_distance_numpy(5, 3),
-        _create_test_distance_numpy(5, 5, 5, random_state=2),
+        make_series(5, 5, return_numpy=True, random_state=1),
+        make_example_3d_numpy(5, 5, 5, random_state=2, return_y=False),
         dist["name"],
         dist["distance"],
         dist["pairwise_distance"],
     )
 
-    _validate_single_to_multiple_result(
-        _create_test_distance_numpy(5, 5),
-        _create_test_distance_numpy(5, 5, 3, random_state=2),
-        dist["name"],
-        dist["distance"],
-        dist["pairwise_distance"],
-    )
+    # TODO: Uncomment in PR #1287
+    # # ==================== Unequal length tests ====================
+    # # Test passing a singular univariate time series of shape (n_timepoints,) compared
+    # # to a collection of unequal length univariate time series of shape
+    # # (n_cases, m_timepoints)
+    # _validate_single_to_multiple_result(
+    #     make_series(5, return_numpy=True, random_state=1),
+    #     make_example_2d_unequal_length(5, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing a singular univariate time series of shape (1, n_timepoints)
+    # compare to a collection of unequal length univariate time series of shape
+    # # (n_cases, m_timepoints)
+    # _validate_single_to_multiple_result(
+    #     make_series(5, 1, return_numpy=True, random_state=1),
+    #     make_example_2d_unequal_length(5, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"]
+    # )
+    #
+    # # Test passing a singular univariate time series of shape (n_timepoints,) compared
+    # # to a collection of unequal length univariate time series of shape
+    # # (n_cases, 1, m_timepoints)
+    # _validate_single_to_multiple_result(
+    #     make_series(5, return_numpy=True, random_state=1),
+    #     make_example_unequal_length(5, 1, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing a singular univariate time series of shape (1, n_timepoints)
+    # compared to a collection of unequal length univariate time series of shape
+    # # (n_cases, 1, m_timepoints)
+    # _validate_single_to_multiple_result(
+    #     make_series(5, 1, return_numpy=True, random_state=1),
+    #     make_example_unequal_length(5, 1, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+    #
+    # # Test passing a singular multivariate time series of shape
+    # # (n_channels, n_timepoints) compared to a collection of unequal length
+    # # multivariate time series of shape (n_cases, n_channels, m_timepoints)
+    # _validate_single_to_multiple_result(
+    #     make_series(5, 5, return_numpy=True, random_state=1),
+    #     make_example_unequal_length(5, 5, random_state=2, return_y=False),
+    #     dist["name"],
+    #     dist["distance"],
+    #     dist["pairwise_distance"],
+    # )
+
+    # ============== Test single point series ==============
+    if dist["name"] not in SINGLE_POINT_NOT_SUPPORTED_DISTANCES:
+        # Test singe point univariate of shape (1,) compared to a collection of a
+        # single univariate time series in the shape (n_cases, 1)
+        _validate_single_to_multiple_result(
+            np.array([10.0]),
+            make_example_2d_numpy(5, 1, random_state=2, return_y=False),
+            dist["name"],
+            dist["distance"],
+            dist["pairwise_distance"],
+        )
+
+        # Test singe point univariate of shape (1, 1) compared to a collection of a
+        # single univariate time series in the shape (n_cases, 1, 1)
+        _validate_single_to_multiple_result(
+            np.array([[10.0]]),
+            make_example_3d_numpy(5, 1, 1, random_state=2, return_y=False),
+            dist["name"],
+            dist["distance"],
+            dist["pairwise_distance"],
+        )
+
+        # Test a multivariate single point series in the shape (n_channels, 1)
+        # compared to a collection of a single multivariate time series in the shape
+        # (n_cases, n_channels, 1)
+        _validate_single_to_multiple_result(
+            make_series(1, 5, return_numpy=True, random_state=1),
+            make_example_3d_numpy(5, 5, 1, random_state=2, return_y=False),
+            dist["name"],
+            dist["distance"],
+            dist["pairwise_distance"],
+        )
diff --git a/aeon/distances/tests/test_utils.py b/aeon/distances/tests/test_utils.py
index e059dd52..1723f78c 100644
--- a/aeon/distances/tests/test_utils.py
+++ b/aeon/distances/tests/test_utils.py
@@ -3,24 +3,9 @@
 import numpy as np
 import pytest
 
-from aeon.distances._utils import (
-    _create_test_distance_numpy,
-    _make_3d_series,
-    reshape_pairwise_to_multiple,
-)
-
-
-def test_create_test_distance_numpy():
-    """Test function to create distance test instance."""
-    x = _create_test_distance_numpy(10)
-    assert isinstance(x, np.ndarray)
-    assert x.shape == (1, 10)
-    x = _create_test_distance_numpy(10, n_channels=4)
-    assert isinstance(x, np.ndarray)
-    assert x.shape == (10, 4)
-    x = _create_test_distance_numpy(10, n_channels=4, n_timepoints=20)
-    assert isinstance(x, np.ndarray)
-    assert x.shape == (10, 4, 20)
+from aeon.distances._utils import reshape_pairwise_to_multiple
+
+SINGLE_POINT_NOT_SUPPORTED_DISTANCES = ["ddtw", "wddtw", "edr"]
 
 
 def test_incorrect_input():
@@ -48,3 +33,42 @@ def test_reshape_pairwise_to_multiple():
     assert x2.shape == y2.shape == (5, 1, 10)
     y = np.random.rand(5)
     assert x2.shape == y2.shape == (5, 1, 10)
+
+
+def _make_3d_series(x: np.ndarray) -> np.ndarray:
+    """Check a series being passed into pairwise is 3d.
+
+    Pairwise assumes it has been passed two sets of series, if passed a single
+    series this function reshapes.
+
+    If given a 1d array the time series is reshaped to (1, 1, m). This is so when
+    looped over x[i] = (1, m).
+
+    If given a 2d array then the time series is reshaped to (d, 1, m). The dimensions
+    are put to the start so the ts can be looped through correctly. When looped over
+    the time series x[i] = (1, m).
+
+    Parameters
+    ----------
+    x: np.ndarray, 2d or 3d
+
+    Returns
+    -------
+    np.ndarray, 3d
+    """
+    n_channels = x.ndim
+    if n_channels == 1:
+        shape = x.shape
+        _x = np.reshape(x, (1, 1, shape[0]))
+    elif n_channels == 2:
+        shape = x.shape
+        _x = np.reshape(x, (shape[0], 1, shape[1]))
+    elif n_channels > 3:
+        raise ValueError(
+            "The matrix provided has more than 3 dimensions. This is not"
+            "supported. Please provide a matrix with less than "
+            "3 dimensions"
+        )
+    else:
+        _x = x
+    return _x
diff --git a/aeon/utils/conversion/tests/test_convert_collection.py b/aeon/utils/conversion/tests/test_convert_collection.py
index b3d64c2f..a8df7836 100644
--- a/aeon/utils/conversion/tests/test_convert_collection.py
+++ b/aeon/utils/conversion/tests/test_convert_collection.py
@@ -187,7 +187,7 @@ NUMPY2D = [
 def test_numpy2D_error(function):
     """Test numpy flat converters only work with 2D numpy."""
     X = np.random.random(size=(10, 2, 20))
-    with pytest.raises(TypeError, match="Error: Input numpy not of type numpy2D"):
+    with pytest.raises(TypeError, match="Input numpy not of type numpy2D"):
         function(X)
 
 
@@ -225,7 +225,7 @@ def test_from_nested():
     }
     X = pd.DataFrame(data)
     with pytest.raises(
-        TypeError, match="Cannot convert unequal length series to " "numpy2D"
+        TypeError, match="Cannot convert unequal length series to numpy2D"
     ):
         _from_nested_univ_to_numpy2d(X)
     X, _ = make_example_nested_dataframe(n_cases=10, n_channels=1, n_timepoints=20)
@@ -233,6 +233,6 @@ def test_from_nested():
     assert result.shape == (10, 20)
     X, _ = make_example_nested_dataframe(n_cases=10, n_channels=2, n_timepoints=20)
     with pytest.raises(
-        TypeError, match="Cannot convert multivariate nested into " "numpy2D"
+        TypeError, match="Cannot convert multivariate nested into numpy2D"
     ):
         _from_nested_univ_to_numpy2d(X)