Skip to content

[BUG] Fix TimeSeriesKernelKMeans mutating kernel when kernel="kdtw" - #3424

Merged
TonyBagnall merged 2 commits into
aeon-toolkit:mainfrom
maxime2476:fix-3422-kernel-kmeans-mutation
Jun 11, 2026
Merged

[BUG] Fix TimeSeriesKernelKMeans mutating kernel when kernel="kdtw"#3424
TonyBagnall merged 2 commits into
aeon-toolkit:mainfrom
maxime2476:fix-3422-kernel-kmeans-mutation

Conversation

@maxime2476

Copy link
Copy Markdown
Contributor

Reference Issues/PRs

Fixes #3422

What does this implement/fix? Explain your changes.

When kernel="kdtw", TimeSeriesKernelKMeans._fit resolved the "kdtw"
string into a local callable and assigned it back to self.kernel, mutating
the public parameter. This had two consequences:

  • get_params() returned a function object instead of the original "kdtw"
    string, breaking scikit-learn parameter consistency (clone, repr, etc.).
  • A second call to fit no longer entered the KDTW branch, because the
    self.kernel == "kdtw" check no longer held.

This PR stores the resolved callable in a fitted self.kernel_ attribute and
passes that to the underlying tslearn estimator, leaving the kernel
parameter untouched. This follows the scikit-learn convention that __init__
parameters are never modified during fit, and that derived state lives in a
trailing-underscore attribute.

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

No.

Any other comments?

  • kernel_ is documented in the class Attributes section.
  • Added test_kernel_k_means_kdtw_does_not_mutate_kernel, which asserts that
    kernel / get_params()["kernel"] stay "kdtw", that kernel_ is the
    resolved callable, and that a repeated fit produces identical labels.
  • All existing kernel k-means tests and the pre-commit hooks pass.

PR checklist

  • The PR title starts with [BUG].
  • I've added a unit test reproducing the issue and guarding the fix.

`_fit` resolved the "kdtw" string to a local callable and assigned it back
to `self.kernel`, mutating the public parameter. This broke parameter
consistency (`get_params()` returned a function instead of "kdtw") and made
repeated `fit` calls take a different code path, since `self.kernel == "kdtw"`
no longer held.

Store the resolved callable in a fitted `self.kernel_` attribute instead,
leaving the `kernel` parameter untouched. Document `kernel_` in the class
docstring and add a regression test.

Fixes #3422
@aeon-actions-bot aeon-actions-bot Bot added bug Something isn't working clustering Clustering package labels Jun 11, 2026
@aeon-actions-bot

Copy link
Copy Markdown
Contributor

Thank you for contributing to aeon

I have added the following labels to this PR based on the title: [ bug ].
I have added the following labels to this PR based on the changes made: [ clustering ]. Feel free to change these if they do not properly represent the PR.

The 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, please run pre-commit locally and push the fixes to your PR branch.

Don't hesitate to ask questions on the aeon Discord channel if you have any.

PR CI actions

These checkboxes will add labels to enable or 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
  • Disable numba cache loading
  • Regenerate expected results for testing
  • Push an empty commit to re-run CI checks

@TonyBagnall

Copy link
Copy Markdown
Contributor

wow that was quick :) failure not related, thanks for this

@maxime2476

Copy link
Copy Markdown
Contributor Author

The only failing job (test-no-soft-deps) failed on test_sklearn_parameter_tuning[HalvingGridSearchCV], which is unrelated to this PR — it tests CanonicalIntervalForestClassifier parameter tuning, whereas this change only touches aeon/clustering/_kernel_k_means.py.

It is a pre-existing flaky test: test_sklearn_parameter_tuning builds data with make_example_3d_numpy(n_cases=20, ...) without a random_state, and HalvingGridSearchCV then randomly subsamples down to n_resources=8 before applying StratifiedKFold(n_splits=2). On an unlucky draw a fold contains a single class, so check_classification_y raises y must contain at least 2 unique labels, but found 1.. The error wasn't auto-retried because the CI --rerun-except filter matches Error (a ValueError).

Evidence that it is unrelated/flaky: every full pytest matrix job (macOS / Ubuntu / Windows × Python 3.12/3.13/3.14) ran the same test and passed; only this single seeded run hit the bad draw.

I'll push an empty commit to re-trigger CI. Happy to open a separate PR hardening that test (passing a fixed random_state) if maintainers would like.

@TonyBagnall

Copy link
Copy Markdown
Contributor

its fine, I can retrigger the failing test only rather than rerun the whole CI, just let it run through

@maxime2476

Copy link
Copy Markdown
Contributor Author

Thanks, sounds good — I'll leave it with you. Happy to open a separate PR adding a fixed random_state to that test if it would help reduce the flakiness.

@TonyBagnall

Copy link
Copy Markdown
Contributor

flaky test a separate issue, thanks

@TonyBagnall
TonyBagnall merged commit aae7fcc into aeon-toolkit:main Jun 11, 2026
19 checks passed
@TonyBagnall

Copy link
Copy Markdown
Contributor

@all-contributors please add @maxime2476 for bug

@allcontributors

Copy link
Copy Markdown
Contributor

@TonyBagnall

I've put up a pull request to add @maxime2476! 🎉

@TonyBagnall TonyBagnall mentioned this pull request Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working clustering Clustering package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Fix TimeSeriesKernelKMeans mutating kernel when `kernel="kdtw"

2 participants