Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion aeon/transformations/collection/_dwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def _transform(self, X, y=None):
Xt : 3D np.ndarray of shape = [n_cases, n_channels, n_timepoints]
collection of transformed time series
"""
_X = np.array(X, dtype=np.float64)
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
_X = np.array(X, dtype=out_dtype)
n_cases, n_channels, n_timepoints = _X.shape
_X = np.swapaxes(_X, 0, 1)
self._check_parameters()
Expand Down
3 changes: 2 additions & 1 deletion aeon/transformations/collection/_slope.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def _transform(self, X, y=None):
# Get information about the dataframe
n_cases, n_channels, n_timepoints = X.shape
self._check_parameters(n_timepoints)
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
full_data = []
for i in range(n_cases):
case_data = []
Expand All @@ -70,7 +71,7 @@ def _transform(self, X, y=None):
# Calculate gradients
res = [self._get_gradient(x) for x in splits]
case_data.append(res)
full_data.append(np.asarray(case_data))
full_data.append(np.asarray(case_data, dtype=out_dtype))

return np.array(full_data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ def _fit_transform(self, X, y=None):
for i in range(1, self.n_intervals):
Xt = np.hstack((Xt, transformed_intervals[i]))

return Xt
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
return Xt.astype(dtype=out_dtype, copy=False)

Comment on lines -211 to 212

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't neet to cast the input here, it must natively used float32 (or 64) in the generate intervals function.

What we want is that if we have a float 32 input, computation are made on float 32 to benefit from the reduced memory footprint (and computation time to some extent)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed final cast. The input is promoted once and interval buffers now use X.dtype, so float32 is preserved throughout the SupervisedIntervals.

def _fit(self, X, y=None):
X, y, rng = self._fit_setup(X, y)
Expand Down Expand Up @@ -251,7 +252,8 @@ def _transform(self, X, y=None):
for i in range(len(self.intervals_))
)

Xt = np.zeros((X.shape[0], len(transform)))
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
Xt = np.zeros((X.shape[0], len(transform)), dtype=out_dtype)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, we want the interval computation to use 32 bit precision, not simply cast the output.

So _transform_intervals itself should return the appropriate dtype already.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_transform_intervals now returns values in the working X.dtype, and _transform allocates its result directly with that dtype instead of correcting the dtype

for i, t in enumerate(transform):
Xt[:, i] = t

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"""Interval extraction test code."""

import numpy as np
import pytest

from aeon.testing.data_generation import make_example_3d_numpy
from aeon.transformations.collection.feature_based import Catch22, SevenNumberSummary
from aeon.transformations.collection.interval_based import (
Expand Down Expand Up @@ -56,3 +59,39 @@ def test_supervised_transformers():
X_t = sit.fit_transform(X, y)

assert X_t.shape == (X.shape[0], 8)


@pytest.mark.parametrize(
"dtype, expected_dtype",
[
(np.float32, np.float32),
(np.float64, np.float64),
(np.int64, np.float64),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to also add int32, float64

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

],
)
def test_supervised_intervals_preserves_float_precision(dtype, expected_dtype):
"""Test SupervisedIntervals preserves float32 and promotes integer input."""
X, y = make_example_3d_numpy(
random_state=0,
n_channels=1,
n_timepoints=20,
)
X = X.astype(dtype)

sit = SupervisedIntervals(
features=[row_mean],
n_intervals=2,
random_state=0,
)
sit.fit(X, y)
Xt = sit.transform(X)

sit = SupervisedIntervals(
features=[row_mean],
n_intervals=2,
random_state=0,
)
Xt_fit_transform = sit.fit_transform(X, y)

assert Xt.dtype == expected_dtype
assert Xt_fit_transform.dtype == expected_dtype
17 changes: 17 additions & 0 deletions aeon/transformations/collection/tests/test_dwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,20 @@ def test_dwt_performs_correcly_along_each_dim():
]
)
np.testing.assert_array_almost_equal(res, orig)


@pytest.mark.parametrize(
"dtype, expected_dtype",
[
(np.float32, np.float32),
(np.float64, np.float64),
(np.int64, np.float64),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to before, add int32, float64

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

],
)
def test_dwt_preserves_float_precision(dtype, expected_dtype):
"""Check dwt preserves float32 and promotes proper dtype output."""
X = np.arange(16, dtype=dtype).reshape(2, 1, 8)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the testing utils functions to generate testing data, as you did in the inerval tests (make_example_3d_numpy)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now uses make_example_3d_numpy


Xt = DWTTransformer(n_levels=2).fit_transform(X)

assert Xt.dtype == expected_dtype
20 changes: 20 additions & 0 deletions aeon/transformations/collection/tests/test_slope_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,23 @@ def test_slope_performs_correcly_along_each_dim():
]
)
np.testing.assert_array_almost_equal(res, orig, decimal=5)


@pytest.mark.parametrize(
"dtype, expected_dtype",
[
(np.float32, np.float32),
(np.float64, np.float64),
(np.int64, np.float64),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also int32, float64

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

],
)
def test_slope_preserves_float_precision(dtype, expected_dtype):
"""Check that Slope preserved float32 and promotes proper dtype output."""
X = np.array(
[[[4, 6, 10, 12, 8, 6, 5, 5]]],
dtype=dtype,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, use the testing utils to generate data

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use make_example_3d_numpy


Xt = SlopeTransformer(n_intervals=2).fit_transform(X)

assert Xt.dtype == expected_dtype
4 changes: 3 additions & 1 deletion aeon/transformations/collection/unequal_length/_resize.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ def _transform(self, X, y=None):

Xt = []
for x in X:
x_new = np.zeros((x.shape[0], length))
out_dtype = np.float32 if x.dtype == np.float32 else np.float64
x_new = np.zeros((x.shape[0], length), dtype=out_dtype)

x2 = np.linspace(0, 1, x.shape[1])
x3 = np.linspace(0, 1, length)
for i, row in enumerate(x):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,23 @@ def test_incorrect_arguments():
resizer = Resizer(resized_length="invalid")
with pytest.raises(ValueError, match="resized_length must be"):
resizer.fit_transform(X)


@pytest.mark.parametrize(
"dtype, expected_dtype",
[
(np.float32, np.float32),
(np.float64, np.float64),
(np.int64, np.float64),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also int32,float64

],
)
def test_resizer_preserves_float_precision(dtype, expected_dtype):
"""Test Resizer preserved float32 and promotes proper dtype output."""
X = [
np.array([[0, 1, 2, 3]], dtype=dtype),
np.array([[0, 2, 4, 6, 8]], dtype=dtype),
Comment on lines +99 to +100

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the current testing util to generate variable length list allows dtype args, so this one is fine for now.

]

Xt = Resizer(resized_length=6).fit_transform(X)

assert Xt.dtype == expected_dtype
3 changes: 2 additions & 1 deletion aeon/transformations/series/_pla.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def _transform(self, X, y=None):
raise ValueError("Invalid max_error: it has to be a number.")
if not (self.buffer_size is None or isinstance(self.buffer_size, (int, float))):
raise ValueError("Invalid buffer_size: use a number only or keep empty.")
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
results = None
X = np.concatenate(X)
if isinstance(self.transformer, (str)):
Expand All @@ -105,7 +106,7 @@ def _transform(self, X, y=None):
else:
raise ValueError("Invalid transformer: it has to be a string.")

return np.concatenate(results)
return np.concatenate(results).astype(out_dtype, copy=False)

def _sliding_window(self, X):
"""Transform a time series using the sliding window algorithm (Online).
Expand Down
21 changes: 21 additions & 0 deletions aeon/transformations/series/tests/test_pla.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,24 @@ def test_piecewise_linear_approximation_one_segment(X):
pla = PLASeriesTransformer(10, "bottom up")
result = pla.fit_transform(X)
np.testing.assert_array_almost_equal(X, result, decimal=1)


@pytest.mark.parametrize(
"transformer", ["sliding window", "top down", "bottom up", "swab"]
)
@pytest.mark.parametrize(
"dtype, expected_dtype",
[
(np.float32, np.float32),
(np.float64, np.float64),
(np.int64, np.float64),
],
)
def test_pla_preserves_float_precision(X, transformer, dtype, expected_dtype):
"""Test PLA float precision transformer."""
Xt = PLASeriesTransformer(
max_error=100_000,
transformer=transformer,
).fit_transform(X.astype(dtype))

assert Xt.dtype == expected_dtype
Loading