Skip to content

Commit 0917f01

Browse files
0.32.0
New Kriging isotropic/anisotropic
1 parent 3b94632 commit 0917f01

23 files changed

Lines changed: 844 additions & 1029 deletions

notebooks/00_spotPython_tests.ipynb

Lines changed: 774 additions & 68 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "spotpython"
10-
version = "0.31.16"
10+
version = "0.32.0"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotpython/hyperparameters/values.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,8 +1470,7 @@ def get_tuned_hyperparameters(spot_tuner, fun_control=None) -> dict:
14701470
] )
14711471
from spotpython.utils.init import design_control_init, surrogate_control_init
14721472
design_control = design_control_init(init_size=INIT_SIZE)
1473-
surrogate_control = surrogate_control_init(noise=True,
1474-
n_theta=2)
1473+
surrogate_control = surrogate_control_init(noise=True)
14751474
from spotpython.fun.hyperlight import HyperLight
14761475
fun = HyperLight(log_level=50).fun
14771476
from spotpython.spot import spot

src/spotpython/spot/spot.py

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ def objective_function(X, fun_control=None):
171171
model_fun_evals=10000,
172172
min_theta=-3,
173173
max_theta=3,
174-
n_theta=2,
175174
theta_init_zero=False,
176175
n_p=1,
177176
optim_p=False,
@@ -422,17 +421,6 @@ def _surrogate_control_setup(self) -> None:
422421
if self.surrogate_control["model_optimizer"] is None or self.optimizer is not None:
423422
self.surrogate_control.update({"model_optimizer": self.optimizer})
424423

425-
# if self.surrogate_control["n_theta"] is a string and == isotropic, use 1 theta value:
426-
if isinstance(self.surrogate_control["n_theta"], str):
427-
if self.surrogate_control["n_theta"] == "anisotropic":
428-
self.surrogate_control.update({"n_theta": self.k})
429-
else:
430-
# case "isotropic":
431-
self.surrogate_control.update({"n_theta": 1})
432-
if isinstance(self.surrogate_control["n_theta"], int):
433-
if self.surrogate_control["n_theta"] > 1:
434-
self.surrogate_control.update({"n_theta": self.k})
435-
436424
def surrogate_setup(self, surrogate) -> None:
437425
"""Set up the surrogate model for the optimization process.
438426
This method initializes the surrogate model. If no surrogate model is provided,
@@ -455,7 +443,6 @@ def surrogate_setup(self, surrogate) -> None:
455443
- model_optimizer: Optimizer for model parameter tuning
456444
- model_fun_evals: Number of function evaluations for model optimization
457445
- min/max_theta: Bounds for theta parameters
458-
- n_theta: Number of theta parameters
459446
- theta_init_zero: Whether to initialize theta at zero
460447
- p_val: Power parameter p value
461448
- n_p: Number of p parameters
@@ -479,15 +466,14 @@ def surrogate_setup(self, surrogate) -> None:
479466
... )
480467
>>> surrogate_control = surrogate_control_init(
481468
... method="interpolation",
482-
... n_theta="anisotropic"
483469
... )
484470
>>> S = spot.Spot(
485471
... fun=Analytical().fun_sphere,
486472
... fun_control=fun_control,
487473
... surrogate_control=surrogate_control
488474
... )
489-
>>> print(S.surrogate.n_theta)
490-
2
475+
>>> print(S.surrogate.isotropic)
476+
False
491477
492478
>>> # Setup with custom surrogate
493479
>>> from sklearn.gaussian_process import GaussianProcessRegressor
@@ -514,7 +500,7 @@ def surrogate_setup(self, surrogate) -> None:
514500
model_fun_evals=self.surrogate_control["model_fun_evals"],
515501
min_theta=self.surrogate_control["min_theta"],
516502
max_theta=self.surrogate_control["max_theta"],
517-
n_theta=self.surrogate_control["n_theta"],
503+
isotropic=self.surrogate_control["isotropic"],
518504
theta_init_zero=self.surrogate_control["theta_init_zero"],
519505
p_val=self.surrogate_control["p_val"],
520506
n_p=self.surrogate_control["n_p"],
@@ -2072,7 +2058,7 @@ def plot_progress(
20722058
tolerance_x = np.sqrt(np.spacing(1))
20732059
)
20742060
design_control=design_control_init(init_size=ni)
2075-
surrogate_control=surrogate_control_init(n_theta=3)
2061+
surrogate_control=surrogate_control_init()
20762062
S = spot.Spot(fun=fun,
20772063
fun_control=fun_control
20782064
design_control=design_control,
@@ -2303,8 +2289,7 @@ def get_tuned_hyperparameters(self, fun_control=None) -> dict:
23032289
] )
23042290
from spotpython.utils.init import design_control_init, surrogate_control_init
23052291
design_control = design_control_init(init_size=INIT_SIZE)
2306-
surrogate_control = surrogate_control_init(method="regression",
2307-
n_theta=2)
2292+
surrogate_control = surrogate_control_init(method="regression")
23082293
from spotpython.fun.hyperlight import HyperLight
23092294
fun = HyperLight(log_level=50).fun
23102295
from spotpython.spot import spot
@@ -2662,7 +2647,7 @@ def plot_important_hyperparameter_contour(
26622647
tolerance_x = np.sqrt(np.spacing(1))
26632648
)
26642649
design_control=design_control_init(init_size=ni)
2665-
surrogate_control=surrogate_control_init(n_theta=3)
2650+
surrogate_control=surrogate_control_init)
26662651
S = spot.Spot(fun=fun,
26672652
fun_control=fun_control,
26682653
design_control=design_control,
@@ -2733,7 +2718,6 @@ def get_importance(self) -> list:
27332718
>>> design_control = design_control_init(init_size=10)
27342719
>>> # Setup surrogate model with multiple theta values
27352720
>>> surrogate_control = surrogate_control_init(
2736-
... n_theta="anisotropic",
27372721
... method="interpolation"
27382722
... )
27392723
>>> # Initialize and run spot
@@ -2753,7 +2737,7 @@ def get_importance(self) -> list:
27532737
x3: 76.15%
27542738
>>> # Try with single theta (should return zeros)
27552739
>>> surrogate_control = surrogate_control_init(
2756-
... n_theta=1,
2740+
... isotropie=True,
27572741
... method="interpolation"
27582742
... )
27592743
>>> S2 = spot.Spot(
@@ -2772,16 +2756,16 @@ def get_importance(self) -> list:
27722756
print("No surrogate model available.")
27732757
return []
27742758

2775-
# Check if surrogate has n_theta attribute
2776-
if not hasattr(self.surrogate, "n_theta"):
2777-
print("Surrogate model does not have n_theta attribute.")
2778-
return []
2779-
27802759
# Check if surrogate has theta attribute for multi-theta models
2781-
if self.surrogate.n_theta > 1 and not hasattr(self.surrogate, "theta"):
2760+
if not hasattr(self.surrogate, "theta"):
27822761
print("Surrogate model does not have theta attribute.")
27832762
return []
27842763

2764+
# Check if surrogate is not isotropic
2765+
if self.surrogate.isotropic:
2766+
print("Surrogate model is isotropic.")
2767+
return []
2768+
27852769
# Check if all required attributes exist for importance calculation
27862770
if not hasattr(self, "all_var_name"):
27872771
print("Variable names (all_var_name) not available.")
@@ -2838,7 +2822,7 @@ def print_importance(self, threshold=0.1, print_screen=True) -> list:
28382822
print(var_name[i] + ": ", imp[i])
28392823
output.append([var_name[i], imp[i]])
28402824
else:
2841-
print("Importance requires more than one theta values (n_theta>1).")
2825+
print("Importance requires more than one theta value (n_theta>1).")
28422826
return output
28432827

28442828
def plot_importance(self, threshold=0.1, filename=None, dpi=300, show=True, tkagg=False, figsize=(9, 6)) -> None:
@@ -2917,7 +2901,7 @@ def parallel_plot(self, show=False) -> go.Figure:
29172901
tolerance_x = np.sqrt(np.spacing(1))
29182902
)
29192903
design_control=design_control_init(init_size=ni)
2920-
surrogate_control=surrogate_control_init(n_theta=3)
2904+
surrogate_control=surrogate_control_init()
29212905
S = spot.Spot(fun=fun,
29222906
fun_control=fun_control,
29232907
design_control=design_control,

src/spotpython/surrogate/kriging.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ class Kriging(BaseEstimator, RegressorMixin):
2222
U_ (np.ndarray): The Cholesky factor of the correlation matrix after fit().
2323
X_ (np.ndarray): The training input data (n x d).
2424
y_ (np.ndarray): The training target values (n,).
25+
negLnLike (float): The negative log-likelihood of the model.
26+
Psi_ (np.ndarray): The correlation matrix after fit().
27+
method (str): The fitting method used, can be "interpolation", "regression", or "reinterpolation".
28+
isotropic (bool): Whether the model is isotropic or not.
29+
30+
Methods:
31+
__init__: Initializes the Kriging model with hyperparameters.
32+
_get_eps: Returns the square root of machine epsilon.
33+
_set_variable_types: Sets variable types for the model.
34+
get_model_params: Returns additional model parameters not included in get_params().
35+
_update_log: Updates the log with current model parameters.
36+
fit: Fits the Kriging model to training data X and y.
37+
predict: Predicts the Kriging response at a set of points X.
38+
build_Psi: Constructs a new correlation matrix Psi.
39+
likelihood: Computes the negative concentrated log-likelihood and correlation matrix.
40+
build_psi_vec: Builds the psi vector for predictive methods.
41+
_pred: Computes a single-point Kriging prediction.
2542
"""
2643

2744
def __init__(
@@ -37,7 +54,6 @@ def __init__(
3754
model_fun_evals: Optional[int] = None,
3855
min_theta: float = -3.0,
3956
max_theta: float = 2.0,
40-
n_theta: int = None,
4157
theta_init_zero: bool = False,
4258
p_val: float = 2.0,
4359
n_p: int = 1,
@@ -50,6 +66,7 @@ def __init__(
5066
spot_writer=None,
5167
counter=None,
5268
metric_factorial="canberra",
69+
isotropic: bool = False,
5370
**kwargs,
5471
):
5572
"""
@@ -66,6 +83,9 @@ def __init__(
6683
not positive-definite. Defaults to 1e4.
6784
method (str, optional):
6885
The type how the model uis fitted. Can be "interpolation", "regression", or "reinterpolation". Defaults to "regression".
86+
isotropic (bool, optional):
87+
If True, the model is isotropic, meaning all variables are treated equally (only one theta value is used).
88+
If False, the model can handle different theta values, one for each dimension. Defaults to False.
6989
"""
7090
if eps is None:
7191
self.eps = self._get_eps()
@@ -90,7 +110,8 @@ def __init__(
90110
self.max_Lambda = max_Lambda
91111
self.min_p = min_p
92112
self.max_p = max_p
93-
self.n_theta = n_theta
113+
self.n_theta = None # Will be set in fit()
114+
self.isotropic = isotropic
94115
self.p_val = p_val
95116
self.n_p = n_p
96117
self.optim_p = optim_p
@@ -256,8 +277,13 @@ def fit(self, X: np.ndarray, y: np.ndarray, bounds: Optional[List[Tuple[float, f
256277
self.y_ = y
257278
self.n, self.k = self.X_.shape
258279
self._set_variable_types()
259-
if self.n_theta is None:
280+
if self.isotropic:
281+
# If isotropic, set n_theta to 1
282+
self.n_theta = 1
283+
print(f"Isotropic model: n_theta set to {self.n_theta}")
284+
else:
260285
self.n_theta = self.k
286+
print(f"Anisotropic model: n_theta set to {self.n_theta}")
261287
# Calculate and store min and max of X
262288
self.min_X = np.min(self.X_, axis=0)
263289
self.max_X = np.max(self.X_, axis=0)

src/spotpython/utils/init.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ def surrogate_control_init(
739739
model_fun_evals=10000,
740740
min_theta=-3.0, # log10
741741
max_theta=2.0, # log10
742-
n_theta="anisotropic",
742+
isotropic=False,
743743
p_val=2.0,
744744
n_p=1,
745745
optim_p=False,
@@ -764,14 +764,15 @@ def surrogate_control_init(
764764
Default is -3.
765765
max_theta (float): The maximum value of theta. Note that the base10-logarithm is used.
766766
Default is 3.
767+
isotropic (bool):
768+
Whether to use isotropic or anisotropic theta values. If True, the theta values are
769+
isotropic, i.e., the same value is used for all dimensions. If False,
770+
the theta values are anisotropic, i.e., different values are used for each dimension.
771+
Default is False.
767772
method (str):
768773
The method to be used for the surrogate model. Default is "regression".
769774
Can be one of ["regression", "interpolation", "reinterpolation"].
770775
Note: Will also be set in the Spot class, if None.
771-
n_theta (int):
772-
The number of theta values. If larger than 1 or set to the string "anisotropic",
773-
then the k theta values are used, where k is the problem dimension.
774-
This is handled in spot.py. Default is "anisotropic".
775776
p_val (float):
776777
p value. Used as an initial value if optim_p = True. Otherwise as a constant. Defaults to 2.0.
777778
n_p (int):
@@ -807,11 +808,8 @@ def surrogate_control_init(
807808
of `var_type` in the Spot class fun_control dictionary and the dimension of the problem.
808809
If the Kriging model is used as a surrogate in the Spot class, the setting from
809810
surrogate_control_init() is overwritten.
810-
* `n_theta`: If self.surrogate_control["n_theta"] > 1,
811-
use k theta values, where k is the problem dimension specified in the Spot class.
812-
The problem dimension is set in the Spot class based on the
813-
length of the lower bounds.
814-
* This value `model_fun_evals` will used for the optimization of the surrogate model, e.g., theta values.
811+
* `isotropic`: If the `isotropic` value is set to `True`, then the theta values are isotropic, i.e., the same value is used for all dimensions. If it is set to `False`, then the theta values are anisotropic, i.e., different values are used for each dimension.
812+
* The value `model_fun_evals` will used for the optimization of the surrogate model, e.g., theta values.
815813
Differential evaluation uses `maxiter = 1000` and sets the number of function evaluations to
816814
(maxiter + 1) * popsize * N, which results in 1000 * 15 * k,
817815
because the default popsize is 15 and N is the number of parameters. This is already sufficient
@@ -826,7 +824,7 @@ def surrogate_control_init(
826824
"model_fun_evals": model_fun_evals,
827825
"min_theta": min_theta,
828826
"max_theta": max_theta,
829-
"n_theta": n_theta,
827+
"isotropic": isotropic,
830828
"p_val": p_val,
831829
"n_p": n_p,
832830
"optim_p": optim_p,

test/test_build_Psi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def test_build_Psi():
4747
S_spot.min_X = X[argmin(y)]
4848
# Kriging:
4949

50-
S = Kriging(name="kriging", seed=124, n_theta=2, noise=True, cod_type="norm")
50+
S = Kriging(name="kriging", seed=124, noise=True, cod_type="norm")
5151
S.nat_X = copy.deepcopy(X)
5252
S.nat_y = copy.deepcopy(y)
5353
S.n = S.nat_X.shape[0]

test/test_build_U.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def test_build_U():
4141
S_spot.min_y = min(y)
4242
S_spot.min_X = X[argmin(y)]
4343
# Kriging:
44-
S = Kriging(name="kriging", seed=124, n_theta=2, noise=True, cod_type="norm")
44+
S = Kriging(name="kriging", seed=124, noise=True, cod_type="norm")
4545
S.nat_X = copy.deepcopy(X)
4646
S.nat_y = copy.deepcopy(y)
4747
S.n = S.nat_X.shape[0]

test/test_build_psi_vec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def test_build_psi_vec():
88
X_train = np.array([[1.0, 2.0], [2.0, 4.0], [3.0, 6.0]])
99
y_train = np.array([1.0, 2.0, 3.0])
1010

11-
S = Kriging(name="kriging", seed=123, log_level=50, n_theta=1, noise=False, cod_type="norm")
11+
S = Kriging(name="kriging", seed=123, log_level=50, isotropic=True, noise=False, cod_type="norm")
1212
S.fit(X_train, y_train)
1313

1414
# force theta to simple values:

0 commit comments

Comments
 (0)