spotoptim 2.1.2__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spotoptim-2.1.2 → spotoptim-3.1.0}/PKG-INFO +1 -2
- {spotoptim-2.1.2 → spotoptim-3.1.0}/pyproject.toml +1 -3
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/SpotOptim.py +61 -8
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/data/__init__.py +14 -1
- spotoptim-3.1.0/src/spotoptim/data/manydataset.py +236 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/__init__.py +5 -0
- spotoptim-3.1.0/src/spotoptim/function/sequence_cv_objective.py +278 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/nn/__init__.py +15 -1
- spotoptim-3.1.0/src/spotoptim/nn/many_to_many_rnn.py +234 -0
- spotoptim-3.1.0/src/spotoptim/nn/optimizer.py +202 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/optimizer/acquisition.py +33 -5
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/kernels.py +3 -18
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/kriging.py +0 -7
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/transform.py +2 -1
- {spotoptim-2.1.2 → spotoptim-3.1.0}/README.md +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/core/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/core/data.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/core/experiment.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/core/protocol.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/core/storage.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/data/base.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/data/diabetes.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/datasets/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/datasets/py.typed +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/datasets/test01.csv +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/datasets/test02.csv +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/datasets/test11.csv +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/eda/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/eda/plots.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/factor_analyzer/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/factor_analyzer/confirmatory_factor_analyzer.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/factor_analyzer/factor_analyzer.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/factor_analyzer/factor_analyzer_rotator.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/factor_analyzer/factor_analyzer_utils.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/cd_data.csv +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/forr08a.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/mo.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/remote.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/so.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/function/torch_objective.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/hyperparameters/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/hyperparameters/parameters.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/hyperparameters/repr_helpers.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/inspection/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/inspection/importance.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/inspection/predictions.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/mo/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/mo/mo_mm.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/mo/pareto.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/nn/linear_regressor.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/nn/mlp.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/optimizer/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/optimizer/schedule_free.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/optimizer/wrapper.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/plot/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/plot/contour.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/plot/mo.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/plot/visualization.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/py.typed +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/reporting/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/reporting/analysis.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/reporting/results.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/sampling/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/sampling/design.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/sampling/effects.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/sampling/lhs.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/sampling/mm.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/mlp_surrogate.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/nystroem.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/pipeline.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/surrogate/simple_kriging.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/tricands/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/tricands/tricands.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/__init__.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/boundaries.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/convert.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/dimreduction.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/eval.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/file.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/mapping.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/ocba.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/pca.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/scaler.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/serialization.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/stats.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/tensorboard.py +0 -0
- {spotoptim-2.1.2 → spotoptim-3.1.0}/src/spotoptim/utils/variables.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: spotoptim
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Sequential Parameter Optimization Toolbox
|
|
5
5
|
Author: bartzbeielstein
|
|
6
6
|
Author-email: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com>
|
|
@@ -22,7 +22,6 @@ Requires-Dist: pytest-timeout>=2.3.0 ; extra == 'dev'
|
|
|
22
22
|
Requires-Dist: black>=24.1.0 ; extra == 'dev'
|
|
23
23
|
Requires-Dist: isort>=5.13.0 ; extra == 'dev'
|
|
24
24
|
Requires-Dist: ruff>=0.3.0 ; extra == 'dev'
|
|
25
|
-
Requires-Dist: safety>=3.0.0 ; extra == 'dev'
|
|
26
25
|
Requires-Dist: bandit>=1.8.0 ; extra == 'dev'
|
|
27
26
|
Requires-Dist: pre-commit>=3.7.0 ; extra == 'dev'
|
|
28
27
|
Requires-Dist: ty>=0.0.29 ; extra == 'dev'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "spotoptim"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.1.0"
|
|
4
4
|
description = "Sequential Parameter Optimization Toolbox"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "AGPL-3.0-or-later" }
|
|
@@ -35,7 +35,6 @@ dev = [
|
|
|
35
35
|
"black>=24.1.0",
|
|
36
36
|
"isort>=5.13.0",
|
|
37
37
|
"ruff>=0.3.0",
|
|
38
|
-
"safety>=3.0.0",
|
|
39
38
|
"bandit>=1.8.0",
|
|
40
39
|
"pre-commit>=3.7.0",
|
|
41
40
|
"ty>=0.0.29",
|
|
@@ -67,7 +66,6 @@ dev = [
|
|
|
67
66
|
"black>=24.1.0",
|
|
68
67
|
"isort>=5.13.0",
|
|
69
68
|
"ruff>=0.3.0",
|
|
70
|
-
"safety>=3.0.0",
|
|
71
69
|
"bandit>=1.8.0",
|
|
72
70
|
"pre-commit>=3.7.0",
|
|
73
71
|
"ty>=0.0.29",
|
|
@@ -278,7 +278,13 @@ class SpotOptim(BaseEstimator):
|
|
|
278
278
|
For example, max_iter=30 with n_initial=10 will perform 10 initial evaluations plus
|
|
279
279
|
20 sequential optimization iterations. Defaults to 20.
|
|
280
280
|
n_initial (int, optional):
|
|
281
|
-
Number of initial design points. Defaults to 10.
|
|
281
|
+
Number of initial design points. Defaults to 10. A ``UserWarning`` is
|
|
282
|
+
raised when ``n_initial < 2 * n_dim``: a cold-start design that small
|
|
283
|
+
tends to under-sample the search space and gives the surrogate too
|
|
284
|
+
little signal to model dimension interactions. The warning is
|
|
285
|
+
guidance only (existing scripts with a deliberately small
|
|
286
|
+
``n_initial`` keep working unchanged); consider
|
|
287
|
+
``n_initial=max(10, 2 * n_dim)`` for cold starts.
|
|
282
288
|
surrogate (object, optional):
|
|
283
289
|
Surrogate model with scikit-learn interface (fit/predict methods).
|
|
284
290
|
If None, uses a Gaussian Process Regressor with Matern kernel. Default configuration::
|
|
@@ -874,6 +880,26 @@ class SpotOptim(BaseEstimator):
|
|
|
874
880
|
# Derived attribute dimension n_dim
|
|
875
881
|
self.n_dim = len(self.bounds)
|
|
876
882
|
|
|
883
|
+
# Guidance for cold-start designs (ADR 2026-07-05, decision 4): a
|
|
884
|
+
# design with n_initial < 2*n_dim tends to under-sample the search
|
|
885
|
+
# space, giving the surrogate too little signal to model dimension
|
|
886
|
+
# interactions. Warning-only — existing small-budget scripts keep
|
|
887
|
+
# working unchanged. Nested catch_warnings + "always" mirrors
|
|
888
|
+
# _validate_factor_surrogate_compat: makes the warning visible even
|
|
889
|
+
# under the default warnings_filter="ignore".
|
|
890
|
+
if self.n_initial < 2 * self.n_dim:
|
|
891
|
+
with warnings.catch_warnings():
|
|
892
|
+
warnings.simplefilter("always")
|
|
893
|
+
warnings.warn(
|
|
894
|
+
f"n_initial ({self.n_initial}) is below 2 * n_dim "
|
|
895
|
+
f"({2 * self.n_dim}) for a {self.n_dim}-dimensional problem. "
|
|
896
|
+
f"Cold-start designs this small may under-sample the search "
|
|
897
|
+
f"space; consider n_initial=max(10, 2 * n_dim) = "
|
|
898
|
+
f"{max(10, 2 * self.n_dim)}.",
|
|
899
|
+
UserWarning,
|
|
900
|
+
stacklevel=2,
|
|
901
|
+
)
|
|
902
|
+
|
|
877
903
|
# Default variable types
|
|
878
904
|
if self.var_type is None:
|
|
879
905
|
self.var_type = self.detect_var_type()
|
|
@@ -2503,6 +2529,8 @@ class SpotOptim(BaseEstimator):
|
|
|
2503
2529
|
* message: termination message indicating reason for stopping, including statistics (function value, iterations, evaluations)
|
|
2504
2530
|
* X: all evaluated points in Natural Space
|
|
2505
2531
|
* y: all function values
|
|
2532
|
+
* x_encoded: best point in full-dimension natural-NUMERIC encoding (factor dimensions as integer level indices) — the representation restart injection and ``validate_x0`` consume
|
|
2533
|
+
* X_encoded: all evaluated points in the same numeric encoding (numeric dtype even when factor dimensions map to string labels in ``X``)
|
|
2506
2534
|
|
|
2507
2535
|
Examples:
|
|
2508
2536
|
```{python}
|
|
@@ -2615,9 +2643,16 @@ class SpotOptim(BaseEstimator):
|
|
|
2615
2643
|
|
|
2616
2644
|
if self.restart_inject_best:
|
|
2617
2645
|
# Inject the current global best into the next run's initial design.
|
|
2618
|
-
#
|
|
2619
|
-
#
|
|
2620
|
-
|
|
2646
|
+
# Use the pre-string-map numeric encoding (x_encoded) when available:
|
|
2647
|
+
# best_res.x has factor dims mapped to string labels, which
|
|
2648
|
+
# validate_x0's `low <= val <= high` bounds check cannot compare
|
|
2649
|
+
# (UFuncTypeError). x_encoded carries the same full-dim natural-scale
|
|
2650
|
+
# point with factor dims still as integer codes.
|
|
2651
|
+
# validate_x0 converts to internal scale so the injected point can be
|
|
2652
|
+
# mixed with LHS samples.
|
|
2653
|
+
self.x0 = self.validate_x0(
|
|
2654
|
+
getattr(best_res, "x_encoded", best_res.x)
|
|
2655
|
+
)
|
|
2621
2656
|
# Keep current_X0 unset so the initial design is rebuilt around the injected x0.
|
|
2622
2657
|
current_X0 = None
|
|
2623
2658
|
|
|
@@ -2641,8 +2676,13 @@ class SpotOptim(BaseEstimator):
|
|
|
2641
2676
|
# Find best result based on 'fun'
|
|
2642
2677
|
best_result = min(self.restarts_results_, key=lambda r: r.fun)
|
|
2643
2678
|
|
|
2644
|
-
# Merge results from all restart runs
|
|
2645
|
-
|
|
2679
|
+
# Merge results from all restart runs. Use the numeric encoding
|
|
2680
|
+
# (X_encoded) rather than the string-mapped X: on a factor problem the
|
|
2681
|
+
# latter has object dtype, which would leave self.X_ non-numeric after
|
|
2682
|
+
# a restart and break the post-restart surrogate fit.
|
|
2683
|
+
X_all_list = [
|
|
2684
|
+
getattr(res, "X_encoded", res.X) for res in self.restarts_results_
|
|
2685
|
+
]
|
|
2646
2686
|
y_all_list = [res.y for res in self.restarts_results_]
|
|
2647
2687
|
|
|
2648
2688
|
# Concatenate all evaluations
|
|
@@ -2653,8 +2693,9 @@ class SpotOptim(BaseEstimator):
|
|
|
2653
2693
|
# Aggregated iterations (sum of all runs)
|
|
2654
2694
|
self.n_iter_ = sum(getattr(res, "nit", 0) for res in self.restarts_results_)
|
|
2655
2695
|
|
|
2656
|
-
# Update best solution found
|
|
2657
|
-
|
|
2696
|
+
# Update best solution found. Same numeric-encoding rationale as X_
|
|
2697
|
+
# above, for cross-path (x0 injection vs. best_x_) consistency.
|
|
2698
|
+
self.best_x_ = getattr(best_result, "x_encoded", best_result.x)
|
|
2658
2699
|
self.best_y_ = best_result.fun
|
|
2659
2700
|
|
|
2660
2701
|
# If the patience-based early-stopping rule fired, rewrite the result
|
|
@@ -3563,6 +3604,14 @@ class SpotOptim(BaseEstimator):
|
|
|
3563
3604
|
message=status_message,
|
|
3564
3605
|
X=X_result,
|
|
3565
3606
|
y=self.y_,
|
|
3607
|
+
# Pre-string-map numeric form of x/X (full-dim, natural
|
|
3608
|
+
# scale, with factor dims as integer codes rather than
|
|
3609
|
+
# string labels). optimize()'s restart-inject path and the
|
|
3610
|
+
# cross-restart X_ merge use these instead of x/X, so a
|
|
3611
|
+
# factor problem's restart never feeds a string into
|
|
3612
|
+
# validate_x0()/the surrogate (UFuncTypeError).
|
|
3613
|
+
x_encoded=best_x_full,
|
|
3614
|
+
X_encoded=X_full,
|
|
3566
3615
|
)
|
|
3567
3616
|
return "RESTART", res
|
|
3568
3617
|
|
|
@@ -3621,6 +3670,10 @@ class SpotOptim(BaseEstimator):
|
|
|
3621
3670
|
message=message,
|
|
3622
3671
|
X=X_result,
|
|
3623
3672
|
y=self.y_,
|
|
3673
|
+
# See the RESTART branch above: pre-string-map numeric x/X, used
|
|
3674
|
+
# by optimize()'s restart-inject path and cross-restart X_ merge.
|
|
3675
|
+
x_encoded=best_x_full,
|
|
3676
|
+
X_encoded=X_full,
|
|
3624
3677
|
)
|
|
3625
3678
|
|
|
3626
3679
|
def determine_termination(self, timeout_start: float) -> str:
|
|
@@ -10,11 +10,24 @@ machine learning tasks.
|
|
|
10
10
|
|
|
11
11
|
from .base import Config, FileConfig
|
|
12
12
|
|
|
13
|
-
__all__ = [
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DiabetesDataset",
|
|
15
|
+
"get_diabetes_dataloaders",
|
|
16
|
+
"Config",
|
|
17
|
+
"FileConfig",
|
|
18
|
+
"ManyToManyDataset",
|
|
19
|
+
"ManyToOneDataset",
|
|
20
|
+
"PadSequenceManyToMany",
|
|
21
|
+
"PadSequenceManyToOne",
|
|
22
|
+
]
|
|
14
23
|
|
|
15
24
|
_lazy_map = {
|
|
16
25
|
"DiabetesDataset": ("spotoptim.data.diabetes", "DiabetesDataset"),
|
|
17
26
|
"get_diabetes_dataloaders": ("spotoptim.data.diabetes", "get_diabetes_dataloaders"),
|
|
27
|
+
"ManyToManyDataset": ("spotoptim.data.manydataset", "ManyToManyDataset"),
|
|
28
|
+
"ManyToOneDataset": ("spotoptim.data.manydataset", "ManyToOneDataset"),
|
|
29
|
+
"PadSequenceManyToMany": ("spotoptim.data.manydataset", "PadSequenceManyToMany"),
|
|
30
|
+
"PadSequenceManyToOne": ("spotoptim.data.manydataset", "PadSequenceManyToOne"),
|
|
18
31
|
}
|
|
19
32
|
|
|
20
33
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 bartzbeielstein
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
"""Variable-length sequence datasets and padding collates for recurrent models.
|
|
6
|
+
|
|
7
|
+
Ported from spotPython (``spotpython.data.manydataset`` and the padding
|
|
8
|
+
collates from ``spotpython.data.lightdatamodule``) without any Lightning
|
|
9
|
+
dependency. Each dataset item is one complete sequence (e.g. one operating
|
|
10
|
+
curve of a compressor map), so batches must be padded with the collate
|
|
11
|
+
classes provided here.
|
|
12
|
+
|
|
13
|
+
Requires the ``torch`` optional extra (``pip install 'spotoptim[torch]'``).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import List, Optional, Union
|
|
17
|
+
|
|
18
|
+
import pandas as pd
|
|
19
|
+
import torch
|
|
20
|
+
from torch.nn.utils.rnn import pad_sequence
|
|
21
|
+
from torch.utils.data import Dataset
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ManyToManyDataset(Dataset):
|
|
25
|
+
"""Sequence dataset with one target value per time step.
|
|
26
|
+
|
|
27
|
+
Each element of ``df_list`` is one variable-length sequence; item ``i``
|
|
28
|
+
is the pair ``(features_i, targets_i)`` with shapes ``(T_i, n_features)``
|
|
29
|
+
and ``(T_i,)``.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
df_list (List[pd.DataFrame]): List of pandas DataFrames, one per sequence.
|
|
33
|
+
target (str): The target column name.
|
|
34
|
+
drop (Optional[Union[str, List[str]]]): Column(s) to drop from the
|
|
35
|
+
DataFrames before extracting features. If a listed column is
|
|
36
|
+
missing, no column is dropped. Defaults to None.
|
|
37
|
+
dtype (torch.dtype): Data type for the tensors. Defaults to ``torch.float32``.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
data (List[pd.DataFrame]): DataFrames with the ``drop`` columns removed.
|
|
41
|
+
target (List[torch.Tensor]): Per-sequence target tensors, shape ``(T_i,)``.
|
|
42
|
+
features (List[torch.Tensor]): Per-sequence feature tensors, shape
|
|
43
|
+
``(T_i, n_features)``.
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
```{python}
|
|
47
|
+
import pandas as pd
|
|
48
|
+
from spotoptim.data.manydataset import ManyToManyDataset
|
|
49
|
+
|
|
50
|
+
df1 = pd.DataFrame({"x": [1.0, 2.0, 3.0], "y": [2.0, 4.0, 6.0]})
|
|
51
|
+
df2 = pd.DataFrame({"x": [4.0, 5.0], "y": [8.0, 10.0]})
|
|
52
|
+
ds = ManyToManyDataset([df1, df2], target="y")
|
|
53
|
+
print(len(ds))
|
|
54
|
+
features, targets = ds[0]
|
|
55
|
+
print(features.shape, targets.shape)
|
|
56
|
+
```
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
df_list: List[pd.DataFrame],
|
|
62
|
+
target: str,
|
|
63
|
+
drop: Optional[Union[str, List[str]]] = None,
|
|
64
|
+
dtype: torch.dtype = torch.float32,
|
|
65
|
+
):
|
|
66
|
+
if drop is None:
|
|
67
|
+
self.data = list(df_list)
|
|
68
|
+
else:
|
|
69
|
+
try:
|
|
70
|
+
self.data = [df.drop(drop, axis=1) for df in df_list]
|
|
71
|
+
except KeyError:
|
|
72
|
+
self.data = df_list
|
|
73
|
+
self.target = [
|
|
74
|
+
torch.tensor(df[target].to_numpy(), dtype=dtype) for df in self.data
|
|
75
|
+
]
|
|
76
|
+
self.features = [
|
|
77
|
+
torch.tensor(df.drop([target], axis=1).to_numpy(), dtype=dtype)
|
|
78
|
+
for df in self.data
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def __getitem__(self, index: int):
|
|
82
|
+
"""Return the ``(features, targets)`` pair of sequence ``index``."""
|
|
83
|
+
x = self.features[index]
|
|
84
|
+
y = self.target[index]
|
|
85
|
+
return x, y
|
|
86
|
+
|
|
87
|
+
def __len__(self) -> int:
|
|
88
|
+
"""Return the number of sequences."""
|
|
89
|
+
return len(self.data)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ManyToOneDataset(Dataset):
|
|
93
|
+
"""Sequence dataset with a single target value per sequence.
|
|
94
|
+
|
|
95
|
+
Like `ManyToManyDataset`, but item ``i`` pairs the full feature sequence
|
|
96
|
+
with the scalar target taken from the first row of sequence ``i``.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
df_list (List[pd.DataFrame]): List of pandas DataFrames, one per sequence.
|
|
100
|
+
target (str): The target column name.
|
|
101
|
+
drop (Optional[Union[str, List[str]]]): Column(s) to drop from the
|
|
102
|
+
DataFrames before extracting features. If a listed column is
|
|
103
|
+
missing, no column is dropped. Defaults to None.
|
|
104
|
+
dtype (torch.dtype): Data type for the tensors. Defaults to ``torch.float32``.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
data (List[pd.DataFrame]): DataFrames with the ``drop`` columns removed.
|
|
108
|
+
target (List[torch.Tensor]): Per-sequence scalar target tensors.
|
|
109
|
+
features (List[torch.Tensor]): Per-sequence feature tensors, shape
|
|
110
|
+
``(T_i, n_features)``.
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
```{python}
|
|
114
|
+
import pandas as pd
|
|
115
|
+
from spotoptim.data.manydataset import ManyToOneDataset
|
|
116
|
+
|
|
117
|
+
df1 = pd.DataFrame({"x": [1.0, 2.0, 3.0], "y": [5.0, 5.0, 5.0]})
|
|
118
|
+
df2 = pd.DataFrame({"x": [4.0, 5.0], "y": [7.0, 7.0]})
|
|
119
|
+
ds = ManyToOneDataset([df1, df2], target="y")
|
|
120
|
+
features, target = ds[1]
|
|
121
|
+
print(features.shape, target)
|
|
122
|
+
```
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
df_list: List[pd.DataFrame],
|
|
128
|
+
target: str,
|
|
129
|
+
drop: Optional[Union[str, List[str]]] = None,
|
|
130
|
+
dtype: torch.dtype = torch.float32,
|
|
131
|
+
):
|
|
132
|
+
if drop is None:
|
|
133
|
+
self.data = list(df_list)
|
|
134
|
+
else:
|
|
135
|
+
try:
|
|
136
|
+
self.data = [df.drop(drop, axis=1) for df in df_list]
|
|
137
|
+
except KeyError:
|
|
138
|
+
self.data = df_list
|
|
139
|
+
self.target = [
|
|
140
|
+
torch.tensor(df[target].to_numpy()[0], dtype=dtype) for df in self.data
|
|
141
|
+
]
|
|
142
|
+
self.features = [
|
|
143
|
+
torch.tensor(df.drop([target], axis=1).to_numpy(), dtype=dtype)
|
|
144
|
+
for df in self.data
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
def __getitem__(self, index: int):
|
|
148
|
+
"""Return the ``(features, target)`` pair of sequence ``index``."""
|
|
149
|
+
x = self.features[index]
|
|
150
|
+
y = self.target[index]
|
|
151
|
+
return x, y
|
|
152
|
+
|
|
153
|
+
def __len__(self) -> int:
|
|
154
|
+
"""Return the number of sequences."""
|
|
155
|
+
return len(self.data)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class PadSequenceManyToMany:
|
|
159
|
+
"""Padding collate for `ManyToManyDataset` batches.
|
|
160
|
+
|
|
161
|
+
Pads features and targets of a batch of variable-length sequences with
|
|
162
|
+
zeros to the longest sequence in the batch and records the true lengths,
|
|
163
|
+
as required by ``torch.nn.utils.rnn.pack_padded_sequence``.
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
```{python}
|
|
167
|
+
import pandas as pd
|
|
168
|
+
from torch.utils.data import DataLoader
|
|
169
|
+
from spotoptim.data.manydataset import ManyToManyDataset, PadSequenceManyToMany
|
|
170
|
+
|
|
171
|
+
df1 = pd.DataFrame({"x": [1.0, 2.0, 3.0], "y": [2.0, 4.0, 6.0]})
|
|
172
|
+
df2 = pd.DataFrame({"x": [4.0, 5.0], "y": [8.0, 10.0]})
|
|
173
|
+
ds = ManyToManyDataset([df1, df2], target="y")
|
|
174
|
+
dl = DataLoader(ds, batch_size=2, shuffle=False, collate_fn=PadSequenceManyToMany())
|
|
175
|
+
x, lengths, y = next(iter(dl))
|
|
176
|
+
print(x.shape, lengths.tolist(), y.shape)
|
|
177
|
+
```
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def __call__(self, batch):
|
|
181
|
+
"""Collate a batch into ``(padded_x, lengths, padded_y)``.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
batch: Sequence of ``(features, targets)`` pairs as produced by
|
|
185
|
+
`ManyToManyDataset`.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
tuple: ``(padded_x, lengths, padded_y)`` where ``padded_x`` has
|
|
189
|
+
shape ``(B, T_max, n_features)``, ``lengths`` is an int tensor of
|
|
190
|
+
shape ``(B,)``, and ``padded_y`` has shape ``(B, T_max)``.
|
|
191
|
+
"""
|
|
192
|
+
batch_x, batch_y = zip(*batch)
|
|
193
|
+
padded_batch_x = pad_sequence(list(batch_x), batch_first=True)
|
|
194
|
+
padded_batch_y = pad_sequence(list(batch_y), batch_first=True)
|
|
195
|
+
lengths = torch.tensor([len(x) for x in batch_x])
|
|
196
|
+
|
|
197
|
+
return padded_batch_x, lengths, padded_batch_y
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class PadSequenceManyToOne:
|
|
201
|
+
"""Padding collate for `ManyToOneDataset` batches.
|
|
202
|
+
|
|
203
|
+
Pads the feature sequences with zeros and stacks the scalar targets.
|
|
204
|
+
|
|
205
|
+
Examples:
|
|
206
|
+
```{python}
|
|
207
|
+
import pandas as pd
|
|
208
|
+
from torch.utils.data import DataLoader
|
|
209
|
+
from spotoptim.data.manydataset import ManyToOneDataset, PadSequenceManyToOne
|
|
210
|
+
|
|
211
|
+
df1 = pd.DataFrame({"x": [1.0, 2.0, 3.0], "y": [5.0, 5.0, 5.0]})
|
|
212
|
+
df2 = pd.DataFrame({"x": [4.0, 5.0], "y": [7.0, 7.0]})
|
|
213
|
+
ds = ManyToOneDataset([df1, df2], target="y")
|
|
214
|
+
dl = DataLoader(ds, batch_size=2, shuffle=False, collate_fn=PadSequenceManyToOne())
|
|
215
|
+
x, lengths, y = next(iter(dl))
|
|
216
|
+
print(x.shape, lengths.tolist(), y.shape)
|
|
217
|
+
```
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
def __call__(self, batch):
|
|
221
|
+
"""Collate a batch into ``(padded_x, lengths, y)``.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
batch: Sequence of ``(features, target)`` pairs as produced by
|
|
225
|
+
`ManyToOneDataset`.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
tuple: ``(padded_x, lengths, y)`` where ``padded_x`` has shape
|
|
229
|
+
``(B, T_max, n_features)``, ``lengths`` is an int tensor of shape
|
|
230
|
+
``(B,)``, and ``y`` is a tensor of shape ``(B,)``.
|
|
231
|
+
"""
|
|
232
|
+
batch_x, batch_y = zip(*batch)
|
|
233
|
+
padded_batch_x = pad_sequence(list(batch_x), batch_first=True)
|
|
234
|
+
lengths = torch.tensor([len(x) for x in batch_x])
|
|
235
|
+
|
|
236
|
+
return padded_batch_x, lengths, torch.tensor(batch_y)
|
|
@@ -69,11 +69,16 @@ __all__ = [
|
|
|
69
69
|
"branin",
|
|
70
70
|
"onevar",
|
|
71
71
|
"TorchObjective",
|
|
72
|
+
"SequenceCVObjective",
|
|
72
73
|
"objective_remote",
|
|
73
74
|
]
|
|
74
75
|
|
|
75
76
|
_lazy_map = {
|
|
76
77
|
"TorchObjective": ("spotoptim.function.torch_objective", "TorchObjective"),
|
|
78
|
+
"SequenceCVObjective": (
|
|
79
|
+
"spotoptim.function.sequence_cv_objective",
|
|
80
|
+
"SequenceCVObjective",
|
|
81
|
+
),
|
|
77
82
|
"objective_remote": ("spotoptim.function.remote", "objective_remote"),
|
|
78
83
|
}
|
|
79
84
|
|