Skip to content

[BUG] Preserve float32 dtype in affected transformers. - #3752

Open
4nmus wants to merge 6 commits into
aeon-toolkit:mainfrom
4nmus:fix-float32-transformers-3723
Open

[BUG] Preserve float32 dtype in affected transformers.#3752
4nmus wants to merge 6 commits into
aeon-toolkit:mainfrom
4nmus:fix-float32-transformers-3723

Conversation

@4nmus

@4nmus 4nmus commented Aug 17, 2026

Copy link
Copy Markdown

Reference Issues/PRs

Fixes part of #3723.

What does this implement/fix? Explain your changes.

Preserves float32 output precision in the following transformers:
DWTTransformer
Resizer
SlopeTransformer
SupervisedIntervals
PLASeriesTransformer

Previously, these transformers could unnecessarily promote float32 input to float64 through explicit casts or default NumPy allocations.
The changes preserve the following dtype behavior:
-float32 input produces float32 output
-float64 input produces float64 output
-integer input is promoted to float64

Regression tests were added or updated to verify dtype behavior for float32, float64, and integer inputs.

ClaSPTransformer, which is mentioned in #3723, is not included in this PR because its existing float64 conversion is related to a separate Numba typing constraint.

Does your contribution introduce a new dependency? If yes, which one?

No.

Any other comments?

ClaSPTransformer still returns float64 since it forces the input to be float64 due to Numba.

PR checklist

For all contributions
  • I've added myself to the list of contributors. Alternatively, you can use the @all-contributors bot to do this for you after the PR has been merged.
  • 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.
  • (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 to receive notifications about future changes to these files.

@baraline baraline left a comment

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.

Thanks for working on this, the issue is a bit more complex than simply casting the output, we want the internal computation to be performed in the appropriate precision, to benefit from the reduced memory footprint.

I didn't comment on _dwt, _slope and _pla as the issues are the same that those pointed out in intervals.

If you need any pointers don't hesitate to ask questions.

[
(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

Comment on lines -211 to 213
return Xt
out_dtype = np.float32 if X.dtype == np.float32 else np.float64
return Xt.astype(dtype=out_dtype, copy=False)

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.

Comment on lines +255 to +256
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

[
(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

[
(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

[
(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

Comment on lines +90 to +93
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

Comment on lines +103 to +104
np.array([[0, 1, 2, 3]], dtype=dtype),
np.array([[0, 2, 4, 6, 8]], 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.

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

@4nmus

4nmus commented Aug 30, 2026

Copy link
Copy Markdown
Author

Thank you for the review! I updated implementation so that computations itself preserve float32 within in _pla, _supervised_intervals and _slope. However, there is an issue within _supervised_intervals: row_* feature funcitons force float64 regardless of changes, it's already covered in #3724 / #3749 so I kept the SupervisedIntervals working arrays and interval buffers in X.dtype and just normalized feature outputs at that boundary. I also added fixedd and added int32 in tests. I hope that is what was missing.

@4nmus
4nmus requested a review from baraline August 30, 2026 19:59
@baraline

baraline commented Sep 1, 2026

Copy link
Copy Markdown
Member

Great ! Overall looks fine but I'll try to have a closer look at it this week.

Thanks for continuing to work on this.

@baraline

baraline commented Sep 1, 2026

Copy link
Copy Markdown
Member

@MatthewMiddlehurst, do you think it would be worth having a internal estimator attribute (would tag system be appropriate to declare the capability ?) with the input precision (32/64) set during fit and reused across all the estimator declarations ?
It would avoid using the same dtype everytime and would make a clean standard across all estimators to declare and use the precision.

We can set it during input preprocessing similarly to how @4nmus wrote it in the test :

internal_dtype = np.float32 if input_dtype == np.float32 else dtype = np.float64

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants