torchsonn 0.1.0__py3-none-any.whl

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.
torchsonn/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ """TorchSONN — self-organizing polynomial neural network on PyTorch.
2
+
3
+ Everything needed for a full train / infer loop is re-exported here:
4
+
5
+ from torchsonn import SONN, SONNConfig, SONNDataset, Trainer
6
+
7
+ `PlotModel` is deliberately *not* re-exported: it pulls in the optional
8
+ `graphviz` dependency (``pip install "torchsonn[viz]"``), which would make a
9
+ bare `import torchsonn` fail on a base install. It stays reachable as
10
+ `from torchsonn.plot_model import PlotModel`.
11
+ """
12
+ from torchsonn.config import SONNConfig
13
+ from torchsonn.data.dataset import SONNDataset
14
+ from torchsonn.model import SONN
15
+ from torchsonn.trainer import Trainer
16
+
17
+
18
+ __all__ = [
19
+ "SONN",
20
+ "SONNConfig",
21
+ "SONNDataset",
22
+ "Trainer",
23
+ ]
@@ -0,0 +1,22 @@
1
+ """Hydra-structured config schemas for SONN.
2
+
3
+ Importing this package triggers `ConfigStore.store(name="default", node=SONNConfig)`,
4
+ so any subsequent `@hydra.main` / `hydra.compose` call that references
5
+ `- default` in its `defaults:` list resolves to the typed dataclass below.
6
+ """
7
+ from torchsonn.config.schemas import (
8
+ ModelConfig,
9
+ OptimizerConfig,
10
+ SchedulerConfig,
11
+ SONNConfig,
12
+ TrainConfig,
13
+ )
14
+
15
+
16
+ __all__ = [
17
+ "ModelConfig",
18
+ "OptimizerConfig",
19
+ "SchedulerConfig",
20
+ "SONNConfig",
21
+ "TrainConfig",
22
+ ]
@@ -0,0 +1,293 @@
1
+ """Structured-config dataclass schemas for SONN.
2
+
3
+ These mirror the keys that previously lived in `conf/default.yaml`, with two
4
+ upgrades over a plain YAML default:
5
+
6
+ • Type-checked field values — `omegaconf` rejects `model.type: 42` at
7
+ compose time instead of yielding cryptic AttributeErrors later.
8
+ • Schema discoverable via Python introspection — `dataclasses.fields(...)`,
9
+ IDE autocomplete, Sphinx autodoc all work.
10
+
11
+ Polymorphic / dynamically-typed fields are deliberately left as `List[Any]`
12
+ or `Optional[Dict[str, Any]]`:
13
+
14
+ • `ModelConfig.ref_functions` — heterogeneous list (bare names, mappings
15
+ carrying options); see `_parse_ref_function_entry` in model.py for the
16
+ parser. Schema-typing it stricter would force every entry into the
17
+ dict form, which would uglify the common `- linear_cov` case.
18
+ • `SchedulerConfig.scheduler_params` — varies per scheduler family.
19
+ • `TrainConfig.criterion_type` — string here (e.g. `"validate"`); coerced
20
+ to `CriterionType` enum inside `SONN.__init__` via `CriterionType.get`.
21
+ """
22
+ from dataclasses import dataclass, field
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ from hydra.core.config_store import ConfigStore
26
+
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Optimizer / scheduler — referenced from TrainConfig
30
+ # ---------------------------------------------------------------------------
31
+ @dataclass
32
+ class OutProjTrainConfig:
33
+ max_steps: int = 5000
34
+ lr: float = 1.0e-3
35
+ weight_decay: float = 0.0
36
+ # 'adam' | 'sgd' | 'lbfgs'. lbfgs runs full-batch with a closure and ignores
37
+ # the ReduceLROnPlateau schedule (lr_patience / lr_factor / lr_min) — it does
38
+ # its own line search. weight_decay is honored for lbfgs as a manual L2
39
+ # penalty added inside the closure.
40
+ optimizer: str = "adam"
41
+ eval_interval: int = 100
42
+ lr_patience: int = 5
43
+ lr_factor: float = 0.5
44
+ lr_min: float = 1.0e-5
45
+ early_stop_patience: int = 10
46
+ early_stop_min_delta: float = 1.0e-4
47
+
48
+ # LBFGS-only tuning. history_size matches PyTorch's default; max_iter is the
49
+ # number of inner LBFGS iterations per opt.step() call. For Otto-sized
50
+ # problems max_steps=100 outer x max_iter=20 inner ≈ sklearn's default.
51
+ lbfgs_history_size: int = 20
52
+ lbfgs_max_iter: int = 20
53
+ lbfgs_line_search: str = "strong_wolfe" # "" / None to disable
54
+
55
+
56
+ def _default_optimizer_params() -> Dict[str, Any]:
57
+ """Shared optimizer/trainer kwargs.
58
+
59
+ The trainer pops `min_lr` and `gamma` (LR-drop schedule) before handing
60
+ the rest to the chosen optimizer class. The optimizer-specific extras
61
+ (`betas`/`eps` for adam, `momentum`/`nesterov` for sgd, `history_size`
62
+ for lbfgs, `damping`/`max_damping` for newton-LM) are NOT enumerated
63
+ here — see the note on `OptimizerConfig.optimizer_params` below.
64
+ """
65
+ return {
66
+ "lr": 1.0e-4,
67
+ "min_lr": 1.0e-5,
68
+ "gamma": 0.5,
69
+ "clip_value": 1.0,
70
+ "clip_norm": 5.0,
71
+ }
72
+
73
+
74
+ @dataclass
75
+ class OptimizerConfig:
76
+ # 'adam' | 'sgd' | 'lbfgs' | 'newton' | 'newton-lm' — see optimizer_map
77
+ # in src/optimizers/__init__.py.
78
+ name: str = "adam"
79
+ verbose: bool = True
80
+ # Deliberately typed `Dict[str, Any]` rather than a nested dataclass:
81
+ # each optimizer family takes its own kwargs (lbfgs needs
82
+ # `history_size`, adam takes `betas`/`eps`, sgd takes `momentum`/
83
+ # `nesterov`, newton-lm takes `damping`/`max_damping`). A strict union
84
+ # schema would force every YAML to set every field. The optimizer
85
+ # constructor itself rejects unknown kwargs at instantiation time, so
86
+ # validation happens at the right boundary.
87
+ #
88
+ # If you want strict per-family typing later, switch to Hydra config
89
+ # groups: cs.store(group="optimizer", name="adam", node=AdamParams) and
90
+ # reference via `defaults: [{optimizer: adam}, default, _self_]`.
91
+ optimizer_params: Dict[str, Any] = field(default_factory=_default_optimizer_params)
92
+
93
+
94
+ @dataclass
95
+ class SchedulerConfig:
96
+ # 'warmup_flat' (currently the only registered scheduler) | null to
97
+ # disable the scheduler entirely.
98
+ name: Optional[str] = None
99
+ # Schema is loose here — each scheduler family takes its own kwargs.
100
+ scheduler_params: Optional[Dict[str, Any]] = None
101
+
102
+
103
+ # ---------------------------------------------------------------------------
104
+ # Model
105
+ # ---------------------------------------------------------------------------
106
+ @dataclass
107
+ class ModelConfig:
108
+ # 'regressor' | 'binary' | 'multi-class'
109
+ type: str = "multi-class"
110
+ soft_binner: bool = True
111
+ soft_binner_scale: float = 100.0
112
+ num_classes: int = 3
113
+
114
+ # Heterogeneous polymorphic list — see _parse_ref_function_entry.
115
+ ref_functions: List[Any] = field(default_factory=lambda: ["linear_cov"])
116
+
117
+ shortcut: bool = True
118
+ normalize: bool = True
119
+
120
+ # Required by the caller (tutorial YAMLs override). Left as Optional so
121
+ # the schema can still load with the library default before the caller
122
+ # supplies a value; SONN.__init__ asserts `nbest_neurons > 1`.
123
+ nbest_neurons: Optional[int] = None
124
+ max_neuron_models: Optional[int] = None
125
+
126
+ use_output_projection: bool = False
127
+ num_out_neurons: Optional[int] = None
128
+
129
+ # Per-neuron linear projection: each neuron in the ensemble gets its own
130
+ # nn.Linear(1, num_classes) trained with in_dims=0 (vs shared_proj which
131
+ # uses in_dims=None). Mutually exclusive with soft_binner.
132
+ use_neuron_proj: bool = False
133
+
134
+ # torch.clamp threshold applied between layers. Raise above the worst-
135
+ # case |xi*xj| at xavier init when feeding heavy-tailed unclipped
136
+ # features (e.g. California-housing Population).
137
+ output_clamp_value: float = 1000.0
138
+
139
+ # Apply nn.LayerNorm(d_layer, elementwise_affine=False) to each layer's
140
+ # post-clamp output, before the optional shortcut concat. Standardizes
141
+ # the (batch, nbest_neurons) feature map so the next layer's polynomial
142
+ # neurons see comparable scales regardless of how heavy-tailed the
143
+ # previous layer's polynomial happens to be. No trainable params — pure
144
+ # per-sample standardization, identical to the preprocessing LayerNorm
145
+ # that the Otto tutorial puts on the model's raw input.
146
+ use_layer_norm: bool = False
147
+
148
+
149
+ # ---------------------------------------------------------------------------
150
+ # Train
151
+ # ---------------------------------------------------------------------------
152
+ @dataclass
153
+ class TrainConfig:
154
+ seed: int = 10
155
+
156
+ # String form — coerced to CriterionType via CriterionType.get() in
157
+ # SONN.__init__. Accepted: 'validate' | 'bias' | 'validate_bias' | 'bias_retrain'.
158
+ criterion_type: str = "validate"
159
+ # Bias criterion variant for multi-class: 'l2' (L2 on logits) | 'js' (Jensen-Shannon divergence).
160
+ bias_ce_type: str = "js"
161
+ error_alpha: float = 0.5
162
+
163
+ max_layer_count: int = 999
164
+ criterion_minimum_width: int = 5
165
+ stop_train_epsilon_condition: float = 0.001
166
+
167
+ manual_best_neurons_selection: bool = False
168
+ min_best_neurons_count: int = 0
169
+ max_best_neurons_count: int = 0
170
+
171
+ # 'top' (smallest err_value wins) | 'avg'.
172
+ layer_err_criterion: str = "top"
173
+
174
+ # Algorithm used by train_layer → neuron_selection to pick survivors from
175
+ # the candidate pool:
176
+ # 'plain' — top-k by individual error (historical default).
177
+ # 'omp_mixed' — walk candidates in error order, drop ones whose residual
178
+ # (after Gram-Schmidt vs. already-selected survivors on the
179
+ # dev set) falls below `neuron_selection_orth_threshold` of
180
+ # its original norm. Trades a small NCE bump for
181
+ # decorrelated outputs feeding the next layer.
182
+ # 'omp' — classical Orthogonal Matching Pursuit: at every step pick
183
+ # the candidate whose orthogonalized residual has the
184
+ # largest norm. Ignores `err` entirely; purely decorrelation-
185
+ # driven, vectorized over the candidate axis.
186
+ neuron_selection_method: str = "plain"
187
+ # Used only by the 'omp_mixed' variant. 0.3 = "keep candidate if at least
188
+ # 30 % of its centered norm survives orthogonalization."
189
+ neuron_selection_orth_threshold: float = 0.3
190
+
191
+ eval_step_interval: int = 1000
192
+ eval_smoothing_factor: float = 0.2
193
+
194
+ save_interval: int = 1000
195
+ save_last_layer: bool = True
196
+ keep_last_n: int = 10
197
+ skip_saving_at_epoch_end: bool = True
198
+ checkpoint_dir: str = ""
199
+
200
+ early_stop_completion_percentage: int = 100
201
+ early_stop_patience: float = 1.0e-4
202
+ early_stop_tolerance_steps: int = 10
203
+
204
+ shared_proj_lr_multiplier: float = 0.1
205
+
206
+ # When True, train_layer runs every frozen layer over the full dataset
207
+ # once and caches the resulting features in CPU-backed DataLoaders, so
208
+ # the inner training step skips the SONN forward. Speeds training up but
209
+ # caches (N_samples, d_layer) floats per split, which can spike CPU RAM
210
+ # at deeper layers. False = recompute features per step (the previous
211
+ # behavior); trades wallclock for memory.
212
+ precompute_features: bool = False
213
+
214
+ # When True, log per-layer survivor-pool diagnostics (off-diagonal
215
+ # correlation stats + top singular values + effective rank) on the dev
216
+ # split after neuron_selection. Streaming O(D²) implementation — safe on
217
+ # large dev sets — but still adds one extra full forward pass over dev
218
+ # per layer, so leave off in production runs.
219
+ log_layer_diagnostics: bool = False
220
+
221
+ # Absolute val-loss threshold for permanently flagging a diverged
222
+ # neuron. `.inf` disables the guard (matches gmdhpy).
223
+ divergence_threshold: float = float("inf")
224
+
225
+ # L2 (ridge) penalty on the per-neuron polynomial `weight` during the
226
+ # OLS-style fit. The training loss for each candidate neuron becomes
227
+ # loss = data_fit + ridge_alpha * (params["weight"] ** 2).sum()
228
+ # Only the polynomial coefficients are penalized — projection weights
229
+ # (proj_weight / proj_bias / shared_proj_*) stay unregularized.
230
+ # Recommended range: 0.01–0.1 for higher-arity primitives (cubic / poly
231
+ # quad dim≥4) on small datasets where over-fit is the dominant gap.
232
+ # Evaluation and prediction partials always see ridge_alpha=0 so the
233
+ # reported dev / test metrics measure pure predictive quality, not the
234
+ # penalized training objective. Default 0.0 = ridge disabled.
235
+ ridge_alpha: float = 0.0
236
+
237
+ optimizer: OptimizerConfig = field(default_factory=OptimizerConfig)
238
+ scheduler: SchedulerConfig = field(default_factory=SchedulerConfig)
239
+ out_proj_train: OutProjTrainConfig = field(default_factory=OutProjTrainConfig)
240
+
241
+ # When True, train_layer runs an extra per-layer fine-tune pass after
242
+ # neuron_selection: jointly trains the surviving neurons' polynomial
243
+ # `weight`s together with a temporary (d_layer, num_classes) Linear head
244
+ # against the class-weighted CE on the dev split. Refines the polynomial
245
+ # coefficients so they're CE-aligned before the next layer trains on top
246
+ # of them. Hyperparameters are shared with `out_proj_train` to keep the
247
+ # config surface small. Skipped on the planned last layer
248
+ # (layer_index == max_layer_count - 1) since the subsequent
249
+ # train_out_proj pass effectively replaces it.
250
+ layer_finetune: bool = False
251
+
252
+ device: str = "cpu"
253
+ dtype: str = "float32"
254
+ batch_size: int = 1
255
+ steps: int = 1000
256
+ shuffle: bool = False
257
+ normalize: bool = True
258
+
259
+ train_loss_tol: float = 0.001
260
+ train_loss_window: int = 20
261
+
262
+ verbose: bool = True
263
+
264
+
265
+ # ---------------------------------------------------------------------------
266
+ # Top-level SONN config
267
+ # ---------------------------------------------------------------------------
268
+ @dataclass
269
+ class SONNConfig:
270
+ model: ModelConfig = field(default_factory=ModelConfig)
271
+ train: TrainConfig = field(default_factory=TrainConfig)
272
+
273
+ # Tutorial-level workflow flags. Included in the base schema so the
274
+ # `defaults: [default, _self_]` composition pattern in tutorial YAMLs
275
+ # doesn't trip strict-mode "unknown key" errors. Each tutorial reads
276
+ # only the flag(s) it cares about.
277
+ resume: bool = False
278
+ train_on_first_half: bool = False # gmdhpy-style split toggle (CA only)
279
+
280
+
281
+ # ---------------------------------------------------------------------------
282
+ # Register the schema with Hydra's ConfigStore.
283
+ #
284
+ # `name="default"` makes `- default` resolvable in any tutorial YAML's
285
+ # `defaults:` list, e.g.:
286
+ #
287
+ # defaults:
288
+ # - default # SONNConfig
289
+ # - _self_
290
+ # model:
291
+ # type: regressor
292
+ # ---------------------------------------------------------------------------
293
+ ConfigStore.instance().store(name="default", node=SONNConfig)
File without changes
@@ -0,0 +1,40 @@
1
+ from typing import Any
2
+
3
+ import numpy as np
4
+ import torch
5
+
6
+ from torch.utils.data import Dataset
7
+
8
+ # Anything that supports `arr.shape[0]` and integer indexing — numpy arrays
9
+ # and torch tensors are the two real use cases.
10
+ ArrayLike = np.ndarray | torch.Tensor
11
+
12
+
13
+ class SONNDataset(Dataset):
14
+ def __init__(
15
+ self,
16
+ x: ArrayLike,
17
+ target: ArrayLike | None,
18
+ split: int | None = None,
19
+ ) -> None:
20
+ self.x = x
21
+ self.target = target
22
+ self.split = split
23
+ assert split in [0, 1, None]
24
+
25
+ def __len__(self) -> int:
26
+ length = self.x.shape[0]
27
+ if self.split == 0:
28
+ length = (length + 1) // 2
29
+ elif self.split == 1:
30
+ length = length // 2
31
+ return length
32
+
33
+ def __getitem__(self, idx: int) -> tuple[Any, Any]:
34
+ if self.split == 0:
35
+ idx = 2 * idx
36
+ elif self.split == 1:
37
+ idx = 2 * idx + 1
38
+ x = self.x[idx]
39
+ y = self.target[idx] if self.target is not None else None
40
+ return x, y
@@ -0,0 +1,185 @@
1
+ from enum import IntEnum
2
+ from typing import Sequence
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+
8
+ class DataSetType(IntEnum):
9
+ """Tag attached to each row of a dataset by `set_split_types`."""
10
+ dsTrain = 0
11
+ dsValidate = 1
12
+
13
+
14
+ class SequenceTypeSet(IntEnum):
15
+ """How rows are assigned to train vs. validate.
16
+
17
+ sqRandom — random Bernoulli per row.
18
+ sqMode1 — every 2nd row is validate.
19
+ sqMode3_1 — every 3rd row is validate.
20
+ sqMode4_1 — every 4th row is validate.
21
+ sqMode2 — every 2nd row is train (inverse of sqMode1).
22
+ sqMode3_2 — every 3rd row is train (inverse of sqMode3_1).
23
+ sqMode4_2 — every 4th row is train (inverse of sqMode4_1).
24
+ """
25
+ sqRandom = 0
26
+ sqMode1 = 1
27
+ sqMode3_1 = 2
28
+ sqMode4_1 = 3
29
+ sqMode2 = 4
30
+ sqMode3_2 = 5
31
+ sqMode4_2 = 6
32
+
33
+ @classmethod
34
+ def is_mode1_type(cls, seq_type: "SequenceTypeSet") -> bool:
35
+ return seq_type in (cls.sqMode1, cls.sqMode3_1, cls.sqMode4_1)
36
+
37
+ @classmethod
38
+ def is_mode2_type(cls, seq_type: "SequenceTypeSet") -> bool:
39
+ return seq_type in (cls.sqMode2, cls.sqMode3_2, cls.sqMode4_2)
40
+
41
+
42
+ def train_preprocessing(
43
+ data_x: np.ndarray | pd.DataFrame,
44
+ data_y: np.ndarray | pd.DataFrame | pd.Series,
45
+ feature_names: Sequence[str] | None,
46
+ ) -> tuple[np.ndarray, np.ndarray]:
47
+ """process of train input data: transform to numpy matrix, transpose etc"""
48
+
49
+ if isinstance(data_x, pd.DataFrame):
50
+ data_x = data_x.to_numpy()
51
+
52
+ if isinstance(data_y, pd.DataFrame) or isinstance(data_y, pd.Series):
53
+ data_y = data_y.to_numpy()
54
+
55
+ if not isinstance(data_y, (np.ndarray, np.generic)):
56
+ data_y = np.asarray(data_y)
57
+ if len(data_y.shape) != 1:
58
+ if data_y.shape[0] == 1:
59
+ data_y = data_y[0, :]
60
+ elif data_y.shape[1] == 1:
61
+ data_y = data_y[:, 0]
62
+ else:
63
+ raise ValueError('data_y dimension should be 1 or (n, 1) or (1, n)')
64
+ data_len = data_y.shape[0]
65
+
66
+ if not isinstance(data_x, (np.ndarray, np.generic)):
67
+ data_x = np.asarray(data_x)
68
+
69
+ if len(data_x.shape) != 2:
70
+ raise ValueError('data_x dimension has to be 2. it has to be a 2D numpy array: number of features x '
71
+ 'number of observations')
72
+
73
+ if data_x.shape[0] != data_len:
74
+ # try to check if transpose matrix is suitable
75
+ if data_x.shape[1] == data_len:
76
+ # ok, need to transpose data_x
77
+ data_x = data_x.transpose()
78
+ else:
79
+ raise ValueError('number of examples in data_x is not equal to number of examples in data_y')
80
+
81
+ if data_x.shape[1] < 2:
82
+ raise ValueError('Error: number of features should be not less than two')
83
+
84
+ if data_x.shape[0] < 2:
85
+ raise ValueError('Error: number of samples should be not less than two')
86
+
87
+ if feature_names is not None:
88
+ feature_names_len = len(feature_names)
89
+ if feature_names_len > 0 and feature_names_len != data_x.shape[1]:
90
+ raise ValueError('Error: size of feature_names list is not equal to number of features')
91
+
92
+ return data_x, data_y
93
+
94
+
95
+ def predict_preprocessing(
96
+ data_x: np.ndarray | pd.DataFrame,
97
+ n_features: int,
98
+ ) -> tuple[np.ndarray, int]:
99
+ """process of predict input data: transform to numpy matrix, transpose etc"""
100
+
101
+ if isinstance(data_x, pd.DataFrame):
102
+ data_x = data_x.to_numpy()
103
+
104
+ if not isinstance(data_x, (np.ndarray, np.generic)):
105
+ data_x = np.asarray(data_x)
106
+
107
+ if len(data_x.shape) != 2:
108
+ raise ValueError('data_x dimension has to be 2. it has to be a 2D numpy array: number of features x '
109
+ 'number of observations')
110
+
111
+ if data_x.shape[1] != n_features:
112
+ # try to check if transpose matrix is suitable
113
+ if data_x.shape[0] == n_features:
114
+ # ok, need to transpose data_x
115
+ data_x = data_x.transpose()
116
+ else:
117
+ raise ValueError('number of features in data_x is not equal to number of features in trained model')
118
+
119
+ data_len = data_x.shape[0]
120
+ return data_x, data_len
121
+
122
+
123
+ def set_split_types(seq_type: SequenceTypeSet, data_len: int) -> np.ndarray:
124
+ """
125
+ Set seq_types array that will be used to divide data set to train and validate ones
126
+ """
127
+ # DataSetType is IntEnum, so numeric comparison works against int8 storage.
128
+ seq_types = np.empty((data_len,), dtype=np.int8)
129
+
130
+ if seq_type == SequenceTypeSet.sqRandom:
131
+ r = np.random.uniform(-1, 1, data_len)
132
+ seq_types[:] = np.where(r > 0, DataSetType.dsTrain, DataSetType.dsValidate)
133
+ return seq_types
134
+
135
+ elif seq_type == SequenceTypeSet.sqMode1:
136
+ n = 2
137
+ elif seq_type == SequenceTypeSet.sqMode3_1:
138
+ n = 3
139
+ elif seq_type == SequenceTypeSet.sqMode4_1:
140
+ n = 4
141
+ elif seq_type == SequenceTypeSet.sqMode2:
142
+ n = 2
143
+ elif seq_type == SequenceTypeSet.sqMode3_2:
144
+ n = 3
145
+ elif seq_type == SequenceTypeSet.sqMode4_2:
146
+ n = 4
147
+ else:
148
+ raise ValueError('Unknown type of data division into train and validate sequences')
149
+
150
+ if SequenceTypeSet.is_mode1_type(seq_type):
151
+ for i in range(data_len, 0, -1):
152
+ if (data_len-i) % n == 0:
153
+ seq_types[i-1] = DataSetType.dsValidate
154
+ else:
155
+ seq_types[i-1] = DataSetType.dsTrain
156
+
157
+ if SequenceTypeSet.is_mode2_type(seq_type):
158
+ for i in range(data_len, 0, -1):
159
+ if (data_len-i) % n == 0:
160
+ seq_types[i-1] = DataSetType.dsTrain
161
+ else:
162
+ seq_types[i-1] = DataSetType.dsValidate
163
+ return seq_types
164
+
165
+
166
+ def split_dataset(
167
+ data_x: np.ndarray,
168
+ data_y: np.ndarray,
169
+ seq_type: SequenceTypeSet,
170
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
171
+ """Split train and validate data sets from input data set and target
172
+ """
173
+ data_len = data_x.shape[0]
174
+
175
+ seq_types = set_split_types(seq_type, data_len)
176
+
177
+ idx_train = np.extract(DataSetType.dsTrain == seq_types, np.arange(data_len))
178
+ input_train_x = data_x[idx_train, :]
179
+ train_y = data_y[idx_train]
180
+
181
+ idx_validate = np.extract(DataSetType.dsValidate == seq_types, np.arange(data_len))
182
+ input_validate_x = data_x[idx_validate, :]
183
+ validate_y = data_y[idx_validate]
184
+
185
+ return input_train_x, train_y, input_validate_x, validate_y