spotoptim 3.0.0__tar.gz → 3.2.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.
Files changed (92) hide show
  1. {spotoptim-3.0.0 → spotoptim-3.2.0}/PKG-INFO +2 -1
  2. {spotoptim-3.0.0 → spotoptim-3.2.0}/pyproject.toml +2 -1
  3. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/data/__init__.py +21 -1
  4. spotoptim-3.2.0/src/spotoptim/data/manydataset.py +375 -0
  5. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/__init__.py +5 -0
  6. spotoptim-3.2.0/src/spotoptim/function/sequence_cv_objective.py +278 -0
  7. spotoptim-3.2.0/src/spotoptim/nn/__init__.py +44 -0
  8. spotoptim-3.2.0/src/spotoptim/nn/many_to_many_rnn.py +234 -0
  9. spotoptim-3.2.0/src/spotoptim/nn/optimizer.py +202 -0
  10. spotoptim-3.2.0/src/spotoptim/nn/training.py +234 -0
  11. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/__init__.py +3 -0
  12. spotoptim-3.2.0/src/spotoptim/utils/seed.py +60 -0
  13. spotoptim-3.0.0/src/spotoptim/nn/__init__.py +0 -26
  14. {spotoptim-3.0.0 → spotoptim-3.2.0}/README.md +0 -0
  15. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/SpotOptim.py +0 -0
  16. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/__init__.py +0 -0
  17. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/core/__init__.py +0 -0
  18. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/core/data.py +0 -0
  19. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/core/experiment.py +0 -0
  20. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/core/protocol.py +0 -0
  21. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/core/storage.py +0 -0
  22. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/data/base.py +0 -0
  23. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/data/diabetes.py +0 -0
  24. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/datasets/__init__.py +0 -0
  25. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/datasets/py.typed +0 -0
  26. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/datasets/test01.csv +0 -0
  27. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/datasets/test02.csv +0 -0
  28. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/datasets/test11.csv +0 -0
  29. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/eda/__init__.py +0 -0
  30. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/eda/plots.py +0 -0
  31. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/factor_analyzer/__init__.py +0 -0
  32. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/factor_analyzer/confirmatory_factor_analyzer.py +0 -0
  33. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/factor_analyzer/factor_analyzer.py +0 -0
  34. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/factor_analyzer/factor_analyzer_rotator.py +0 -0
  35. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/factor_analyzer/factor_analyzer_utils.py +0 -0
  36. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/cd_data.csv +0 -0
  37. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/forr08a.py +0 -0
  38. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/mo.py +0 -0
  39. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/remote.py +0 -0
  40. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/so.py +0 -0
  41. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/function/torch_objective.py +0 -0
  42. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/hyperparameters/__init__.py +0 -0
  43. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/hyperparameters/parameters.py +0 -0
  44. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/hyperparameters/repr_helpers.py +0 -0
  45. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/inspection/__init__.py +0 -0
  46. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/inspection/importance.py +0 -0
  47. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/inspection/predictions.py +0 -0
  48. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/mo/__init__.py +0 -0
  49. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/mo/mo_mm.py +0 -0
  50. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/mo/pareto.py +0 -0
  51. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/nn/linear_regressor.py +0 -0
  52. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/nn/mlp.py +0 -0
  53. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/optimizer/__init__.py +0 -0
  54. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/optimizer/acquisition.py +0 -0
  55. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/optimizer/schedule_free.py +0 -0
  56. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/optimizer/wrapper.py +0 -0
  57. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/plot/__init__.py +0 -0
  58. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/plot/contour.py +0 -0
  59. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/plot/mo.py +0 -0
  60. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/plot/visualization.py +0 -0
  61. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/py.typed +0 -0
  62. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/reporting/__init__.py +0 -0
  63. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/reporting/analysis.py +0 -0
  64. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/reporting/results.py +0 -0
  65. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/sampling/__init__.py +0 -0
  66. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/sampling/design.py +0 -0
  67. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/sampling/effects.py +0 -0
  68. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/sampling/lhs.py +0 -0
  69. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/sampling/mm.py +0 -0
  70. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/__init__.py +0 -0
  71. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/kernels.py +0 -0
  72. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/kriging.py +0 -0
  73. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/mlp_surrogate.py +0 -0
  74. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/nystroem.py +0 -0
  75. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/pipeline.py +0 -0
  76. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/surrogate/simple_kriging.py +0 -0
  77. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/tricands/__init__.py +0 -0
  78. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/tricands/tricands.py +0 -0
  79. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/boundaries.py +0 -0
  80. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/convert.py +0 -0
  81. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/dimreduction.py +0 -0
  82. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/eval.py +0 -0
  83. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/file.py +0 -0
  84. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/mapping.py +0 -0
  85. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/ocba.py +0 -0
  86. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/pca.py +0 -0
  87. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/scaler.py +0 -0
  88. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/serialization.py +0 -0
  89. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/stats.py +0 -0
  90. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/tensorboard.py +0 -0
  91. {spotoptim-3.0.0 → spotoptim-3.2.0}/src/spotoptim/utils/transform.py +0 -0
  92. {spotoptim-3.0.0 → spotoptim-3.2.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.0.0
3
+ Version: 3.2.0
4
4
  Summary: Sequential Parameter Optimization Toolbox
5
5
  Author: bartzbeielstein
6
6
  Author-email: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com>
@@ -36,6 +36,7 @@ Requires-Dist: requests>=2.32.3 ; extra == 'remote'
36
36
  Requires-Dist: statsmodels>=0.14.6 ; extra == 'stats'
37
37
  Requires-Dist: torch>=2.9.1 ; extra == 'torch'
38
38
  Requires-Dist: tensorboard>=2.20.0 ; extra == 'torch'
39
+ Requires-Dist: torchmetrics>=1.8.2 ; extra == 'torch'
39
40
  Requires-Dist: matplotlib>=3.10.7 ; extra == 'viz'
40
41
  Requires-Dist: seaborn>=0.13.2 ; extra == 'viz'
41
42
  Requires-Python: >=3.13
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spotoptim"
3
- version = "3.0.0"
3
+ version = "3.2.0"
4
4
  description = "Sequential Parameter Optimization Toolbox"
5
5
  readme = "README.md"
6
6
  license = { text = "AGPL-3.0-or-later" }
@@ -44,6 +44,7 @@ dev = [
44
44
  torch = [
45
45
  "torch>=2.9.1",
46
46
  "tensorboard>=2.20.0",
47
+ "torchmetrics>=1.8.2",
47
48
  ]
48
49
  viz = [
49
50
  "matplotlib>=3.10.7",
@@ -10,11 +10,31 @@ machine learning tasks.
10
10
 
11
11
  from .base import Config, FileConfig
12
12
 
13
- __all__ = ["DiabetesDataset", "get_diabetes_dataloaders", "Config", "FileConfig"]
13
+ __all__ = [
14
+ "DiabetesDataset",
15
+ "get_diabetes_dataloaders",
16
+ "Config",
17
+ "FileConfig",
18
+ "ManyToManyDataset",
19
+ "ManyToOneDataset",
20
+ "PadSequenceManyToMany",
21
+ "PadSequenceManyToOne",
22
+ "load_sequence_data",
23
+ "load_pooled_sequence_data",
24
+ ]
14
25
 
15
26
  _lazy_map = {
16
27
  "DiabetesDataset": ("spotoptim.data.diabetes", "DiabetesDataset"),
17
28
  "get_diabetes_dataloaders": ("spotoptim.data.diabetes", "get_diabetes_dataloaders"),
29
+ "ManyToManyDataset": ("spotoptim.data.manydataset", "ManyToManyDataset"),
30
+ "ManyToOneDataset": ("spotoptim.data.manydataset", "ManyToOneDataset"),
31
+ "PadSequenceManyToMany": ("spotoptim.data.manydataset", "PadSequenceManyToMany"),
32
+ "PadSequenceManyToOne": ("spotoptim.data.manydataset", "PadSequenceManyToOne"),
33
+ "load_sequence_data": ("spotoptim.data.manydataset", "load_sequence_data"),
34
+ "load_pooled_sequence_data": (
35
+ "spotoptim.data.manydataset",
36
+ "load_pooled_sequence_data",
37
+ ),
18
38
  }
19
39
 
20
40
 
@@ -0,0 +1,375 @@
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, Tuple, 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 ConcatDataset, 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)
237
+
238
+
239
+ def load_sequence_data(
240
+ data: pd.DataFrame,
241
+ target: str,
242
+ group_by: str,
243
+ drop: Optional[Union[str, List[str]]] = None,
244
+ input_features: Optional[List[str]] = None,
245
+ feature_scaling=None,
246
+ target_scaling=None,
247
+ dataset_type: str = "many_to_many",
248
+ ) -> Tuple[Dataset, pd.DataFrame]:
249
+ """Group a DataFrame into a variable-length sequence dataset.
250
+
251
+ Splits ``data`` by the values of ``group_by`` (e.g. one compressor speed
252
+ line per group) and wraps the groups in a `ManyToManyDataset` or
253
+ `ManyToOneDataset`. Ported from the schu25a study's ``load_data`` helper
254
+ (``src/rnn/utils.py``); the experiment-specific column defaults were
255
+ removed.
256
+
257
+ Args:
258
+ data (pd.DataFrame): Input data; one row per time step.
259
+ target (str): The target column name.
260
+ group_by (str): Column whose values define the sequences.
261
+ drop (Optional[Union[str, List[str]]]): Column(s) to drop from the
262
+ groups before extracting features. Defaults to None.
263
+ input_features (Optional[List[str]]): Columns scaled by
264
+ ``feature_scaling``. Only used when ``feature_scaling`` is given;
265
+ the dataset features are always all columns except ``drop`` and
266
+ ``target``. Defaults to None.
267
+ feature_scaling: Optional sklearn-style scaler; applied in place to
268
+ ``data[input_features]`` via ``fit_transform``. Defaults to None.
269
+ target_scaling: Optional sklearn-style scaler; applied in place to
270
+ ``data[target]`` via ``fit_transform``. Defaults to None.
271
+ dataset_type (str, optional): Dataset flavor. Options:
272
+ - "many_to_many": one target value per time step.
273
+ - "many_to_one": one scalar target per sequence.
274
+ Defaults to "many_to_many".
275
+
276
+ Returns:
277
+ tuple: ``(dataset, data)`` — the sequence dataset and the (possibly
278
+ scaled) DataFrame.
279
+
280
+ Raises:
281
+ ValueError: If ``dataset_type`` is not "many_to_many" or "many_to_one".
282
+
283
+ Examples:
284
+ ```{python}
285
+ import pandas as pd
286
+ from spotoptim.data.manydataset import load_sequence_data
287
+
288
+ df = pd.DataFrame({
289
+ "line": [1, 1, 1, 2, 2],
290
+ "x": [0.1, 0.2, 0.3, 0.4, 0.5],
291
+ "y": [1.0, 2.0, 3.0, 4.0, 5.0],
292
+ })
293
+ ds, df = load_sequence_data(df, target="y", group_by="line", drop="line")
294
+ print(len(ds), ds[0][0].shape)
295
+ ```
296
+ """
297
+ if feature_scaling is not None:
298
+ data[input_features] = feature_scaling.fit_transform(data[input_features])
299
+
300
+ if target_scaling is not None:
301
+ data[target] = target_scaling.fit_transform(data[target])
302
+
303
+ groups = [group for _, group in data.groupby(group_by)]
304
+
305
+ if dataset_type == "many_to_many":
306
+ return ManyToManyDataset(groups, target=target, drop=drop), data
307
+ elif dataset_type == "many_to_one":
308
+ return ManyToOneDataset(groups, target=target, drop=drop), data
309
+ raise ValueError(
310
+ f"dataset_type {dataset_type!r} not supported. "
311
+ "Options: 'many_to_many', 'many_to_one'."
312
+ )
313
+
314
+
315
+ def load_pooled_sequence_data(
316
+ data_list: List[pd.DataFrame],
317
+ target: str,
318
+ group_by: str,
319
+ drop: Optional[Union[str, List[str]]] = None,
320
+ input_features: Optional[List[str]] = None,
321
+ feature_scaling=None,
322
+ target_scaling=None,
323
+ dataset_type: str = "many_to_many",
324
+ ) -> ConcatDataset:
325
+ """Pool several DataFrames into one concatenated sequence dataset.
326
+
327
+ Applies `load_sequence_data` to every DataFrame (e.g. one compressor map
328
+ each) and concatenates the resulting sequence datasets — the pooled
329
+ training set of the schu25a "global training" workflow (ported from
330
+ ``load_pretrain_data`` in ``src/rnn/utils.py``).
331
+
332
+ Args:
333
+ data_list (List[pd.DataFrame]): Input DataFrames; one per map.
334
+ target (str): The target column name.
335
+ group_by (str): Column whose values define the sequences.
336
+ drop (Optional[Union[str, List[str]]]): Column(s) to drop from the
337
+ groups before extracting features. Defaults to None.
338
+ input_features (Optional[List[str]]): Columns scaled by
339
+ ``feature_scaling``; see `load_sequence_data`. Defaults to None.
340
+ feature_scaling: Optional sklearn-style scaler, applied per DataFrame.
341
+ Defaults to None.
342
+ target_scaling: Optional sklearn-style scaler, applied per DataFrame.
343
+ Defaults to None.
344
+ dataset_type (str, optional): "many_to_many" or "many_to_one".
345
+ Defaults to "many_to_many".
346
+
347
+ Returns:
348
+ ConcatDataset: The concatenation of the per-DataFrame sequence datasets.
349
+
350
+ Examples:
351
+ ```{python}
352
+ import pandas as pd
353
+ from spotoptim.data.manydataset import load_pooled_sequence_data
354
+
355
+ df1 = pd.DataFrame({"line": [1, 1, 2], "x": [0.1, 0.2, 0.3], "y": [1.0, 2.0, 3.0]})
356
+ df2 = pd.DataFrame({"line": [1, 1], "x": [0.4, 0.5], "y": [4.0, 5.0]})
357
+ pooled = load_pooled_sequence_data([df1, df2], target="y", group_by="line", drop="line")
358
+ print(len(pooled))
359
+ ```
360
+ """
361
+ ds_ls = []
362
+ for data in data_list:
363
+ ds, _ = load_sequence_data(
364
+ data,
365
+ target=target,
366
+ group_by=group_by,
367
+ drop=drop,
368
+ input_features=input_features,
369
+ feature_scaling=feature_scaling,
370
+ target_scaling=target_scaling,
371
+ dataset_type=dataset_type,
372
+ )
373
+ ds_ls.append(ds)
374
+
375
+ return ConcatDataset(ds_ls)
@@ -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