spotoptim 3.2.0__tar.gz → 3.3.1__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 (91) hide show
  1. {spotoptim-3.2.0 → spotoptim-3.3.1}/PKG-INFO +1 -1
  2. {spotoptim-3.2.0 → spotoptim-3.3.1}/pyproject.toml +1 -1
  3. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/torch_objective.py +20 -0
  4. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/linear_regressor.py +13 -2
  5. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/kriging.py +32 -6
  6. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/mapping.py +5 -1
  7. {spotoptim-3.2.0 → spotoptim-3.3.1}/README.md +0 -0
  8. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/SpotOptim.py +0 -0
  9. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/__init__.py +0 -0
  10. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/core/__init__.py +0 -0
  11. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/core/data.py +0 -0
  12. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/core/experiment.py +0 -0
  13. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/core/protocol.py +0 -0
  14. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/core/storage.py +0 -0
  15. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/data/__init__.py +0 -0
  16. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/data/base.py +0 -0
  17. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/data/diabetes.py +0 -0
  18. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/data/manydataset.py +0 -0
  19. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/datasets/__init__.py +0 -0
  20. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/datasets/py.typed +0 -0
  21. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/datasets/test01.csv +0 -0
  22. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/datasets/test02.csv +0 -0
  23. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/datasets/test11.csv +0 -0
  24. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/eda/__init__.py +0 -0
  25. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/eda/plots.py +0 -0
  26. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/factor_analyzer/__init__.py +0 -0
  27. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/factor_analyzer/confirmatory_factor_analyzer.py +0 -0
  28. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/factor_analyzer/factor_analyzer.py +0 -0
  29. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/factor_analyzer/factor_analyzer_rotator.py +0 -0
  30. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/factor_analyzer/factor_analyzer_utils.py +0 -0
  31. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/__init__.py +0 -0
  32. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/cd_data.csv +0 -0
  33. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/forr08a.py +0 -0
  34. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/mo.py +0 -0
  35. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/remote.py +0 -0
  36. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/sequence_cv_objective.py +0 -0
  37. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/function/so.py +0 -0
  38. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/hyperparameters/__init__.py +0 -0
  39. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/hyperparameters/parameters.py +0 -0
  40. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/hyperparameters/repr_helpers.py +0 -0
  41. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/inspection/__init__.py +0 -0
  42. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/inspection/importance.py +0 -0
  43. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/inspection/predictions.py +0 -0
  44. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/mo/__init__.py +0 -0
  45. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/mo/mo_mm.py +0 -0
  46. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/mo/pareto.py +0 -0
  47. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/__init__.py +0 -0
  48. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/many_to_many_rnn.py +0 -0
  49. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/mlp.py +0 -0
  50. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/optimizer.py +0 -0
  51. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/nn/training.py +0 -0
  52. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/optimizer/__init__.py +0 -0
  53. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/optimizer/acquisition.py +0 -0
  54. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/optimizer/schedule_free.py +0 -0
  55. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/optimizer/wrapper.py +0 -0
  56. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/plot/__init__.py +0 -0
  57. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/plot/contour.py +0 -0
  58. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/plot/mo.py +0 -0
  59. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/plot/visualization.py +0 -0
  60. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/py.typed +0 -0
  61. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/reporting/__init__.py +0 -0
  62. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/reporting/analysis.py +0 -0
  63. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/reporting/results.py +0 -0
  64. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/sampling/__init__.py +0 -0
  65. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/sampling/design.py +0 -0
  66. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/sampling/effects.py +0 -0
  67. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/sampling/lhs.py +0 -0
  68. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/sampling/mm.py +0 -0
  69. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/__init__.py +0 -0
  70. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/kernels.py +0 -0
  71. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/mlp_surrogate.py +0 -0
  72. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/nystroem.py +0 -0
  73. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/pipeline.py +0 -0
  74. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/surrogate/simple_kriging.py +0 -0
  75. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/tricands/__init__.py +0 -0
  76. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/tricands/tricands.py +0 -0
  77. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/__init__.py +0 -0
  78. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/boundaries.py +0 -0
  79. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/convert.py +0 -0
  80. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/dimreduction.py +0 -0
  81. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/eval.py +0 -0
  82. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/file.py +0 -0
  83. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/ocba.py +0 -0
  84. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/pca.py +0 -0
  85. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/scaler.py +0 -0
  86. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/seed.py +0 -0
  87. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/serialization.py +0 -0
  88. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/stats.py +0 -0
  89. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/tensorboard.py +0 -0
  90. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/transform.py +0 -0
  91. {spotoptim-3.2.0 → spotoptim-3.3.1}/src/spotoptim/utils/variables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: spotoptim
3
- Version: 3.2.0
3
+ Version: 3.3.1
4
4
  Summary: Sequential Parameter Optimization Toolbox
5
5
  Author: bartzbeielstein
6
6
  Author-email: bartzbeielstein <32470350+bartzbeielstein@users.noreply.github.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spotoptim"
3
- version = "3.2.0"
3
+ version = "3.3.1"
4
4
  description = "Sequential Parameter Optimization Toolbox"
5
5
  readme = "README.md"
6
6
  license = { text = "AGPL-3.0-or-later" }
@@ -369,6 +369,10 @@ class TorchObjective:
369
369
 
370
370
  Executes the training loop for the specified number of epochs. Handles optimizer
371
371
  creation, loss calculation, backward pass, and validation evaluation.
372
+ Schedule-free optimizers (those exposing ``train()``/``eval()``, e.g.
373
+ ``AdamWScheduleFree``) are switched to training mode before each epoch and to
374
+ evaluation mode before validation, so validation metrics are computed on the
375
+ averaged parameters; the optimizer is left in evaluation mode on return.
372
376
 
373
377
  Args:
374
378
  model (nn.Module): The PyTorch model to train.
@@ -438,8 +442,17 @@ class TorchObjective:
438
442
  # For now, we train for exact 'epochs'
439
443
  trained_epochs = epochs
440
444
 
445
+ # Schedule-free optimizers (e.g. AdamWScheduleFree) keep the training
446
+ # iterate and the averaged parameters separate; they require
447
+ # optimizer.train()/optimizer.eval() around the respective phases so
448
+ # that validation sees the averaged weights.
449
+ optimizer_train = getattr(optimizer, "train", None)
450
+ optimizer_eval = getattr(optimizer, "eval", None)
451
+
441
452
  for epoch in range(epochs):
442
453
  model.train()
454
+ if callable(optimizer_train):
455
+ optimizer_train()
443
456
  train_loss = 0.0
444
457
  steps = 0
445
458
  for X_batch, y_batch in train_loader:
@@ -459,6 +472,8 @@ class TorchObjective:
459
472
  # Validation
460
473
  if val_loader:
461
474
  model.eval()
475
+ if callable(optimizer_eval):
476
+ optimizer_eval()
462
477
  val_loss = 0.0
463
478
  val_steps = 0
464
479
  with torch.no_grad():
@@ -481,6 +496,11 @@ class TorchObjective:
481
496
  if final_train_loss < min_val_loss:
482
497
  min_val_loss = final_train_loss
483
498
 
499
+ # Leave schedule-free optimizers in eval mode so the model holds the
500
+ # averaged parameters for any downstream use.
501
+ if callable(optimizer_eval):
502
+ optimizer_eval()
503
+
484
504
  # Collect metrics
485
505
  metrics_out = {}
486
506
 
@@ -296,8 +296,9 @@ class LinearRegressor(nn.Module):
296
296
  Args:
297
297
  optimizer_name (str, optional): Name of the optimizer from torch.optim.
298
298
  Common options: "Adam", "AdamW", "Adamax", "SGD", "RMSprop", "Adagrad",
299
- "Adadelta", "NAdam", "RAdam", "ASGD", "LBFGS", "Rprop".
300
- Defaults to "Adam".
299
+ "Adadelta", "NAdam", "RAdam", "ASGD", "LBFGS", "Rprop". The vendored
300
+ schedule-free "AdamWScheduleFree" (spotoptim.optimizer) is also
301
+ supported. Defaults to "Adam".
301
302
  lr (float, optional): Unified learning rate multiplier. If None, uses self.lr.
302
303
  This value is automatically scaled to optimizer-specific learning rates.
303
304
  A value of 1.0 corresponds to the optimizer's default learning rate.
@@ -467,6 +468,16 @@ class LinearRegressor(nn.Module):
467
468
  optimizer_class = getattr(optim, optimizer_name)
468
469
  # Create optimizer with model parameters, mapped learning rate, and additional kwargs
469
470
  return optimizer_class(self.parameters(), lr=lr_actual, **kwargs)
471
+
472
+ # Check for custom schedule-free optimizer
473
+ elif optimizer_name == "AdamWScheduleFree":
474
+ try:
475
+ from spotoptim.optimizer import AdamWScheduleFree
476
+
477
+ return AdamWScheduleFree(self.parameters(), lr=lr_actual, **kwargs)
478
+ except ImportError as e:
479
+ raise ImportError(f"Could not import AdamWScheduleFree: {e}")
480
+
470
481
  else:
471
482
  raise ValueError(
472
483
  f"Optimizer '{optimizer_name}' not found in torch.optim. "
@@ -73,8 +73,17 @@ class Kriging(BaseEstimator, RegressorMixin):
73
73
  theta_init_zero (bool, optional): Initialize theta to zero. Defaults to False.
74
74
  n_p (int, optional): Number of p parameters (currently not optimized). Defaults to 1.
75
75
  optim_p (bool, optional): Optimize p parameters (currently not supported). Defaults to False.
76
- min_Lambda (float, optional): Minimum log10(Lambda) bound. Defaults to -9.0.
76
+ min_Lambda (float, optional): Minimum log10(Lambda) bound; the admissible
77
+ nugget range is [10**min_Lambda, 10**max_Lambda] regardless of
78
+ ``lambda_scale``. Defaults to -9.0.
77
79
  max_Lambda (float, optional): Maximum log10(Lambda) bound. Defaults to 0.0.
80
+ lambda_scale (str, optional): Search parametrization of the nugget Lambda
81
+ within [10**min_Lambda, 10**max_Lambda]. Options:
82
+ - "log10": the optimizer searches the exponent log10(Lambda)
83
+ uniformly, emphasizing small nuggets (near-interpolation).
84
+ - "linear": the optimizer searches Lambda itself uniformly,
85
+ emphasizing larger nuggets (more smoothing on noisy data).
86
+ Defaults to "log10".
78
87
  metric_factorial (str, optional): Distance metric for factor variables.
79
88
  Defaults to ``"hamming"``. Hamming is a true nominal (order-agnostic) metric;
80
89
  canberra distance on integer level indices is order-dependent and singles out
@@ -86,7 +95,9 @@ class Kriging(BaseEstimator, RegressorMixin):
86
95
  X_ (ndarray): Training data, shape (n_samples, n_features).
87
96
  y_ (ndarray): Training targets, shape (n_samples,).
88
97
  theta_ (ndarray): Optimized log10(theta) parameters.
89
- Lambda_ (float or None): Optimized log10(Lambda) for regression methods.
98
+ Lambda_ (float or None): Optimized Lambda parameter in the search scale
99
+ (the log10 exponent for ``lambda_scale="log10"``, the linear value
100
+ for ``lambda_scale="linear"``) for regression methods.
90
101
  mu_ (float): Mean of Kriging predictor.
91
102
  sigma2_ (float): Variance of Kriging predictor.
92
103
  U_ (ndarray): Cholesky factor of correlation matrix.
@@ -162,6 +173,7 @@ class Kriging(BaseEstimator, RegressorMixin):
162
173
  optim_p: bool = False,
163
174
  min_Lambda: float = -9.0,
164
175
  max_Lambda: float = 0.0,
176
+ lambda_scale: str = "log10",
165
177
  metric_factorial: str = "hamming",
166
178
  isotropic: bool = False,
167
179
  theta: Optional[np.ndarray] = None,
@@ -183,6 +195,9 @@ class Kriging(BaseEstimator, RegressorMixin):
183
195
  self.max_theta = max_theta
184
196
  self.min_Lambda = min_Lambda
185
197
  self.max_Lambda = max_Lambda
198
+ if lambda_scale not in ["log10", "linear"]:
199
+ raise ValueError("lambda_scale must be 'log10' or 'linear'")
200
+ self.lambda_scale = lambda_scale
186
201
  self.n_theta = n_theta
187
202
  self.isotropic = isotropic
188
203
  self.n_p = n_p
@@ -216,6 +231,18 @@ class Kriging(BaseEstimator, RegressorMixin):
216
231
  """Get square root of machine epsilon."""
217
232
  return np.sqrt(np.finfo(float).eps)
218
233
 
234
+ def _lambda_bounds(self) -> Tuple[float, float]:
235
+ """Search bounds of the Lambda parameter in the configured scale."""
236
+ if self.lambda_scale == "linear":
237
+ return (10.0**self.min_Lambda, 10.0**self.max_Lambda)
238
+ return (self.min_Lambda, self.max_Lambda)
239
+
240
+ def _lambda_value(self, param: float) -> float:
241
+ """Nugget value Lambda from its search-scale parameter."""
242
+ if self.lambda_scale == "linear":
243
+ return param
244
+ return 10.0**param
245
+
219
246
  def _set_variable_types(self) -> None:
220
247
  """Set variable type masks for different variable types.
221
248
 
@@ -298,7 +325,7 @@ class Kriging(BaseEstimator, RegressorMixin):
298
325
  bounds = [(self.min_theta, self.max_theta)] * self.n_theta
299
326
 
300
327
  if self.method in ["regression", "reinterpolation"]:
301
- bounds += [(self.min_Lambda, self.max_Lambda)]
328
+ bounds += [self._lambda_bounds()]
302
329
 
303
330
  if self.optim_p:
304
331
  bounds += [(1.0, 2.0)] * self.n_p
@@ -431,8 +458,7 @@ class Kriging(BaseEstimator, RegressorMixin):
431
458
  self.theta_ = params[: self.n_theta]
432
459
 
433
460
  if self.method in ["regression", "reinterpolation"]:
434
- lambda_log = params[self.n_theta : self.n_theta + 1][0]
435
- lambda_ = 10.0**lambda_log
461
+ lambda_ = self._lambda_value(params[self.n_theta : self.n_theta + 1][0])
436
462
  else:
437
463
  lambda_ = self.noise
438
464
 
@@ -536,7 +562,7 @@ class Kriging(BaseEstimator, RegressorMixin):
536
562
  >>> y_pred, y_std = k.predict_single(x_new)
537
563
  """
538
564
  if self.method in ["regression", "reinterpolation"]:
539
- lambda_ = 10.0**self.Lambda_
565
+ lambda_ = self._lambda_value(self.Lambda_)
540
566
  else:
541
567
  lambda_ = self.noise
542
568
 
@@ -27,6 +27,8 @@ OPTIMIZER_DEFAULT_LR: Dict[str, float] = {
27
27
  "RMSprop": 0.01,
28
28
  "Rprop": 0.01,
29
29
  "SGD": 0.01,
30
+ # spotoptim's vendored schedule-free optimizer (spotoptim.optimizer)
31
+ "AdamWScheduleFree": 0.0025,
30
32
  }
31
33
 
32
34
 
@@ -49,7 +51,9 @@ def map_lr(
49
51
  rate, values > 1.0 increase it. Typical range: [0.001, 100.0].
50
52
  optimizer_name (str): Name of the PyTorch optimizer. Must be one of:
51
53
  "Adadelta", "Adagrad", "Adam", "AdamW", "SparseAdam", "Adamax",
52
- "ASGD", "LBFGS", "NAdam", "RAdam", "RMSprop", "Rprop", "SGD".
54
+ "ASGD", "LBFGS", "NAdam", "RAdam", "RMSprop", "Rprop", "SGD",
55
+ "AdamWScheduleFree" (spotoptim's vendored schedule-free optimizer,
56
+ default lr 0.0025).
53
57
  use_default_scale (bool, optional): Whether to scale by the optimizer's default
54
58
  learning rate. If True (default), lr_unified is multiplied by the default
55
59
  lr. If False, returns lr_unified directly. Defaults to True.
File without changes