Skip to content

Commit e521a3b

Browse files
0.33.9
improved get() methods for Kriging
1 parent a5f4413 commit e521a3b

4 files changed

Lines changed: 242 additions & 144 deletions

File tree

notebooks/00_spotPython_tests.ipynb

Lines changed: 102 additions & 82 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.33.8"
10+
version = "0.33.9"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotpython/spot/spot.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,27 +2751,27 @@ def get_importance(self) -> list:
27512751
>>> print(importance2)
27522752
[]
27532753
"""
2754-
# Check if surrogate exists
2755-
if not hasattr(self, "surrogate"):
2754+
# Check if surrogate exists and surrogate is not None
2755+
if not hasattr(self, "surrogate") or self.surrogate is None:
27562756
print("No surrogate model available.")
27572757
return []
27582758

2759-
# Check if surrogate has theta attribute for multi-theta models
2760-
if not hasattr(self.surrogate, "theta"):
2761-
print("Surrogate model does not have theta attribute.")
2762-
return []
2763-
2764-
# Check if surrogate is not isotropic
2765-
if self.surrogate.isotropic:
2766-
print("Surrogate model is isotropic.")
2759+
# check if surrogate name is "Kriging"
2760+
if not (isinstance(self.surrogate, Kriging) and getattr(self.surrogate, "name", None) in ["Kriging"]):
2761+
print("Importance calculation is only available for Kriging surrogate models.")
27672762
return []
27682763

27692764
# Check if all required attributes exist for importance calculation
27702765
if not hasattr(self, "all_var_name"):
27712766
print("Variable names (all_var_name) not available.")
27722767
return []
27732768

2774-
if self.surrogate.n_theta > 1 and hasattr(self, "var_name") and self.var_name is not None:
2769+
n_theta = self.surrogate.get_params()["n_theta"]
2770+
if n_theta is None:
2771+
print("Number of theta values (n_theta) not available. Check if surrogate is fitted.")
2772+
return []
2773+
2774+
if n_theta > 1 and hasattr(self, "var_name") and self.var_name is not None:
27752775
try:
27762776
output = [0] * len(self.all_var_name)
27772777
theta = np.power(10, self.surrogate.theta)

src/spotpython/surrogate/kriging.py

Lines changed: 128 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ def __init__(
4646
eps: float = None,
4747
penalty: float = 1e4,
4848
method="regression",
49-
noise: bool = False,
5049
var_type: List[str] = ["num"],
5150
name: str = "Kriging",
5251
seed: int = 124,
5352
model_optimizer=None,
5453
model_fun_evals: Optional[int] = None,
54+
n_theta: Optional[int] = None,
5555
min_theta: float = -3.0,
5656
max_theta: float = 2.0,
5757
theta_init_zero: bool = False,
@@ -67,6 +67,8 @@ def __init__(
6767
counter=None,
6868
metric_factorial="canberra",
6969
isotropic: bool = False,
70+
theta: Optional[np.ndarray] = None,
71+
Lambda: Optional[float] = None,
7072
**kwargs,
7173
):
7274
"""
@@ -83,9 +85,63 @@ def __init__(
8385
not positive-definite. Defaults to 1e4.
8486
method (str, optional):
8587
The type how the model uis fitted. Can be "interpolation", "regression", or "reinterpolation". Defaults to "regression".
88+
var_type (List[str], optional):
89+
List specifying the variable types for each input dimension.
90+
Possible values are "num", "int", "factor", and "ordered".
91+
Defaults to ["num"].
92+
name (str, optional):
93+
Name of the Kriging model instance. Defaults to "Kriging".
94+
seed (int, optional):
95+
Random seed for reproducibility. Defaults to 124.
96+
model_optimizer (callable, optional):
97+
Optimization algorithm for hyperparameter tuning. Defaults to
98+
scipy.optimize.differential_evolution.
99+
model_fun_evals (int, optional):
100+
Maximum number of function evaluations for the optimizer.
101+
Defaults to 100.
102+
n_theta (int, optional):
103+
Number of theta values to be used. If None, it will be set during fitting.
104+
Defaults to None.
105+
min_theta (float, optional):
106+
Minimum bound for log(theta) during optimization. Defaults to -3.0.
107+
max_theta (float, optional):
108+
Maximum bound for log(theta) during optimization. Defaults to 2.0.
109+
theta_init_zero (bool, optional):
110+
If True, initializes theta values to zero before fitting.
111+
Defaults to False.
112+
p_val (float, optional):
113+
Initial power exponent value for the correlation function. Defaults to 2.0.
114+
n_p (int, optional):
115+
Number of p values to be used. Defaults to 1.
116+
optim_p (bool, optional):
117+
If True, optimizes the p values during fitting. Defaults to False.
118+
min_p (float, optional):
119+
Minimum bound for p during optimization. Defaults to 1.0.
120+
max_p (float, optional):
121+
Maximum bound for p during optimization. Defaults to 2.0.
122+
min_Lambda (float, optional):
123+
Minimum bound for log(Lambda) during optimization. Defaults to -9.0.
124+
max_Lambda (float, optional):
125+
Maximum bound for log(Lambda) during optimization. Defaults to 2.0.
126+
log_level (int, optional):
127+
Logging level for the model. Defaults to 0 (no logging).
128+
spot_writer (object, optional):
129+
Writer object for logging (e.g., TensorBoard writer). Defaults to None.
130+
counter (int, optional):
131+
Counter for logging iterations. Defaults to None.
132+
metric_factorial (str, optional):
133+
Metric to be used for factorial design. Defaults to "canberra".
86134
isotropic (bool, optional):
87135
If True, the model is isotropic, meaning all variables are treated equally (only one theta value is used).
88136
If False, the model can handle different theta values, one for each dimension. Defaults to False.
137+
theta (np.ndarray, optional):
138+
Currently ignored. Initial theta values for the model. If None, theta values are initialized during fitting.
139+
Defaults to None.
140+
Lambda (float, optional):
141+
Currently ignored. Initial Lambda value for the model. If None, Lambda is initialized during fitting.
142+
Defaults to None.
143+
**kwargs:
144+
Additional keyword arguments.
89145
"""
90146
if eps is None:
91147
self.eps = self._get_eps()
@@ -95,8 +151,6 @@ def __init__(
95151
raise ValueError("eps must be positive")
96152
self.eps = eps
97153
self.penalty = penalty
98-
99-
self.noise = noise
100154
self.var_type = var_type
101155
self.name = name
102156
self.seed = seed
@@ -110,12 +164,14 @@ def __init__(
110164
self.max_Lambda = max_Lambda
111165
self.min_p = min_p
112166
self.max_p = max_p
113-
self.n_theta = None # Will be set in fit()
167+
self.n_theta = n_theta # Will be set during fit
114168
self.isotropic = isotropic
115169
self.p_val = p_val
116170
self.n_p = n_p
117171
self.optim_p = optim_p
118172
self.theta_init_zero = theta_init_zero
173+
self.theta = theta
174+
self.Lambda = Lambda
119175
self.model_optimizer = model_optimizer
120176
if self.model_optimizer is None:
121177
self.model_optimizer = differential_evolution
@@ -175,7 +231,7 @@ def _set_variable_types(self) -> None:
175231
var_type = ["num", "int", "float"]
176232
n_theta=2
177233
n_p=2
178-
S=Kriging(var_type=var_type, seed=124, n_theta=n_theta, n_p=n_p, optim_p=True, noise=True)
234+
S=Kriging(var_type=var_type, seed=124, n_theta=n_theta, n_p=n_p, optim_p=True)
179235
S._initialize_variables(nat_X, nat_y)
180236
S._set_variable_types()
181237
assert S.var_type == ["num", "int", "float"]
@@ -207,7 +263,26 @@ def get_model_params(self) -> Dict[str, float]:
207263
This method is NOT required for scikit-learn compatibility.
208264
209265
Returns:
210-
dict: Parameter names not included in get_params() mapped to their values.
266+
dict:
267+
Parameter names not included in get_params() mapped to their values. This includes the following keys:
268+
- "log_theta_lambda"
269+
- "U"
270+
- "X"
271+
- "y"
272+
- "negLnLike"
273+
274+
Examples:
275+
>>> import numpy as np
276+
>>> from spotpython.surrogate.kriging import Kriging
277+
>>> # Training data
278+
>>> X_train = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
279+
>>> y_train = np.array([0.1, 0.2, 0.3])
280+
>>> # Initialize and fit the Kriging model
281+
>>> model = Kriging()
282+
>>> model.fit(X_train, y_train)
283+
>>> # get theta values of the fitted model
284+
>>> X_values = model.get_model_params()["X"]
285+
>>> print("X values:", X_values)
211286
"""
212287
return {"log_theta_lambda": self.logtheta_lambda_, "U": self.U_, "X": self.X_, "y": self.y_, "negLnLike": self.negLnLike}
213288

@@ -247,6 +322,15 @@ def _update_log(self) -> None:
247322
self.spot_writer.add_scalars("spot_p", {f"p_{i}": p[i] for i in range(self.n_p)}, self.counter + self.log_length)
248323
self.spot_writer.flush()
249324

325+
def set_theta(self) -> None:
326+
if self.isotropic:
327+
# If isotropic, set n_theta to 1
328+
self.n_theta = 1
329+
print(f"Isotropic model: n_theta set to {self.n_theta}")
330+
else:
331+
self.n_theta = self.k
332+
print(f"Anisotropic model: n_theta set to {self.n_theta}")
333+
250334
def fit(self, X: np.ndarray, y: np.ndarray, bounds: Optional[List[Tuple[float, float]]] = None) -> "Kriging":
251335
"""
252336
Fits the Kriging model to training data X and y. This method is compatible
@@ -282,13 +366,8 @@ def fit(self, X: np.ndarray, y: np.ndarray, bounds: Optional[List[Tuple[float, f
282366
self.y_ = y
283367
self.n, self.k = self.X_.shape
284368
self._set_variable_types()
285-
if self.isotropic:
286-
# If isotropic, set n_theta to 1
287-
self.n_theta = 1
288-
print(f"Isotropic model: n_theta set to {self.n_theta}")
289-
else:
290-
self.n_theta = self.k
291-
print(f"Anisotropic model: n_theta set to {self.n_theta}")
369+
if self.n_theta is None:
370+
self.set_theta()
292371
# Calculate and store min and max of X
293372
self.min_X = np.min(self.X_, axis=0)
294373
self.max_X = np.max(self.X_, axis=0)
@@ -415,7 +494,7 @@ def build_Psi(self) -> None:
415494
nat_y = np.array([0, 1])
416495
n=1
417496
p=1
418-
S=Kriging(name='kriging', seed=124, n_theta=n, n_p=p, optim_p=True, noise=False)
497+
S=Kriging(name='kriging', seed=124, n_theta=n, n_p=p, optim_p=True)
419498
S._initialize_variables(nat_X, nat_y)
420499
S._set_variable_types()
421500
print(S.nat_X)
@@ -574,42 +653,41 @@ def likelihood(self, x: np.ndarray) -> Tuple[float, np.ndarray, np.ndarray]:
574653

575654
def build_psi_vec(self, x: np.ndarray) -> None:
576655
"""
577-
Build the psi vector required for predictive methods.
578-
579-
Args:
580-
x (ndarray): Point to calculate the psi vector for.
581-
582-
Returns:
583-
None
584-
585-
Modifies:
586-
self.psi (np.ndarray): Updates the psi vector.
587-
588-
Examples:
589-
>>> import numpy as np
590-
from spotpython.build.kriging import Kriging
591-
X_train = np.array([[1., 2.],
592-
[2., 4.],
593-
[3., 6.]])
594-
y_train = np.array([1., 2., 3.])
595-
S = Kriging(name='kriging',
596-
seed=123,
597-
log_level=50,
598-
n_theta=1,
599-
noise=False,
600-
cod_type="norm")
601-
S.fit(X_train, y_train)
602-
# force theta to simple values:
603-
S.theta = np.array([0.0])
604-
nat_X = np.array([1., 0.])
605-
S.psi = np.zeros((S.n, 1))
606-
S.build_psi_vec(nat_X)
607-
res = np.array([[np.exp(-4)],
608-
[np.exp(-17)],
609-
[np.exp(-40)]])
610-
assert np.array_equal(S.psi, res)
611-
print(f"S.psi: {S.psi}")
612-
print(f"Control value res: {res}")
656+
Build the psi vector required for predictive methods.
657+
658+
Args:
659+
x (ndarray): Point to calculate the psi vector for.
660+
661+
Returns:
662+
None
663+
664+
Modifies:
665+
self.psi (np.ndarray): Updates the psi vector.
666+
667+
Examples:
668+
>>> import numpy as np
669+
from spotpython.build.kriging import Kriging
670+
X_train = np.array([[1., 2.],
671+
[2., 4.],
672+
[3., 6.]])
673+
y_train = np.array([1., 2., 3.])
674+
S = Kriging(name='kriging',
675+
seed=123,
676+
log_level=50,
677+
n_theta=1,
678+
^ cod_type="norm")
679+
S.fit(X_train, y_train)
680+
# force theta to simple values:
681+
S.theta = np.array([0.0])
682+
nat_X = np.array([1., 0.])
683+
S.psi = np.zeros((S.n, 1))
684+
S.build_psi_vec(nat_X)
685+
res = np.array([[np.exp(-4)],
686+
[np.exp(-17)],
687+
[np.exp(-40)]])
688+
assert np.array_equal(S.psi, res)
689+
print(f"S.psi: {S.psi}")
690+
print(f"Control value res: {res}")
613691
"""
614692
try:
615693
n = self.X_.shape[0]

0 commit comments

Comments
 (0)