Skip to content

Commit 2df8dee

Browse files
0.33.14
1 parent 3d76463 commit 2df8dee

6 files changed

Lines changed: 48 additions & 39 deletions

File tree

notebooks/00_spotPython_tests.ipynb

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

src/spotpython/surrogate/kriging.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Kriging(BaseEstimator, RegressorMixin):
1818
Attributes:
1919
eps (float): A small regularization term to reduce ill-conditioning.
2020
penalty (float): The penalty value used if the correlation matrix is ill-conditioned.
21-
logtheta_lambda_ (np.ndarray): Best-fit log(theta) parameters from fit().
21+
logtheta_loglambda_p_ (np.ndarray): Best-fit log(theta), log(lambda), and p parameters from fit().
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,).
@@ -92,7 +92,7 @@ def __init__(
9292
name (str, optional):
9393
Name of the Kriging model instance. Defaults to "Kriging".
9494
seed (int, optional):
95-
Random seed for reproducibility. Defaults to 124.
95+
Random seed for reproducibility. Used by the optimizer. Defaults to 124.
9696
model_optimizer (callable, optional):
9797
Optimization algorithm for hyperparameter tuning. Defaults to
9898
scipy.optimize.differential_evolution.
@@ -184,7 +184,7 @@ def __init__(
184184
self.log["p"] = []
185185
self.log["Lambda"] = []
186186

187-
self.logtheta_lambda_ = None
187+
self.logtheta_loglambda_p_ = None
188188
self.U_ = None
189189
self.X_ = None
190190
self.y_ = None
@@ -283,7 +283,17 @@ def get_model_params(self) -> Dict[str, float]:
283283
>>> X_values = model.get_model_params()["X"]
284284
>>> print("X values:", X_values)
285285
"""
286-
return {"log_theta_lambda": self.logtheta_lambda_, "U": self.U_, "X": self.X_, "y": self.y_, "negLnLike": self.negLnLike, "inf_Psi": self.inf_Psi, "cnd_Psi": self.cnd_Psi}
286+
return {
287+
"n": self.n,
288+
"k": self.k,
289+
"logtheta_loglambda_p_": self.logtheta_loglambda_p_,
290+
"U": self.U_,
291+
"X": self.X_,
292+
"y": self.y_,
293+
"negLnLike": self.negLnLike,
294+
"inf_Psi": self.inf_Psi,
295+
"cnd_Psi": self.cnd_Psi,
296+
}
287297

288298
def _update_log(self) -> None:
289299
"""
@@ -389,24 +399,24 @@ def fit(self, X: np.ndarray, y: np.ndarray, bounds: Optional[List[Tuple[float, f
389399
n_p = self.n_p if hasattr(self, "n_p") else self.k
390400
bounds += [(self.min_p, self.max_p)] * n_p
391401

392-
self.logtheta_lambda_, _ = self.max_likelihood(bounds)
402+
self.logtheta_loglambda_p_, _ = self.max_likelihood(bounds)
393403

394404
# store theta and Lambda in log scale
395-
self.theta = self.logtheta_lambda_[: self.n_theta]
405+
self.theta = self.logtheta_loglambda_p_[: self.n_theta]
396406
if (self.method == "regression") or (self.method == "reinterpolation"):
397-
# select the first n_theta values from logtheta_lambda_:
398-
self.Lambda = self.logtheta_lambda_[self.n_theta : self.n_theta + 1]
407+
# select the first n_theta values from logtheta_loglambda_p_:
408+
self.Lambda = self.logtheta_loglambda_p_[self.n_theta : self.n_theta + 1]
399409
if self.optim_p:
400-
self.p_val = self.logtheta_lambda_[self.n_theta + 1 : self.n_theta + 1 + self.n_p]
410+
self.p_val = self.logtheta_loglambda_p_[self.n_theta + 1 : self.n_theta + 1 + self.n_p]
401411
elif self.method == "interpolation":
402412
self.Lambda = None
403413
if self.optim_p:
404-
self.p_val = self.logtheta_lambda_[self.n_theta : self.n_theta + self.n_p]
414+
self.p_val = self.logtheta_loglambda_p_[self.n_theta : self.n_theta + self.n_p]
405415
else:
406416
raise ValueError("method must be one of 'interpolation', 'regression', or 'reinterpolation'")
407417

408418
# Once logtheta_lambda is found, compute the final correlation matrix
409-
self.negLnLike, self.Psi_, self.U_ = self.likelihood(self.logtheta_lambda_)
419+
self.negLnLike, self.Psi_, self.U_ = self.likelihood(self.logtheta_loglambda_p_)
410420

411421
# Update log with the current values
412422
self._update_log()
@@ -696,18 +706,18 @@ def _pred(self, x: np.ndarray) -> float:
696706
y = self.y_.flatten()
697707

698708
if (self.method == "regression") or (self.method == "reinterpolation"):
699-
self.theta = self.logtheta_lambda_[: self.n_theta]
700-
lambda_ = self.logtheta_lambda_[self.n_theta : self.n_theta + 1]
709+
self.theta = self.logtheta_loglambda_p_[: self.n_theta]
710+
lambda_ = self.logtheta_loglambda_p_[self.n_theta : self.n_theta + 1]
701711
# lambda is in log scale, so transform it back:
702712
lambda_ = 10.0**lambda_
703713
if self.optim_p:
704-
self.p_val = self.logtheta_lambda_[self.n_theta + 1 : self.n_theta + 1 + self.n_p]
714+
self.p_val = self.logtheta_loglambda_p_[self.n_theta + 1 : self.n_theta + 1 + self.n_p]
705715
elif self.method == "interpolation":
706-
self.theta = self.logtheta_lambda_[: self.n_theta]
716+
self.theta = self.logtheta_loglambda_p_[: self.n_theta]
707717
# use the original, untransformed eps:
708718
lambda_ = self.eps
709719
if self.optim_p:
710-
self.p_val = self.logtheta_lambda_[self.n_theta : self.n_theta + self.n_p]
720+
self.p_val = self.logtheta_loglambda_p_[self.n_theta : self.n_theta + self.n_p]
711721
else:
712722
raise ValueError("method must be one of 'interpolation', 'regression', or 'reinterpolation'")
713723

@@ -790,11 +800,11 @@ def max_likelihood(self, bounds: List[Tuple[float, float]]) -> Tuple[np.ndarray,
790800
print("Minimized negative log-likelihood:", best_fun)
791801
"""
792802

793-
def objective(logtheta_loglambda_p: np.ndarray) -> float:
794-
neg_ln_like, _, _ = self.likelihood(logtheta_loglambda_p)
803+
def objective(logtheta_loglambda_p_: np.ndarray) -> float:
804+
neg_ln_like, _, _ = self.likelihood(logtheta_loglambda_p_)
795805
return neg_ln_like
796806

797-
result = differential_evolution(objective, bounds)
807+
result = differential_evolution(func=objective, bounds=bounds, seed=self.seed)
798808
return result.x, result.fun
799809

800810
def plot(self, i: int = 0, j: int = 1, show: Optional[bool] = True, add_points: bool = True) -> None:

test/test_fit_kriging.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def fake_likelihood(x):
4040

4141
assert np.allclose(model.theta, np.array([0.1, -0.2]))
4242
assert np.allclose(model.Lambda, np.array([-6.0]))
43-
assert np.allclose(model.logtheta_lambda_, np.array([0.1, -0.2, -6.0]))
43+
assert np.allclose(model.logtheta_loglambda_p_, np.array([0.1, -0.2, -6.0]))
4444
assert model.negLnLike == pytest.approx(3.14)
4545
assert np.allclose(model.Psi_, np.eye(X.shape[0]))
4646
assert np.allclose(model.U_, np.eye(X.shape[0]))
@@ -73,7 +73,7 @@ def fake_likelihood(x):
7373

7474
assert np.allclose(model.theta, np.array([0.25, -0.75]))
7575
assert model.Lambda is None
76-
assert np.allclose(model.logtheta_lambda_, np.array([0.25, -0.75]))
76+
assert np.allclose(model.logtheta_loglambda_p_, np.array([0.25, -0.75]))
7777
assert model.negLnLike == pytest.approx(1.23)
7878
assert np.allclose(model.Psi_, np.eye(X.shape[0]))
7979
assert np.allclose(model.U_, np.eye(X.shape[0]))
@@ -107,7 +107,7 @@ def fake_likelihood(x):
107107

108108
assert np.allclose(model.theta, np.array([0.0, 0.1, 0.2]))
109109
assert np.allclose(model.Lambda, np.array([-3.0]))
110-
assert np.allclose(model.logtheta_lambda_, np.array([0.0, 0.1, 0.2, -3.0]))
110+
assert np.allclose(model.logtheta_loglambda_p_, np.array([0.0, 0.1, 0.2, -3.0]))
111111
assert model.negLnLike == pytest.approx(0.77)
112112
assert np.allclose(model.Psi_, np.eye(X.shape[0]))
113113
assert np.allclose(model.U_, np.eye(X.shape[0]))
@@ -142,8 +142,8 @@ def fake_likelihood(x):
142142
assert np.allclose(model.theta, np.array([0.5]))
143143
# Lambda is taken from the first param after n_theta
144144
assert np.allclose(model.Lambda, np.array([0.4]))
145-
# logtheta_lambda_ still holds all optimized parameters
146-
assert np.allclose(model.logtheta_lambda_, np.array([0.5, 0.4, 0.3, -4.0]))
145+
# logtheta_loglambda_p_ still holds all optimized parameters
146+
assert np.allclose(model.logtheta_loglambda_p_, np.array([0.5, 0.4, 0.3, -4.0]))
147147

148148

149149
def test_fit_respects_explicit_bounds_override(monkeypatch):

test/test_fit_n.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_fit_standard_kriging(simple_data):
3232
assert model.k == X.shape[1]
3333

3434
# 3. Check if hyperparameters and model components are set
35-
assert model.logtheta_lambda_ is not None
35+
assert model.logtheta_loglambda_p_ is not None
3636
assert model.theta is not None
3737
assert model.Lambda is not None # Default is 'regression'
3838
assert model.negLnLike is not None
@@ -56,8 +56,8 @@ def test_fit_with_custom_bounds(simple_data):
5656
model.fit(X, y, bounds=custom_bounds)
5757

5858
# Check if the optimized parameters are within the custom bounds
59-
log_thetas = model.logtheta_lambda_[:2]
60-
log_lambda = model.logtheta_lambda_[2]
59+
log_thetas = model.logtheta_loglambda_p_[:2]
60+
log_lambda = model.logtheta_loglambda_p_[2]
6161

6262
assert np.all(log_thetas >= -1) and np.all(log_thetas <= 1)
6363
assert -5 <= log_lambda <= -1
@@ -75,7 +75,7 @@ def test_fit_interpolation_method(simple_data):
7575
# For interpolation, Lambda should be None and not optimized
7676
assert model.Lambda is None
7777
# The optimized vector should only contain theta values
78-
assert len(model.logtheta_lambda_) == model.n_theta
78+
assert len(model.logtheta_loglambda_p_) == model.n_theta
7979

8080

8181
def test_fit_with_optim_p(simple_data):
@@ -91,5 +91,5 @@ def test_fit_with_optim_p(simple_data):
9191
assert model.p_val is not None
9292
assert isinstance(model.p_val, np.ndarray)
9393
# The optimized vector should contain theta, lambda, and p
94-
assert len(model.logtheta_lambda_) == model.n_theta + 1 + model.n_p
94+
assert len(model.logtheta_loglambda_p_) == model.n_theta + 1 + model.n_p
9595

test/test_maxlikelihood_kriging.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import spotpython.surrogate.kriging as kriging_mod
55
from spotpython.surrogate.kriging import Kriging
66

7-
87
def test_max_likelihood_calls_objective_and_returns_de_result(monkeypatch):
98
model = Kriging(method="regression", n_theta=2)
109

@@ -17,11 +16,11 @@ def fake_likelihood(x: np.ndarray):
1716

1817
monkeypatch.setattr(model, "likelihood", fake_likelihood, raising=True)
1918

20-
def fake_de(objective, bounds):
19+
def fake_de(*, func=None, bounds=None, **kwargs):
2120
# Capture bounds, evaluate objective at a chosen candidate
2221
calls["bounds"] = bounds
2322
best_x = np.array([0.0, 0.0, -6.0], dtype=float)
24-
fun = objective(best_x) # should call model.likelihood(best_x)
23+
fun = func(best_x) # should call model.likelihood(best_x)
2524
return SimpleNamespace(x=best_x, fun=fun)
2625

2726
# Patch the module-level DE used inside Kriging.max_likelihood
@@ -42,7 +41,7 @@ def test_max_likelihood_propagates_de_output_even_if_objective_not_used(monkeypa
4241
model = Kriging(method="regression", n_theta=2)
4342

4443
# Ensure that even if DE doesn't call objective, return values are passed through
45-
def fake_de(objective, bounds):
44+
def fake_de(*, func=None, bounds=None, **kwargs):
4645
return SimpleNamespace(x=np.array([1.0, -1.0, -3.0]), fun=-123.456)
4746

4847
monkeypatch.setattr(kriging_mod, "differential_evolution", fake_de, raising=True)
@@ -62,9 +61,9 @@ def test_max_likelihood_passes_bounds_correctly(monkeypatch):
6261
# Minimal likelihood to satisfy objective
6362
monkeypatch.setattr(model, "likelihood", lambda x: (0.0, None, None), raising=True)
6463

65-
def fake_de(objective, bounds):
64+
def fake_de(*, func=None, bounds=None, **kwargs):
6665
seen["bounds"] = bounds
67-
return SimpleNamespace(x=np.zeros(len(bounds)), fun=objective(np.zeros(len(bounds))))
66+
return SimpleNamespace(x=np.zeros(len(bounds)), fun=func(np.zeros(len(bounds))))
6867

6968
monkeypatch.setattr(kriging_mod, "differential_evolution", fake_de, raising=True)
7069

0 commit comments

Comments
 (0)