spotoptim 2.1.2__tar.gz → 3.0.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 (85) hide show
  1. {spotoptim-2.1.2 → spotoptim-3.0.0}/PKG-INFO +1 -2
  2. {spotoptim-2.1.2 → spotoptim-3.0.0}/pyproject.toml +1 -3
  3. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/SpotOptim.py +61 -8
  4. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/optimizer/acquisition.py +33 -5
  5. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/kernels.py +3 -18
  6. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/kriging.py +0 -7
  7. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/transform.py +2 -1
  8. {spotoptim-2.1.2 → spotoptim-3.0.0}/README.md +0 -0
  9. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/__init__.py +0 -0
  10. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/core/__init__.py +0 -0
  11. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/core/data.py +0 -0
  12. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/core/experiment.py +0 -0
  13. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/core/protocol.py +0 -0
  14. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/core/storage.py +0 -0
  15. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/data/__init__.py +0 -0
  16. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/data/base.py +0 -0
  17. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/data/diabetes.py +0 -0
  18. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/datasets/__init__.py +0 -0
  19. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/datasets/py.typed +0 -0
  20. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/datasets/test01.csv +0 -0
  21. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/datasets/test02.csv +0 -0
  22. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/datasets/test11.csv +0 -0
  23. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/eda/__init__.py +0 -0
  24. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/eda/plots.py +0 -0
  25. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/factor_analyzer/__init__.py +0 -0
  26. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/factor_analyzer/confirmatory_factor_analyzer.py +0 -0
  27. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/factor_analyzer/factor_analyzer.py +0 -0
  28. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/factor_analyzer/factor_analyzer_rotator.py +0 -0
  29. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/factor_analyzer/factor_analyzer_utils.py +0 -0
  30. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/__init__.py +0 -0
  31. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/cd_data.csv +0 -0
  32. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/forr08a.py +0 -0
  33. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/mo.py +0 -0
  34. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/remote.py +0 -0
  35. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/so.py +0 -0
  36. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/function/torch_objective.py +0 -0
  37. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/hyperparameters/__init__.py +0 -0
  38. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/hyperparameters/parameters.py +0 -0
  39. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/hyperparameters/repr_helpers.py +0 -0
  40. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/inspection/__init__.py +0 -0
  41. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/inspection/importance.py +0 -0
  42. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/inspection/predictions.py +0 -0
  43. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/mo/__init__.py +0 -0
  44. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/mo/mo_mm.py +0 -0
  45. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/mo/pareto.py +0 -0
  46. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/nn/__init__.py +0 -0
  47. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/nn/linear_regressor.py +0 -0
  48. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/nn/mlp.py +0 -0
  49. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/optimizer/__init__.py +0 -0
  50. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/optimizer/schedule_free.py +0 -0
  51. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/optimizer/wrapper.py +0 -0
  52. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/plot/__init__.py +0 -0
  53. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/plot/contour.py +0 -0
  54. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/plot/mo.py +0 -0
  55. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/plot/visualization.py +0 -0
  56. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/py.typed +0 -0
  57. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/reporting/__init__.py +0 -0
  58. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/reporting/analysis.py +0 -0
  59. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/reporting/results.py +0 -0
  60. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/sampling/__init__.py +0 -0
  61. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/sampling/design.py +0 -0
  62. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/sampling/effects.py +0 -0
  63. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/sampling/lhs.py +0 -0
  64. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/sampling/mm.py +0 -0
  65. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/__init__.py +0 -0
  66. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/mlp_surrogate.py +0 -0
  67. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/nystroem.py +0 -0
  68. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/pipeline.py +0 -0
  69. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/surrogate/simple_kriging.py +0 -0
  70. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/tricands/__init__.py +0 -0
  71. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/tricands/tricands.py +0 -0
  72. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/__init__.py +0 -0
  73. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/boundaries.py +0 -0
  74. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/convert.py +0 -0
  75. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/dimreduction.py +0 -0
  76. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/eval.py +0 -0
  77. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/file.py +0 -0
  78. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/mapping.py +0 -0
  79. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/ocba.py +0 -0
  80. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/pca.py +0 -0
  81. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/scaler.py +0 -0
  82. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/serialization.py +0 -0
  83. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/stats.py +0 -0
  84. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/tensorboard.py +0 -0
  85. {spotoptim-2.1.2 → spotoptim-3.0.0}/src/spotoptim/utils/variables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: spotoptim
3
- Version: 2.1.2
3
+ Version: 3.0.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 = "2.1.2"
3
+ version = "3.0.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
- # best_res.x is in natural scale; validate_x0 converts to internal scale
2619
- # so the injected point can be mixed with LHS samples.
2620
- self.x0 = self.validate_x0(best_res.x)
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
- X_all_list = [res.X for res in self.restarts_results_]
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
- self.best_x_ = best_result.x
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:
@@ -6,6 +6,8 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import warnings
10
+
9
11
  from typing import TYPE_CHECKING, List, Optional, Tuple
10
12
 
11
13
  import numpy as np
@@ -43,8 +45,17 @@ def optimize_acquisition_tricands(optimizer: SpotOptimProtocol) -> np.ndarray:
43
45
  # Generate candidates
44
46
  nmax = max(100 * optimizer.n_dim, optimizer.acquisition_fun_return_size * 50)
45
47
 
46
- # Normalize X_ to [0, 1] relative to bounds
47
- X_norm = (optimizer.X_ - optimizer.lower) / (optimizer.upper - optimizer.lower)
48
+ # optimizer.lower/upper are TRANSFORMED-scale bounds, so X_ (natural scale)
49
+ # must be transformed first before normalizing against them — otherwise any
50
+ # log10/sqrt/... dim lands far outside [0, 1] and tricands() raises "X
51
+ # outside of lower/upper bounds" (mirrors try_optimizer_candidates, which
52
+ # already calls optimizer.transform_X before comparing against bounds).
53
+ X_internal = optimizer.transform_X(optimizer.X_)
54
+ span = optimizer.upper - optimizer.lower
55
+
56
+ # Normalize to [0, 1] relative to the transformed bounds, clipping away any
57
+ # residual floating-point overshoot at the box edges.
58
+ X_norm = np.clip((X_internal - optimizer.lower) / span, 0.0, 1.0)
48
59
 
49
60
  # Generate candidates in [0, 1] space
50
61
  X_cands_norm = tricands(
@@ -55,8 +66,10 @@ def optimize_acquisition_tricands(optimizer: SpotOptimProtocol) -> np.ndarray:
55
66
  fringe=optimizer.tricands_fringe,
56
67
  )
57
68
 
58
- # Denormalize candidates back to original space
59
- X_cands = X_cands_norm * (optimizer.upper - optimizer.lower) + optimizer.lower
69
+ # Denormalize candidates back to transformed space, clipping into bounds.
70
+ X_cands = np.clip(
71
+ X_cands_norm * span + optimizer.lower, optimizer.lower, optimizer.upper
72
+ )
60
73
 
61
74
  # Evaluate acquisition function on all candidates
62
75
  acq_values = optimizer._acquisition_function(X_cands.T)
@@ -374,7 +387,22 @@ def try_optimizer_candidates(
374
387
  if current_batch is None:
375
388
  current_batch = []
376
389
 
377
- x_next_candidates = optimizer.optimize_acquisition_func()
390
+ try:
391
+ x_next_candidates = optimizer.optimize_acquisition_func()
392
+ except Exception as err:
393
+ # Any acquisition-optimizer failure (e.g. an out-of-bounds tricands
394
+ # candidate on a mixed-scale problem, or a restart-injected factor
395
+ # string reaching a numeric optimizer) degrades to an empty candidate
396
+ # set here, so suggest_next_infill_point() falls through to the
397
+ # existing acquisition_failure_strategy fallback instead of aborting
398
+ # optimize() entirely.
399
+ warnings.warn(
400
+ f"Acquisition optimizer failed ({err}); falling back to the "
401
+ f"{optimizer.acquisition_failure_strategy!r} infill strategy.",
402
+ RuntimeWarning,
403
+ stacklevel=2,
404
+ )
405
+ return []
378
406
 
379
407
  # Ensure iterable of 1D arrays
380
408
  if x_next_candidates.ndim == 1:
@@ -75,7 +75,6 @@ class SpotOptimKernel(Kernel):
75
75
  Note: In standard Kriging usage, this corresponds to `10^theta_log`.
76
76
  This kernel expects the LINEAR scale theta values (weights), not log.
77
77
  var_type (list of str): List of variable types, e.g. ['float', 'int', 'factor'].
78
- p_val (float, optional): Power parameter for ordered distance. Defaults to 2.0.
79
78
  metric_factorial (str, optional): Metric for factor distance (passed to cdist/pdist).
80
79
  Defaults to 'hamming'. Hamming is a true nominal (order-agnostic) metric;
81
80
  canberra distance on integer level indices is order-dependent and singles out
@@ -86,12 +85,10 @@ class SpotOptimKernel(Kernel):
86
85
  self,
87
86
  theta,
88
87
  var_type,
89
- p_val=2.0,
90
88
  metric_factorial="hamming",
91
89
  ):
92
90
  self.theta = np.asanyarray(theta)
93
91
  self.var_type = var_type
94
- self.p_val = p_val
95
92
  self.metric_factorial = metric_factorial
96
93
 
97
94
  # Precompute masks
@@ -166,21 +163,9 @@ class SpotOptimKernel(Kernel):
166
163
  )
167
164
  D += D_factor
168
165
 
169
- # Final exponential (Gaussian correlation)
170
- # Note: Kriging's Psi = exp(-D)
171
- if self.p_val != 2.0:
172
- # If p != 2, the 'sqeuclidean' above was mathematically sum w * (diff^2).
173
- # If we strictly want sum w * |diff|^p, we can't use 'sqeuclidean' directly if p != 2.
174
- # But standard implementation often assumes p=2 for efficiency.
175
- # Kriging code shows:
176
- # pdist(..., metric="sqeuclidean", ...)
177
- # Psi = np.exp(-Psi)
178
- # This implies p=2 is hardcoded effectively in 'sqeuclidean' metric usage in Kriging code provided earlier.
179
- # The parameter p_val seems unused in the `build_correlation_matrix` snippet I read earlier
180
- # (which used `sqeuclidean`).
181
- # I will stick to what the code did: sqeuclidean -> exp(-D).
182
- pass
183
-
166
+ # Final exponential (Gaussian correlation). The Gaussian correlation
167
+ # power is fixed at 2: D_ordered is computed via the 'sqeuclidean'
168
+ # metric above (sum w * diff^2), so Psi = exp(-D) is exact.
184
169
  return np.exp(-D)
185
170
 
186
171
  def diag(self, X):
@@ -71,8 +71,6 @@ class Kriging(BaseEstimator, RegressorMixin):
71
71
  min_theta (float, optional): Minimum log10(theta) bound. Defaults to -3.0.
72
72
  max_theta (float, optional): Maximum log10(theta) bound. Defaults to 2.0.
73
73
  theta_init_zero (bool, optional): Initialize theta to zero. Defaults to False.
74
- p_val (float, optional): Power parameter for correlation (fixed at 2.0 for Gaussian).
75
- Defaults to 2.0.
76
74
  n_p (int, optional): Number of p parameters (currently not optimized). Defaults to 1.
77
75
  optim_p (bool, optional): Optimize p parameters (currently not supported). Defaults to False.
78
76
  min_Lambda (float, optional): Minimum log10(Lambda) bound. Defaults to -9.0.
@@ -160,7 +158,6 @@ class Kriging(BaseEstimator, RegressorMixin):
160
158
  min_theta: float = -3.0,
161
159
  max_theta: float = 2.0,
162
160
  theta_init_zero: bool = False,
163
- p_val: float = 2.0,
164
161
  n_p: int = 1,
165
162
  optim_p: bool = False,
166
163
  min_Lambda: float = -9.0,
@@ -188,7 +185,6 @@ class Kriging(BaseEstimator, RegressorMixin):
188
185
  self.max_Lambda = max_Lambda
189
186
  self.n_theta = n_theta
190
187
  self.isotropic = isotropic
191
- self.p_val = p_val
192
188
  self.n_p = n_p
193
189
  self.optim_p = optim_p
194
190
  self.theta_init_zero = theta_init_zero
@@ -393,7 +389,6 @@ class Kriging(BaseEstimator, RegressorMixin):
393
389
  kernel = SpotOptimKernel(
394
390
  theta=theta10,
395
391
  var_type=self.var_type,
396
- p_val=self.p_val,
397
392
  metric_factorial=self.metric_factorial,
398
393
  )
399
394
 
@@ -506,7 +501,6 @@ class Kriging(BaseEstimator, RegressorMixin):
506
501
  kernel = SpotOptimKernel(
507
502
  theta=theta10,
508
503
  var_type=self.var_type,
509
- p_val=self.p_val,
510
504
  metric_factorial=self.metric_factorial,
511
505
  )
512
506
 
@@ -614,7 +608,6 @@ class Kriging(BaseEstimator, RegressorMixin):
614
608
  "min_theta": self.min_theta,
615
609
  "max_theta": self.max_theta,
616
610
  "theta_init_zero": self.theta_init_zero,
617
- "p_val": self.p_val,
618
611
  "n_p": self.n_p,
619
612
  "optim_p": self.optim_p,
620
613
  "min_Lambda": self.min_Lambda,
@@ -114,7 +114,8 @@ def inverse_transform_value(x: float, trans: Optional[str]) -> float:
114
114
  elif trans == "square":
115
115
  return np.sqrt(x)
116
116
  elif trans == "cube":
117
- return np.power(x, 1.0 / 3.0)
117
+ # np.cbrt handles negative inputs; np.power(x, 1/3) returns NaN there.
118
+ return np.cbrt(x)
118
119
  elif trans == "inv" or trans == "reciprocal":
119
120
  return 1.0 / x
120
121
 
File without changes