Skip to content

Commit 621115a

Browse files
Merge remote-tracking branch 'origin/main' into mm/workflows
2 parents 609207a + 9b67bfb commit 621115a

File tree

101 files changed

+1077
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+1077
-426
lines changed

.github/workflows/periodic_tests.yml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,7 @@ jobs:
220220
with:
221221
python-version: "3.12"
222222

223-
- if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'no numba cache') }}
224-
name: Use numba cache to set env variables but not restore cache
223+
- name: Use numba cache to set env variables but not restore cache
225224
uses: ./.github/actions/numba_cache
226225
with:
227226
cache_name: "doctests"
@@ -242,6 +241,39 @@ jobs:
242241
- name: Run tests
243242
run: python -m pytest -n logical --doctest-only --doctest-continue-on-failure
244243

244+
multithreaded-estimators:
245+
runs-on: ubuntu-24.04
246+
247+
steps:
248+
- name: Checkout
249+
uses: actions/checkout@v4
250+
251+
- name: Setup Python 3.12
252+
uses: actions/setup-python@v5
253+
with:
254+
python-version: "3.12"
255+
256+
- name: Use numba cache to set env variables but not restore cache
257+
uses: ./.github/actions/numba_cache
258+
with:
259+
cache_name: "multithreaded-estimators"
260+
runner_os: ${{ runner.os }}
261+
python_version: "3.12"
262+
restore_cache: "false"
263+
264+
- name: Install aeon and dependencies
265+
uses: nick-fields/retry@v3
266+
with:
267+
timeout_minutes: 30
268+
max_attempts: 3
269+
command: python -m pip install .[all_extras,dev]
270+
271+
- name: Show dependencies
272+
run: python -m pip list
273+
274+
- name: Run tests
275+
run: python -m pytest aeon/testing/tests/ --enablethreading true -k "check_estimator_multithreading"
276+
245277
codecov:
246278
runs-on: ubuntu-24.04
247279

.github/workflows/pr_labelled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
steps:
1717
- name: Create app token
18-
uses: actions/create-github-app-token@v1
18+
uses: actions/create-github-app-token@v2
1919
id: app-token
2020
with:
2121
app-id: ${{ vars.PR_APP_ID }}

.github/workflows/pr_pytest.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,36 @@ jobs:
130130
- name: Run tests
131131
run: python -m pytest -n logical --doctest-only --doctest-continue-on-failure
132132

133+
multithreaded-estimators:
134+
runs-on: ubuntu-24.04
135+
136+
steps:
137+
- name: Checkout
138+
uses: actions/checkout@v4
139+
140+
- name: Setup Python 3.12
141+
uses: actions/setup-python@v5
142+
with:
143+
python-version: "3.12"
144+
145+
- if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'no numba cache') }}
146+
name: Restore numba cache
147+
uses: ./.github/actions/numba_cache
148+
with:
149+
cache_name: "multithreaded-estimators"
150+
runner_os: ${{ runner.os }}
151+
python_version: "3.12"
152+
153+
- name: Install aeon and dependencies
154+
uses: ./.github/actions/cpu_all_extras
155+
with:
156+
additional_extras: "dev"
157+
158+
- name: Show dependencies
159+
run: python -m pip list
160+
161+
- name: Run tests
162+
run: python -m pytest aeon/testing/tests/ --enablethreading true -k "check_estimator_multithreading"
133163

134164
codecov:
135165
# run the code coverage job if a PR has the 'codecov actions' label

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
args: [ "--create", "--python-folders", "aeon" ]
3030

3131
- repo: https://github.com/astral-sh/ruff-pre-commit
32-
rev: v0.12.0
32+
rev: v0.12.1
3333
hooks:
3434
- id: ruff
3535
args: [ "--fix"]

aeon/anomaly_detection/series/distance_based/_lof.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import numpy as np
99

1010
from aeon.anomaly_detection.series._pyodadapter import PyODAdapter
11+
from aeon.utils.validation import check_n_jobs
1112
from aeon.utils.validation._dependencies import _check_soft_dependencies
1213

1314

@@ -98,14 +99,16 @@ def __init__(
9899
# Set a default contamination value internally
99100
contamination = 0.1
100101

102+
self._n_jobs = check_n_jobs(n_jobs)
103+
101104
model = PyOD_LOF(
102105
n_neighbors=n_neighbors,
103106
algorithm=algorithm,
104107
leaf_size=leaf_size,
105108
metric=metric,
106109
p=p,
107110
metric_params=metric_params,
108-
n_jobs=n_jobs,
111+
n_jobs=self._n_jobs,
109112
contamination=contamination, # Only for PyOD LOF
110113
novelty=False, # Initialize unsupervised LOF (novelty=False)
111114
)
@@ -116,6 +119,7 @@ def __init__(
116119
self.p = p
117120
self.metric_params = metric_params
118121
self.n_jobs = n_jobs
122+
119123
super().__init__(pyod_model=model, window_size=window_size, stride=stride)
120124

121125
def _fit(self, X: np.ndarray, y: Union[np.ndarray, None] = None) -> None:

aeon/anomaly_detection/series/distance_based/_rockad.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from aeon.anomaly_detection.series.base import BaseSeriesAnomalyDetector
1515
from aeon.transformations.collection.convolution_based import Rocket
16+
from aeon.utils.validation import check_n_jobs
1617
from aeon.utils.windowing import reverse_windowing, sliding_windows
1718

1819

@@ -163,10 +164,12 @@ def _check_params(self, X: np.ndarray) -> None:
163164
)
164165

165166
def _inner_fit(self, X: np.ndarray) -> None:
167+
self._n_jobs = check_n_jobs(self.n_jobs)
168+
166169
self.rocket_transformer_ = Rocket(
167170
n_kernels=self.n_kernels,
168171
normalise=self.normalise,
169-
n_jobs=self.n_jobs,
172+
n_jobs=self._n_jobs,
170173
random_state=self.random_state,
171174
)
172175
# X: (n_windows, window_size)
@@ -199,7 +202,7 @@ def _inner_fit(self, X: np.ndarray) -> None:
199202
# Initialize estimator
200203
estimator = NearestNeighbors(
201204
n_neighbors=self.n_neighbors,
202-
n_jobs=self.n_jobs,
205+
n_jobs=self._n_jobs,
203206
metric=self.metric,
204207
algorithm="kd_tree",
205208
)

aeon/anomaly_detection/series/distribution_based/_copod.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import numpy as np
99

1010
from aeon.anomaly_detection.series._pyodadapter import PyODAdapter
11+
from aeon.utils.validation import check_n_jobs
1112
from aeon.utils.validation._dependencies import _check_soft_dependencies
1213

1314

@@ -46,7 +47,9 @@ def __init__(self, n_jobs: int = 1, window_size: int = 10, stride: int = 1):
4647
_check_soft_dependencies(*self._tags["python_dependencies"])
4748
from pyod.models.copod import COPOD
4849

49-
model = COPOD(n_jobs=n_jobs)
50+
self._n_jobs = check_n_jobs(n_jobs)
51+
52+
model = COPOD(n_jobs=self._n_jobs)
5053
self.n_jobs = n_jobs
5154
super().__init__(model, window_size=window_size, stride=stride)
5255

aeon/anomaly_detection/series/outlier_detection/_iforest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import numpy as np
99

1010
from aeon.anomaly_detection.series._pyodadapter import PyODAdapter
11+
from aeon.utils.validation import check_n_jobs
1112
from aeon.utils.validation._dependencies import _check_soft_dependencies
1213

1314

@@ -101,12 +102,14 @@ def __init__(
101102
_check_soft_dependencies(*self._tags["python_dependencies"])
102103
from pyod.models.iforest import IForest
103104

105+
self._n_jobs = check_n_jobs(n_jobs)
106+
104107
model = IForest(
105108
n_estimators=n_estimators,
106109
max_samples=max_samples,
107110
max_features=max_features,
108111
bootstrap=bootstrap,
109-
n_jobs=n_jobs,
112+
n_jobs=self._n_jobs,
110113
random_state=random_state,
111114
verbose=verbose,
112115
)

aeon/base/_base_collection.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class name: BaseCollectionEstimator
1717
fitted model/strategy - by convention, any attributes ending in "_"
1818
fitted state flag - is_fitted (property)
1919
fitted state inspection - check_is_fitted()
20-
2120
"""
2221

2322
from abc import abstractmethod
@@ -30,7 +29,6 @@ class name: BaseCollectionEstimator
3029
resolve_equal_length_inner_type,
3130
resolve_unequal_length_inner_type,
3231
)
33-
from aeon.utils.validation import check_n_jobs
3432
from aeon.utils.validation.collection import (
3533
get_n_cases,
3634
get_n_channels,
@@ -65,7 +63,6 @@ class BaseCollectionEstimator(BaseAeonEstimator):
6563
@abstractmethod
6664
def __init__(self):
6765
self.metadata_ = {} # metadata/properties of data seen in fit
68-
self._n_jobs = 1
6966

7067
super().__init__()
7168

@@ -115,21 +112,12 @@ def _preprocess_collection(self, X, store_metadata=True):
115112
"""
116113
if isinstance(X, list) and isinstance(X[0], np.ndarray):
117114
X = self._reshape_np_list(X)
115+
118116
meta = self._check_X(X)
119117
if len(self.metadata_) == 0 and store_metadata:
120118
self.metadata_ = meta
121119

122-
X = self._convert_X(X)
123-
# This usage of n_jobs is legacy, see issue #102
124-
multithread = self.get_tag("capability:multithreading")
125-
if multithread:
126-
if hasattr(self, "n_jobs"):
127-
self._n_jobs = check_n_jobs(self.n_jobs)
128-
else:
129-
raise AttributeError(
130-
"self.n_jobs must be set if capability:multithreading is True"
131-
)
132-
return X
120+
return self._convert_X(X)
133121

134122
def _check_X(self, X):
135123
"""

aeon/base/_estimators/hybrid/base_rist.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def __init__(
114114

115115
def _fit(self, X, y) -> object:
116116
self.n_cases_, self.n_channels_, self.n_timepoints_ = X.shape
117+
self._n_jobs = check_n_jobs(self.n_jobs)
117118

118119
rng = check_random_state(self.random_state)
119120

@@ -134,10 +135,9 @@ def _fit(self, X, y) -> object:
134135

135136
self._estimator = _clone_estimator(self._estimator, rng)
136137

137-
n_jobs = check_n_jobs(self.n_jobs)
138138
m = getattr(self._estimator, "n_jobs", "missing")
139139
if m != "missing":
140-
self._estimator.n_jobs = n_jobs
140+
self._estimator.n_jobs = self._n_jobs
141141

142142
if self.series_transformers == "default":
143143
self._series_transformers = [
@@ -168,7 +168,7 @@ def _fit(self, X, y) -> object:
168168
if st is not None:
169169
m = getattr(st, "n_jobs", "missing")
170170
if m != "missing":
171-
st.n_jobs = n_jobs
171+
st.n_jobs = self._n_jobs
172172

173173
s = st.fit_transform(X, y)
174174
else:
@@ -198,7 +198,7 @@ def _fit(self, X, y) -> object:
198198
row_numba_max,
199199
row_ppv,
200200
],
201-
n_jobs=n_jobs,
201+
n_jobs=self._n_jobs,
202202
)
203203
_set_random_states(ct, rng)
204204
self._transformers.append(ct)
@@ -214,7 +214,7 @@ def _fit(self, X, y) -> object:
214214
n_shapelets = self.n_shapelets
215215

216216
st = RandomDilatedShapeletTransform(
217-
max_shapelets=n_shapelets, n_jobs=n_jobs
217+
max_shapelets=n_shapelets, n_jobs=self._n_jobs
218218
)
219219
_set_random_states(st, rng)
220220
self._transformers.append(st)

0 commit comments

Comments
 (0)