Skip to content

[ENH] Skip redundant per-slice input validation in interval feature extraction - #3665

Open
TonyBagnall wants to merge 5 commits into
mainfrom
ajb/check_x
Open

[ENH] Skip redundant per-slice input validation in interval feature extraction#3665
TonyBagnall wants to merge 5 commits into
mainfrom
ajb/check_x

Conversation

@TonyBagnall

@TonyBagnall TonyBagnall commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

closes #3599

This came about because of a test for flat series triggering multiple warnings in DrCIF.

This lead us to look at RandomIntervals and SupervisedIntervals. These extract features on interval slices of data already validated at the top-level fit. They called the features public fit_transform/transform, paying a full _check_X (missing-value and variance scans) on every slice - thousands of times per DrCIF fit/predict.

Had to adjust SupervisedIntervals to stick with Numpy 3D. Then its simply a matter of using the private _fit_transform/_transform functions which assume pre-validated inner-type input, eliminating the per-slice checks. SupervisedIntervals expands its 2D per-channel slices to the numpy3D inner type first. Non-collection transformer features keep the public path. Neither RandomIntervals and SupervisedIntervals can work with unequal length, so will always deal correctly with numpy3D.

Output is bit-identical (DrCIF, RSTSF, SupervisedIntervals) and n_jobs invariant is preserved.

As well as removing annoying warnings, the real pay off is the speed up. DrCIF is 12% faster fit and 18% predict. to 1.2, supervised is about 3x faster when a transformer feature is used. The DrCIF gain scales with the validation share: more channels or longer series means more/larger slices thus larger gain; this univariate case is a conservative point. Multivariate DrCIF would benefit more.

Now off to look to see if this happens elsewhere

@aeon-actions-bot aeon-actions-bot Bot added enhancement New feature, improvement request or other non-bug code enhancement transformations Transformations package labels Jul 22, 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: [ enhancement ].
I have added the following labels to this PR based on the changes made: [ transformations ]. 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
TonyBagnall marked this pull request as ready for review July 22, 2026 17:59
@TonyBagnall TonyBagnall changed the title [ENH] call _fit_transform [ENH] Skip redundant per-slice input validation in interval feature extraction Jul 22, 2026
@TonyBagnall

Copy link
Copy Markdown
Contributor Author

as an extension, I looked if this slow pattern was repeated elswhere. tl;dr: there are small further gains for interval based, possible larger gains for dictionary based.

Might be worth a follow up PR, especially for WEASEL and MUSE, but requires further investigation, I'll make two issues later.

  1. Interval forests — same [ENH] Move collection variance check out of _check_X so it only validates top-level estimator inputs #3599 pattern, outer call site. DrCIF, STSF, CIF, TSF, RISE, RSTSF all re-validate the same stored representation via intervals[r].fit_transform(Xt[r]) once per tree × representation
    (base_interval_forest.py:1012/1086). At default n_estimators (200–500) × ~3 representations that's 600–1500 redundant validations of already-validated derived data. This is the cheapest win: the fast-path
    primitives now exist, and it's one shared base class. (One subtlety: the representations are derived — e.g. first-order-difference — so unlike the original X they're first seen here; the variance/missing scan is
    still redundant against the top-level guarantee, but that's worth stating precisely.)

  2. Dictionary-based — heaviest by call count, different mechanism. MUSE, BOSS/cBOSS, WEASEL, TDE re-validate full (or subsampled) data through an SFA/SFAFast per ensemble member / window / channel. MUSE is the
    standout: 204 SFAFast validations even at a tiny config, because it fits one SFA per channel×window; on real multivariate data this is thousands. WEASEL re-validates on every window at both fit and predict.

@TonyBagnall

Copy link
Copy Markdown
Contributor Author

further exploring, number of calls

  • DrCIF (and interval forests), after fix: ≈ 3·E + 2 — linear in E (n_estimators), independent of both n_cases and n_timepoints.

    • DrCIF, before fix: ≈ (3 + 3·I(M))·E + 2 where I(M) = intervals/representation ≈ O(√M) — the 3·I(M)·E inner term is what was removed.
    • Dictionary (BOSS/WEASEL/MUSE): constant in n_cases, grows with n_timepoints (one SFA per window/channel, n_windows ≈ O(M)); no n_estimators knob (capped by max_ensemble_size/window grid).

    So: constant in n_cases everywhere ,linear in n_estimators for ensembles; growing in n_timepoints for anything windowed or interval-based — and the DrCIF fix on this PR specifically collapsed the n_timepoints growth to flat and dropped the n_estimators slope ~7×. The dictionary family still has the full n_timepoints growth, which is why MUSE/WEASEL are the strongest remaining candidates.

@MatthewMiddlehurst MatthewMiddlehurst 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.

Looks fine, but is there a need to do it differently got both transforms? I would other go with the inline or function for both if possible.

@TonyBagnall

Copy link
Copy Markdown
Contributor Author

Looks fine, but is there a need to do it differently got both transforms? I would other go with the inline or function for both if possible.

yes fair enough, Ive gone with functions rather than inlining and generalised to a shared helpers, _fit_feature,
_fit_transform_feature and _transform_feature. Each takes the 2D single-channel slice, expands it to numpy3D and routes aeon collection transformers to the private method.

One thing worth flagging, since a shared helper would otherwise have quietly decided it: the two transforms already differed in how they call a feature that isn't a collection transformer. RandomIntervals passes the expanded 3D slice and y; SupervisedIntervals passes the 2D slice and no y. That predates this PR — both did it before I touched them. I've preserved both exactly via an expand_fallback argument rather than silently unifying them, since that would be a behaviour change on a path this PR isn't otherwise about.

Output is unchanged: my bot diffed 36 output arrays across both transforms plus DrCIF and RSTSF — univariate and multivariate, n_jobs 1 and 2, callable and transformer features, dilation, normalise_for_search=False — against the previous commit, and they're bit-identical.

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

Labels

enhancement New feature, improvement request or other non-bug code enhancement transformations Transformations package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH] Move collection variance check out of _check_X so it only validates top-level estimator inputs

2 participants