• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SMTorg / smt / 9797207634

04 Jul 2024 03:58PM UTC coverage: 92.495% (-0.005%) from 92.5%
9797207634

Pull #614

github

web-flow
Merge 151b23126 into d75ff4488
Pull Request #614: fix ei warning divide by zero

12 of 12 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

6655 of 7195 relevant lines covered (92.49%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.72
/smt/surrogate_models/krg_based.py
1
"""
2
Author: Dr. Mohamed Amine Bouhlel <mbouhlel@umich.edu>
3
Some functions are copied from gaussian_process submodule (Scikit-learn 0.14)
4
This package is distributed under New BSD license.
5
"""
6

7
import numpy as np
1✔
8
from enum import Enum
1✔
9
from scipy import linalg, optimize
1✔
10
from copy import deepcopy
1✔
11
import warnings
1✔
12

13
from smt.surrogate_models.surrogate_model import SurrogateModel
1✔
14
from smt.utils.kriging import (
1✔
15
    differences,
16
    constant,
17
    linear,
18
    quadratic,
19
    pow_exp,
20
    squar_exp,
21
    squar_sin_exp,
22
    abs_exp,
23
    act_exp,
24
    cross_distances,
25
    matern52,
26
    matern32,
27
    gower_componentwise_distances,
28
    componentwise_distance,
29
    componentwise_distance_PLS,
30
    compute_X_cont,
31
    cross_levels,
32
    compute_X_cross,
33
    cross_levels_homo_space,
34
    MixHrcKernelType,
35
    matrix_data_corr_levels_cat_matrix,
36
    matrix_data_corr_levels_cat_mod,
37
    matrix_data_corr_levels_cat_mod_comps,
38
)
39

40
from smt.utils.misc import standardization
1✔
41
from smt.utils.checks import ensure_2d_array, check_support
1✔
42
from scipy.stats import multivariate_normal as m_norm
1✔
43
from smt.sampling_methods import LHS
1✔
44
from smt.utils.design_space import (
1✔
45
    BaseDesignSpace,
46
    ensure_design_space,
47
    CategoricalVariable,
48
)
49

50

51
class MixIntKernelType(Enum):
1✔
52
    EXP_HOMO_HSPHERE = "EXP_HOMO_HSPHERE"
1✔
53
    HOMO_HSPHERE = "HOMO_HSPHERE"
1✔
54
    CONT_RELAX = "CONT_RELAX"
1✔
55
    GOWER = "GOWER"
1✔
56
    COMPOUND_SYMMETRY = "COMPOUND_SYMMETRY"
1✔
57

58

59
class KrgBased(SurrogateModel):
1✔
60
    _regression_types = {"constant": constant, "linear": linear, "quadratic": quadratic}
1✔
61

62
    _correlation_types = {
1✔
63
        "pow_exp": pow_exp,
64
        "abs_exp": abs_exp,
65
        "squar_exp": squar_exp,
66
        "squar_sin_exp": squar_sin_exp,
67
        "act_exp": act_exp,
68
        "matern52": matern52,
69
        "matern32": matern32,
70
    }
71

72
    name = "KrigingBased"
1✔
73

74
    def _initialize(self):
1✔
75
        super(KrgBased, self)._initialize()
1✔
76
        declare = self.options.declare
1✔
77
        supports = self.supports
1✔
78
        declare(
1✔
79
            "poly",
80
            "constant",
81
            values=("constant", "linear", "quadratic"),
82
            desc="Regression function type",
83
            types=(str),
84
        )
85
        declare(
1✔
86
            "corr",
87
            "squar_exp",
88
            values=(
89
                "pow_exp",
90
                "abs_exp",
91
                "squar_exp",
92
                "act_exp",
93
                "matern52",
94
                "matern32",
95
            ),
96
            desc="Correlation function type",
97
        )
98
        declare(
1✔
99
            "pow_exp_power",
100
            1.9,
101
            types=(float),
102
            desc="Power for the pow_exp kernel function (valid values in (0.0, 2.0]). \
103
                This option is set automatically when corr option is squar, abs, or matern.",
104
        )
105
        declare(
1✔
106
            "categorical_kernel",
107
            MixIntKernelType.CONT_RELAX,
108
            values=[
109
                MixIntKernelType.CONT_RELAX,
110
                MixIntKernelType.GOWER,
111
                MixIntKernelType.EXP_HOMO_HSPHERE,
112
                MixIntKernelType.HOMO_HSPHERE,
113
                MixIntKernelType.COMPOUND_SYMMETRY,
114
            ],
115
            desc="The kernel to use for categorical inputs. Only for non continuous Kriging",
116
        )
117
        declare(
1✔
118
            "hierarchical_kernel",
119
            MixHrcKernelType.ALG_KERNEL,
120
            values=[
121
                MixHrcKernelType.ALG_KERNEL,
122
                MixHrcKernelType.ARC_KERNEL,
123
            ],
124
            desc="The kernel to use for mixed hierarchical inputs. Only for non continuous Kriging",
125
        )
126
        declare(
1✔
127
            "nugget",
128
            100.0 * np.finfo(np.double).eps,
129
            types=(float),
130
            desc="a jitter for numerical stability",
131
        )
132
        declare(
1✔
133
            "theta0", [1e-2], types=(list, np.ndarray), desc="Initial hyperparameters"
134
        )
135
        # In practice, in 1D and for X in [0,1], theta^{-2} in [1e-2,infty), i.e.
136
        # theta in (0,1e1], is a good choice to avoid overfitting. By standardising
137
        # X in R, X_norm = (X-X_mean)/X_std, then X_norm in [-1,1] if considering
138
        # one std intervals. This leads to theta in (0,2e1]
139
        declare(
1✔
140
            "theta_bounds",
141
            [1e-6, 2e1],
142
            types=(list, np.ndarray),
143
            desc="bounds for hyperparameters",
144
        )
145
        declare(
1✔
146
            "hyper_opt",
147
            "TNC",
148
            values=("Cobyla", "TNC"),
149
            desc="Optimiser for hyperparameters optimisation",
150
            types=str,
151
        )
152
        declare(
1✔
153
            "eval_noise",
154
            False,
155
            types=bool,
156
            values=(True, False),
157
            desc="noise evaluation flag",
158
        )
159
        declare(
1✔
160
            "noise0",
161
            [0.0],
162
            types=(list, np.ndarray),
163
            desc="Initial noise hyperparameters",
164
        )
165
        declare(
1✔
166
            "noise_bounds",
167
            [100.0 * np.finfo(np.double).eps, 1e10],
168
            types=(list, np.ndarray),
169
            desc="bounds for noise hyperparameters",
170
        )
171
        declare(
1✔
172
            "use_het_noise",
173
            False,
174
            types=bool,
175
            values=(True, False),
176
            desc="heteroscedastic noise evaluation flag",
177
        )
178
        declare(
1✔
179
            "n_start",
180
            10,
181
            types=int,
182
            desc="number of optimizer runs (multistart method)",
183
        )
184
        declare(
1✔
185
            "xlimits",
186
            None,
187
            types=(list, np.ndarray),
188
            desc="definition of a design space of float (continuous) variables: "
189
            "array-like of size nx x 2 (lower, upper bounds)",
190
        )
191
        declare(
1✔
192
            "design_space",
193
            None,
194
            types=(BaseDesignSpace, list, np.ndarray),
195
            desc="definition of the (hierarchical) design space: "
196
            "use `smt.utils.design_space.DesignSpace` as the main API. Also accepts list of float variable bounds",
197
        )
198
        self.options.declare(
1✔
199
            "random_state",
200
            default=41,
201
            types=(type(None), int, np.random.RandomState),
202
            desc="Numpy RandomState object or seed number which controls random draws \
203
                for internal optim (set by default to get reproductibility)",
204
        )
205
        self.best_iteration_fail = None
1✔
206
        self.nb_ill_matrix = 5
1✔
207
        self.is_acting_points = {}
1✔
208

209
        supports["derivatives"] = True
1✔
210
        supports["variances"] = True
1✔
211
        supports["variance_derivatives"] = True
1✔
212
        supports["x_hierarchy"] = True
1✔
213

214
    def _final_initialize(self):
1✔
215
        if isinstance(self.options["random_state"], np.random.RandomState):
1✔
216
            self.random_state = self.options["random_state"]
×
217
        elif isinstance(self.options["random_state"], int):
1✔
218
            self.random_state = np.random.RandomState(self.options["random_state"])
1✔
219
        else:
220
            self.random_state = np.random.RandomState()
×
221

222
        # initialize default power values
223
        if self.options["corr"] == "squar_exp":
1✔
224
            self.options["pow_exp_power"] = 2.0
1✔
225
        elif self.options["corr"] in [
1✔
226
            "abs_exp",
227
            "squar_sin_exp",
228
            "matern32",
229
            "matern52",
230
        ]:
231
            self.options["pow_exp_power"] = 1.0
1✔
232

233
        # Check the pow_exp_power is >0 and <=2
234
        assert (
1✔
235
            self.options["pow_exp_power"] > 0 and self.options["pow_exp_power"] <= 2
236
        ), (
237
            "The power value for exponential power function can only be >0 and <=2, but %s was given"
238
            % self.options["pow_exp_power"]
239
        )
240

241
    @property
1✔
242
    def design_space(self) -> BaseDesignSpace:
1✔
243
        xt = self.training_points.get(None)
1✔
244
        if xt is not None:
1✔
245
            xt = xt[0][0]
1✔
246

247
        if self.options["design_space"] is None:
1✔
248
            self.options["design_space"] = ensure_design_space(
1✔
249
                xt=xt, xlimits=self.options["xlimits"]
250
            )
251

252
        elif not isinstance(self.options["design_space"], BaseDesignSpace):
1✔
253
            ds_input = self.options["design_space"]
×
254
            self.options["design_space"] = ensure_design_space(
×
255
                xt=xt, xlimits=ds_input, design_space=ds_input
256
            )
257
        return self.options["design_space"]
1✔
258

259
    @property
1✔
260
    def is_continuous(self) -> bool:
1✔
261
        return self.design_space.is_all_cont
1✔
262

263
    def set_training_values(
1✔
264
        self, xt: np.ndarray, yt: np.ndarray, name=None, is_acting=None
265
    ) -> None:
266
        """
267
        Set training data (values).
268

269
        Parameters
270
        ----------
271
        xt : np.ndarray[nt, nx] or np.ndarray[nt]
272
            The input values for the nt training points.
273
        yt : np.ndarray[nt, ny] or np.ndarray[nt]
274
            The output values for the nt training points.
275
        name : str or None
276
            An optional label for the group of training points being set.
277
            This is only used in special situations (e.g., multi-fidelity applications).
278
        is_acting : np.ndarray[nt, nx] or np.ndarray[nt]
279
            Matrix specifying which of the design variables is acting in a hierarchical design space
280
        """
281
        super().set_training_values(xt, yt, name=name)
1✔
282
        if is_acting is not None:
1✔
283
            self.is_acting_points[name] = is_acting
1✔
284

285
    def _correct_distances_cat_decreed(
1✔
286
        self,
287
        D,
288
        is_acting,
289
        listcatdecreed,
290
        ij,
291
        is_acting_y=None,
292
        mixint_type=MixIntKernelType.CONT_RELAX,
293
    ):
294
        indjcat = -1
1✔
295
        for j in listcatdecreed:
1✔
296
            indjcat = indjcat + 1
1✔
297
            if j:
1✔
298
                indicat = -1
1✔
299
                indices = 0
1✔
300
                for v in range(len(self.design_space.design_variables)):
1✔
301
                    if isinstance(
1✔
302
                        self.design_space.design_variables[v], CategoricalVariable
303
                    ):
304
                        indicat = indicat + 1
1✔
305
                        if indicat == indjcat:
1✔
306
                            ia2 = np.zeros((len(ij), 2), dtype=bool)
1✔
307
                            if is_acting_y is None:
1✔
308
                                ia2 = (is_acting[:, self.cat_features][:, indjcat])[ij]
1✔
309
                            else:
310
                                ia2[:, 0] = (
1✔
311
                                    is_acting[:, self.cat_features][:, indjcat]
312
                                )[ij[:, 0]]
313
                                ia2[:, 1] = (
1✔
314
                                    is_acting_y[:, self.cat_features][:, indjcat]
315
                                )[ij[:, 1]]
316

317
                            act_inact = ia2[:, 0] ^ ia2[:, 1]
1✔
318
                            act_act = ia2[:, 0] & ia2[:, 1]
1✔
319

320
                            if mixint_type == MixIntKernelType.CONT_RELAX:
1✔
321
                                val_act = (
1✔
322
                                    np.array([1] * self.n_levels[indjcat])
323
                                    - self.X2_offset[
324
                                        indices : indices + self.n_levels[indjcat]
325
                                    ]
326
                                ) / self.X2_scale[
327
                                    indices : indices + self.n_levels[indjcat]
328
                                ] - (
329
                                    np.array([0] * self.n_levels[indjcat])
330
                                    - self.X2_offset[
331
                                        indices : indices + self.n_levels[indjcat]
332
                                    ]
333
                                ) / self.X2_scale[
334
                                    indices : indices + self.n_levels[indjcat]
335
                                ]
336
                                D[:, indices : indices + self.n_levels[indjcat]][
1✔
337
                                    act_inact
338
                                ] = val_act
339
                                D[:, indices : indices + self.n_levels[indjcat]][
1✔
340
                                    act_act
341
                                ] = (
342
                                    np.sqrt(2)
343
                                    * D[:, indices : indices + self.n_levels[indjcat]][
344
                                        act_act
345
                                    ]
346
                                )
347
                            elif mixint_type == MixIntKernelType.GOWER:
1✔
348
                                D[:, indices : indices + 1][act_inact] = (
1✔
349
                                    self.n_levels[indjcat] * 0.5
350
                                )
351
                                D[:, indices : indices + 1][act_act] = (
1✔
352
                                    np.sqrt(2) * D[:, indices : indices + 1][act_act]
353
                                )
354

355
                            else:
356
                                raise ValueError(
×
357
                                    "Continuous decreed kernel not implemented"
358
                                )
359
                        else:
360
                            if mixint_type == MixIntKernelType.CONT_RELAX:
1✔
361
                                indices = indices + self.n_levels[indicat]
1✔
362
                            elif mixint_type == MixIntKernelType.GOWER:
1✔
363
                                indices = indices + 1
1✔
364
                    else:
365
                        indices = indices + 1
1✔
366
        return D
1✔
367

368
    def _new_train(self):
1✔
369
        # Sampling points X and y
370
        X = self.training_points[None][0][0]
1✔
371
        y = self.training_points[None][0][1]
1✔
372
        # Get is_acting status from design space model if needed (might correct training points)
373
        is_acting = self.is_acting_points.get(None)
1✔
374
        if is_acting is None and not self.is_continuous:
1✔
375
            X, is_acting = self.design_space.correct_get_acting(X)
1✔
376
            self.training_points[None][0][0] = X
1✔
377
            self.is_acting_points[None] = is_acting
1✔
378

379
        # Compute PLS-coefficients (attr of self) and modified X and y (if GEKPLS is used)
380
        if self.name not in ["Kriging", "MGP", "SGP"]:
1✔
381
            if self.is_continuous:
1✔
382
                X, y = self._compute_pls(X.copy(), y.copy())
1✔
383

384
        self._check_param()
1✔
385
        self.X_train = X
1✔
386
        self.is_acting_train = is_acting
1✔
387
        self._corr_params = None
1✔
388
        _, self.cat_features = compute_X_cont(self.X_train, self.design_space)
1✔
389
        D = None  # For SGP, D is not computed at all
1✔
390
        # Center and scale X and y
391
        (
1✔
392
            self.X_norma,
393
            self.y_norma,
394
            self.X_offset,
395
            self.y_mean,
396
            self.X_scale,
397
            self.y_std,
398
        ) = standardization(X.copy(), y.copy())
399

400
        if not self.options["eval_noise"]:
1✔
401
            self.optimal_noise = np.array(self.options["noise0"])
1✔
402
        elif self.options["use_het_noise"]:
1✔
403
            # hetGP works with unique design variables when noise variance are not given
404
            (
1✔
405
                self.X_norma,
406
                index_unique,
407
                nt_reps,
408
            ) = np.unique(self.X_norma, return_inverse=True, return_counts=True, axis=0)
409
            self.nt = self.X_norma.shape[0]
1✔
410

411
            # computing the mean of the output per unique design variable (see Binois et al., 2018)
412
            y_norma_unique = []
1✔
413
            for i in range(self.nt):
1✔
414
                y_norma_unique.append(np.mean(self.y_norma[index_unique == i]))
1✔
415
            # pointwise sensible estimates of the noise variances (see Ankenman et al., 2010)
416
            self.optimal_noise = self.options["noise0"] * np.ones(self.nt)
1✔
417
            for i in range(self.nt):
1✔
418
                diff = self.y_norma[index_unique == i] - y_norma_unique[i]
1✔
419
                if np.sum(diff**2) != 0.0:
1✔
420
                    self.optimal_noise[i] = np.std(diff, ddof=1) ** 2
1✔
421
            self.optimal_noise = self.optimal_noise / nt_reps
1✔
422
            self.y_norma = y_norma_unique
1✔
423

424
        if not (self.is_continuous):
1✔
425
            D, self.ij, X_cont = gower_componentwise_distances(
1✔
426
                X=X,
427
                x_is_acting=is_acting,
428
                design_space=self.design_space,
429
                hierarchical_kernel=self.options["hierarchical_kernel"],
430
            )
431
            self.Lij, self.n_levels = cross_levels(
1✔
432
                X=self.X_train, ij=self.ij, design_space=self.design_space
433
            )
434
            listcatdecreed = self.design_space.is_conditionally_acting[
1✔
435
                self.cat_features
436
            ]
437
            if np.any(listcatdecreed):
1✔
438
                D = self._correct_distances_cat_decreed(
1✔
439
                    D,
440
                    is_acting,
441
                    listcatdecreed,
442
                    self.ij,
443
                    mixint_type=MixIntKernelType.GOWER,
444
                )
445
            if self.options["categorical_kernel"] == MixIntKernelType.CONT_RELAX:
1✔
446
                X2, _ = self.design_space.unfold_x(X)
1✔
447
                (
1✔
448
                    self.X2_norma,
449
                    _,
450
                    self.X2_offset,
451
                    _,
452
                    self.X2_scale,
453
                    _,
454
                ) = standardization(X2.copy(), y.copy())
455
                D, _ = cross_distances(self.X2_norma)
1✔
456
                self.Lij, self.n_levels = cross_levels(
1✔
457
                    X=self.X_train, ij=self.ij, design_space=self.design_space
458
                )
459
                listcatdecreed = self.design_space.is_conditionally_acting[
1✔
460
                    self.cat_features
461
                ]
462
                if np.any(listcatdecreed):
1✔
463
                    D = self._correct_distances_cat_decreed(
1✔
464
                        D,
465
                        is_acting,
466
                        listcatdecreed,
467
                        self.ij,
468
                        mixint_type=MixIntKernelType.CONT_RELAX,
469
                    )
470

471
            # Center and scale X_cont and y
472
            (
1✔
473
                self.X_norma,
474
                self.y_norma,
475
                self.X_offset,
476
                self.y_mean,
477
                self.X_scale,
478
                self.y_std,
479
            ) = standardization(X_cont.copy(), y.copy())
480

481
        if self.name not in ["SGP"]:
1✔
482
            if self.is_continuous:
1✔
483
                # Calculate matrix of distances D between samples
484
                D, self.ij = cross_distances(self.X_norma)
1✔
485

486
            if np.min(np.sum(np.abs(D), axis=1)) == 0.0:
1✔
487
                warnings.warn(
1✔
488
                    "Warning: multiple x input features have the same value (at least same row twice)."
489
                )
490

491
        ####
492
        # Regression matrix and parameters
493
        self.F = self._regression_types[self.options["poly"]](self.X_norma)
1✔
494
        n_samples_F = self.F.shape[0]
1✔
495
        if self.F.ndim > 1:
1✔
496
            p = self.F.shape[1]
1✔
497
        else:
498
            p = 1
×
499
        self._check_F(n_samples_F, p)
1✔
500

501
        # Optimization
502
        (
1✔
503
            self.optimal_rlf_value,
504
            self.optimal_par,
505
            self.optimal_theta,
506
        ) = self._optimize_hyperparam(D)
507
        if self.name in ["MGP"]:
1✔
508
            self._specific_train()
1✔
509
        elif self.name in ["SGP"] and not self.options["use_het_noise"]:
1✔
510
            if self.options["eval_noise"]:
1✔
511
                self.optimal_noise = self.optimal_theta[-1]
1✔
512
                self.optimal_sigma2 = self.optimal_theta[-2]
1✔
513
                self.optimal_theta = self.optimal_theta[:-2]
1✔
514
            else:
515
                self.optimal_sigma2 = self.optimal_theta[-1]
×
516
                self.optimal_theta = self.optimal_theta[:-1]
×
517
        else:
518
            if self.options["eval_noise"] and not self.options["use_het_noise"]:
1✔
519
                self.optimal_noise = self.optimal_theta[-1]
1✔
520
                self.optimal_theta = self.optimal_theta[:-1]
1✔
521
        # if self.name != "MGP":
522
        #     del self.y_norma, self.D
523

524
    def is_training_ill_conditioned(self):
1✔
525
        """
526
        Check if the training dataset could be an issue and print both
527
        the dataset correlation matrix condition number and
528
        minimal distance between two points.
529
        ----
530
        Returns true if R is ill_conditionned
531
        """
532
        R = self.optimal_par["C"] @ self.optimal_par["C"]
1✔
533
        condR = np.linalg.cond(R)
1✔
534
        print(
1✔
535
            "Minimal distance between two points in any dimension is",
536
            "{:.2e}".format(np.min(self.D)),
537
        )
538
        print(
1✔
539
            "Correlation matrix R condition number is",
540
            "{:.2e}".format(condR),
541
        )
542
        return (
1✔
543
            linalg.svd(R, compute_uv=False)[-1]
544
            < (1.5 * 100.0 * np.finfo(np.double).eps)
545
            and condR > 1e9
546
        )
547

548
    def _train(self):
1✔
549
        """
550
        Train the model
551
        """
552
        # outputs['sol'] = self.sol
553

554
        self._new_train()
1✔
555

556
    def _initialize_theta(self, theta, n_levels, cat_features, cat_kernel):
1✔
557
        self.n_levels_origin = n_levels
1✔
558
        if self._corr_params is not None:
1✔
559
            return self._corr_params
1✔
560
        nx = self.nx
1✔
561
        try:
1✔
562
            cat_kernel_comps = self.options["cat_kernel_comps"]
1✔
563
            if cat_kernel_comps is not None:
1✔
564
                n_levels = np.array(cat_kernel_comps)
1✔
565
        except KeyError:
1✔
566
            cat_kernel_comps = None
1✔
567
        try:
1✔
568
            ncomp = self.options["n_comp"]
1✔
569
            try:
1✔
570
                self.pls_coeff_cont
1✔
571
            except AttributeError:
1✔
572
                self.pls_coeff_cont = []
1✔
573
        except KeyError:
1✔
574
            cat_kernel_comps = None
1✔
575
            ncomp = 1e5
1✔
576

577
        theta_cont_features = np.zeros((len(theta), 1), dtype=bool)
1✔
578
        theta_cat_features = np.ones((len(theta), len(n_levels)), dtype=bool)
1✔
579
        if cat_kernel in [
1✔
580
            MixIntKernelType.EXP_HOMO_HSPHERE,
581
            MixIntKernelType.HOMO_HSPHERE,
582
        ]:
583
            theta_cat_features = np.zeros((len(theta), len(n_levels)), dtype=bool)
1✔
584
        i = 0
1✔
585
        j = 0
1✔
586
        n_theta_cont = 0
1✔
587
        for feat in cat_features:
1✔
588
            if feat:
1✔
589
                if cat_kernel in [
1✔
590
                    MixIntKernelType.EXP_HOMO_HSPHERE,
591
                    MixIntKernelType.HOMO_HSPHERE,
592
                ]:
593
                    theta_cat_features[
1✔
594
                        j : j + int(n_levels[i] * (n_levels[i] - 1) / 2), i
595
                    ] = [True] * int(n_levels[i] * (n_levels[i] - 1) / 2)
596
                    j += int(n_levels[i] * (n_levels[i] - 1) / 2)
1✔
597
                i += 1
1✔
598
            else:
599
                if n_theta_cont < ncomp:
1✔
600
                    theta_cont_features[j] = True
1✔
601
                    theta_cat_features[j] = False
1✔
602
                    j += 1
1✔
603
                    n_theta_cont += 1
1✔
604

605
        theta_cat_features = (
1✔
606
            [
607
                np.where(theta_cat_features[:, i_lvl])[0]
608
                for i_lvl in range(len(n_levels))
609
            ],
610
            np.any(theta_cat_features, axis=1) if len(n_levels) > 0 else None,
611
        )
612

613
        self._corr_params = params = (
1✔
614
            cat_kernel_comps,
615
            ncomp,
616
            theta_cat_features,
617
            theta_cont_features,
618
            nx,
619
            n_levels,
620
        )
621
        return params
1✔
622

623
    def _matrix_data_corr(
1✔
624
        self,
625
        corr,
626
        design_space,
627
        power,
628
        theta,
629
        theta_bounds,
630
        dx,
631
        Lij,
632
        n_levels,
633
        cat_features,
634
        cat_kernel,
635
        x=None,
636
    ):
637
        """
638
        matrix kernel correlation model.
639

640
        Parameters
641
        ----------
642
        corr: correlation_types
643
            - The autocorrelation model
644
        design_space: BaseDesignSpace
645
            - The design space definition
646
        theta : list[small_d * n_comp]
647
            Hyperparameters of the correlation model
648
        dx: np.ndarray[n_obs * (n_obs - 1) / 2, n_comp]
649
            - The gower_componentwise_distances between the samples.
650
        Lij: np.ndarray [n_obs * (n_obs - 1) / 2, 2]
651
                - The levels corresponding to the indices i and j of the vectors in X.
652
        n_levels: np.ndarray
653
                - The number of levels for every categorical variable.
654
        cat_features: np.ndarray [dim]
655
            -  Indices of the categorical input dimensions.
656
         cat_kernel : string
657
             - The kernel to use for categorical inputs. Only for non continuous Kriging",
658
        x : np.ndarray[n_obs , n_comp]
659
            - The input instead of dx for homo_hs prediction
660
        Returns
661
        -------
662
        r: np.ndarray[n_obs * (n_obs - 1) / 2,1]
663
            An array containing the values of the autocorrelation model.
664
        """
665

666
        _correlation_types = {
1✔
667
            "pow_exp": pow_exp,
668
            "abs_exp": abs_exp,
669
            "squar_exp": squar_exp,
670
            "squar_sin_exp": squar_sin_exp,
671
            "act_exp": act_exp,
672
            "matern52": matern52,
673
            "matern32": matern32,
674
        }
675

676
        # Initialize static parameters
677
        (
1✔
678
            cat_kernel_comps,
679
            ncomp,
680
            theta_cat_features,
681
            theta_cont_features,
682
            nx,
683
            n_levels,
684
        ) = self._initialize_theta(theta, n_levels, cat_features, cat_kernel)
685

686
        # Sampling points X and y
687
        if "MFK" in self.name:
1✔
688
            if self._lvl < self.nlvl - 1:
1✔
689
                X = self.training_points[self._lvl][0][0]
1✔
690
                y = self.training_points[self._lvl][0][1]
1✔
691
            elif self._lvl == self.nlvl - 1:
1✔
692
                X = self.training_points[None][0][0]
1✔
693
                y = self.training_points[None][0][1]
1✔
694
        else:
695
            X = self.training_points[None][0][0]
1✔
696
            y = self.training_points[None][0][1]
1✔
697

698
        if cat_kernel == MixIntKernelType.CONT_RELAX:
1✔
699
            X_pls_space, _ = design_space.unfold_x(X)
1✔
700
            nx = len(theta)
1✔
701

702
        elif cat_kernel == MixIntKernelType.GOWER:
1✔
703
            X_pls_space = np.copy(X)
1✔
704
        else:
705
            X_pls_space, _ = compute_X_cont(X, design_space)
1✔
706
        if cat_kernel_comps is not None or ncomp < 1e5:
1✔
707
            if np.size(self.pls_coeff_cont) == 0:
1✔
708
                X, y = self._compute_pls(X_pls_space.copy(), y.copy())
1✔
709
                self.pls_coeff_cont = self.coeff_pls
1✔
710
            if cat_kernel in [MixIntKernelType.GOWER, MixIntKernelType.CONT_RELAX]:
1✔
711
                d = componentwise_distance_PLS(
1✔
712
                    dx,
713
                    corr,
714
                    self.options["n_comp"],
715
                    self.pls_coeff_cont,
716
                    power,
717
                    theta=None,
718
                    return_derivative=False,
719
                )
720
                r = _correlation_types[corr](theta, d)
1✔
721
                return r
1✔
722
            else:
723
                d_cont = componentwise_distance_PLS(
1✔
724
                    dx[:, np.logical_not(cat_features)],
725
                    corr,
726
                    self.options["n_comp"],
727
                    self.pls_coeff_cont,
728
                    power,
729
                    theta=None,
730
                    return_derivative=False,
731
                )
732
        else:
733
            d = componentwise_distance(
1✔
734
                dx,
735
                corr,
736
                nx,
737
                power,
738
                theta=None,
739
                return_derivative=False,
740
            )
741
            if cat_kernel in [MixIntKernelType.GOWER, MixIntKernelType.CONT_RELAX]:
1✔
742
                r = _correlation_types[corr](theta, d)
1✔
743
                return r
1✔
744
            else:
745
                d_cont = d[:, np.logical_not(cat_features)]
1✔
746
        if self.options["corr"] == "squar_sin_exp":
1✔
747
            if self.options["categorical_kernel"] != MixIntKernelType.GOWER:
1✔
748
                theta_cont_features[-len([self.design_space.is_cat_mask]) :] = (
1✔
749
                    np.atleast_2d(
750
                        np.array([True] * len([self.design_space.is_cat_mask]))
751
                    ).T
752
                )
753
                theta_cat_features[1][-len([self.design_space.is_cat_mask]) :] = (
1✔
754
                    np.atleast_2d(
755
                        np.array([False] * len([self.design_space.is_cat_mask]))
756
                    ).T
757
                )
758

759
        theta_cont = theta[theta_cont_features[:, 0]]
1✔
760
        r_cont = _correlation_types[corr](theta_cont, d_cont)
1✔
761
        r_cat = np.copy(r_cont) * 0
1✔
762
        r = np.copy(r_cont)
1✔
763
        ##Theta_cat_i loop
764
        try:
1✔
765
            self.coeff_pls_cat
1✔
766
        except AttributeError:
1✔
767
            self.coeff_pls_cat = []
1✔
768

769
        theta_cat_kernel = theta
1✔
770
        if len(n_levels) > 0:
1✔
771
            theta_cat_kernel = theta.copy()
1✔
772
            if cat_kernel == MixIntKernelType.EXP_HOMO_HSPHERE:
1✔
773
                theta_cat_kernel[theta_cat_features[1]] *= 0.5 * np.pi / theta_bounds[1]
1✔
774
            elif cat_kernel == MixIntKernelType.HOMO_HSPHERE:
1✔
775
                theta_cat_kernel[theta_cat_features[1]] *= 2.0 * np.pi / theta_bounds[1]
1✔
776
            elif cat_kernel == MixIntKernelType.COMPOUND_SYMMETRY:
1✔
777
                theta_cat_kernel[theta_cat_features[1]] *= 2.0
1✔
778
                theta_cat_kernel[theta_cat_features[1]] -= (
1✔
779
                    theta_bounds[1] + theta_bounds[0]
780
                )
781
                theta_cat_kernel[theta_cat_features[1]] *= 1 / (
1✔
782
                    1.000000000001 * theta_bounds[1]
783
                )
784

785
        for i in range(len(n_levels)):
1✔
786
            theta_cat = theta_cat_kernel[theta_cat_features[0][i]]
1✔
787
            if cat_kernel == MixIntKernelType.COMPOUND_SYMMETRY:
1✔
788
                T = np.zeros((n_levels[i], n_levels[i]))
1✔
789
                for tij in range(n_levels[i]):
1✔
790
                    for tji in range(n_levels[i]):
1✔
791
                        if tij == tji:
1✔
792
                            T[tij, tji] = 1
1✔
793
                        else:
794
                            T[tij, tji] = max(
1✔
795
                                theta_cat[0], 1e-10 - 1 / (n_levels[i] - 1)
796
                            )
797
            else:
798
                T = matrix_data_corr_levels_cat_matrix(
1✔
799
                    i,
800
                    n_levels,
801
                    theta_cat,
802
                    theta_bounds,
803
                    is_ehh=cat_kernel == MixIntKernelType.EXP_HOMO_HSPHERE,
804
                )
805

806
            if cat_kernel_comps is not None:
1✔
807
                # Sampling points X and y
808
                X = self.training_points[None][0][0]
1✔
809
                y = self.training_points[None][0][1]
1✔
810
                X_icat = X[:, cat_features]
1✔
811
                X_icat = X_icat[:, i]
1✔
812
                old_n_comp = (
1✔
813
                    self.options["n_comp"] if "n_comp" in self.options else None
814
                )
815
                self.options["n_comp"] = int(n_levels[i] / 2 * (n_levels[i] - 1))
1✔
816
                X_full_space = compute_X_cross(X_icat, self.n_levels_origin[i])
1✔
817
                try:
1✔
818
                    self.coeff_pls = self.coeff_pls_cat[i]
1✔
819
                except IndexError:
1✔
820
                    _, _ = self._compute_pls(X_full_space.copy(), y.copy())
1✔
821
                    self.coeff_pls_cat.append(self.coeff_pls)
1✔
822

823
                if x is not None:
1✔
824
                    x_icat = x[:, cat_features]
1✔
825
                    x_icat = x_icat[:, i]
1✔
826
                    x_full_space = compute_X_cross(x_icat, self.n_levels_origin[i])
1✔
827
                    dx_cat_i = cross_levels_homo_space(
1✔
828
                        x_full_space, self.ij, y=X_full_space
829
                    )
830
                else:
831
                    dx_cat_i = cross_levels_homo_space(X_full_space, self.ij)
1✔
832

833
                d_cat_i = componentwise_distance_PLS(
1✔
834
                    dx_cat_i,
835
                    "squar_exp",
836
                    self.options["n_comp"],
837
                    self.coeff_pls,
838
                    power=self.options["pow_exp_power"],
839
                    theta=None,
840
                    return_derivative=False,
841
                )
842

843
                matrix_data_corr_levels_cat_mod_comps(
1✔
844
                    i,
845
                    Lij,
846
                    r_cat,
847
                    n_levels,
848
                    T,
849
                    d_cat_i,
850
                    has_cat_kernel=cat_kernel
851
                    in [
852
                        MixIntKernelType.EXP_HOMO_HSPHERE,
853
                        MixIntKernelType.HOMO_HSPHERE,
854
                    ],
855
                )
856
            else:
857
                matrix_data_corr_levels_cat_mod(
1✔
858
                    i,
859
                    Lij,
860
                    r_cat,
861
                    T,
862
                    has_cat_kernel=cat_kernel
863
                    in [
864
                        MixIntKernelType.EXP_HOMO_HSPHERE,
865
                        MixIntKernelType.HOMO_HSPHERE,
866
                        MixIntKernelType.COMPOUND_SYMMETRY,
867
                    ],
868
                )
869

870
            r = np.multiply(r, r_cat)
1✔
871
            if cat_kernel_comps is not None:
1✔
872
                if old_n_comp is None:
1✔
873
                    self.options._dict.pop("n_comp", None)
×
874
                else:
875
                    self.options["n_comp"] = old_n_comp
1✔
876
        return r
1✔
877

878
    def _reduced_likelihood_function(self, theta):
1✔
879
        """
880
        This function determines the BLUP parameters and evaluates the reduced
881
        likelihood function for the given autocorrelation parameters theta.
882
        Maximizing this function wrt the autocorrelation parameters theta is
883
        equivalent to maximizing the likelihood of the assumed joint Gaussian
884
        distribution of the observations y evaluated onto the design of
885
        experiments X.
886

887
        Parameters
888
        ----------
889
        theta: list(n_comp), optional
890
            - An array containing the autocorrelation parameters at which the
891
              Gaussian Process model parameters should be determined.
892

893
        Returns
894
        -------
895
        reduced_likelihood_function_value: real
896
            - The value of the reduced likelihood function associated to the
897
              given autocorrelation parameters theta.
898
        par: dict()
899
            - A dictionary containing the requested Gaussian Process model
900
              parameters:
901
            sigma2
902
            Gaussian Process variance.
903
            beta
904
            Generalized least-squares regression weights for
905
            Universal Kriging or for Ordinary Kriging.
906
            gamma
907
            Gaussian Process weights.
908
            C
909
            Cholesky decomposition of the correlation matrix [R].
910
            Ft
911
            Solution of the linear equation system : [R] x Ft = F
912
            Q, G
913
            QR decomposition of the matrix Ft.
914
        """
915
        # Initialize output
916

917
        reduced_likelihood_function_value = -np.inf
1✔
918
        par = {}
1✔
919
        # Set up R
920
        nugget = self.options["nugget"]
1✔
921
        if self.options["eval_noise"]:
1✔
922
            nugget = 0
1✔
923

924
        noise = self.noise0
1✔
925
        tmp_var = theta
1✔
926
        if self.options["use_het_noise"]:
1✔
927
            noise = self.optimal_noise
1✔
928
        if self.options["eval_noise"] and not self.options["use_het_noise"]:
1✔
929
            theta = tmp_var[0:-1]
1✔
930
            noise = tmp_var[-1]
1✔
931
        if not (self.is_continuous):
1✔
932
            dx = self.D
1✔
933
            if self.options["categorical_kernel"] == MixIntKernelType.CONT_RELAX:
1✔
934
                if "MFK" in self.name:
1✔
935
                    if (
1✔
936
                        self._lvl == self.nlvl - 1
937
                    ):  # highest fidelity identified by the key None
938
                        X2, _ = self.design_space.unfold_x(
1✔
939
                            self.training_points[None][0][0]
940
                        )
941
                        self.X2_norma[str(self._lvl)] = (
1✔
942
                            X2 - self.X2_offset
943
                        ) / self.X2_scale
944
                        dx, _ = cross_distances(self.X2_norma[str(self._lvl)])
1✔
945
                    elif self._lvl < self.nlvl - 1:
1✔
946
                        X2, _ = self.design_space.unfold_x(
1✔
947
                            self.training_points[self._lvl][0][0]
948
                        )
949
                        self.X2_norma[str(self._lvl)] = (
1✔
950
                            X2 - self.X2_offset
951
                        ) / self.X2_scale
952
                        dx, _ = cross_distances(self.X2_norma[str(self._lvl)])
1✔
953
                else:
954
                    X2, _ = self.design_space.unfold_x(self.training_points[None][0][0])
1✔
955
                    (
1✔
956
                        self.X2_norma,
957
                        _,
958
                        self.X2_offset,
959
                        _,
960
                        self.X2_scale,
961
                        _,
962
                    ) = standardization(X2, self.training_points[None][0][1])
963
                    dx, _ = cross_distances(self.X2_norma)
1✔
964

965
            r = self._matrix_data_corr(
1✔
966
                corr=self.options["corr"],
967
                design_space=self.design_space,
968
                power=self.options["pow_exp_power"],
969
                theta=theta,
970
                theta_bounds=self.options["theta_bounds"],
971
                dx=dx,
972
                Lij=self.Lij,
973
                n_levels=self.n_levels,
974
                cat_features=self.cat_features,
975
                cat_kernel=self.options["categorical_kernel"],
976
            ).reshape(-1, 1)
977
        else:
978
            r = self._correlation_types[self.options["corr"]](theta, self.D).reshape(
1✔
979
                -1, 1
980
            )
981
        R = np.eye(self.nt) * (1.0 + nugget + noise)
1✔
982
        R[self.ij[:, 0], self.ij[:, 1]] = r[:, 0]
1✔
983
        R[self.ij[:, 1], self.ij[:, 0]] = r[:, 0]
1✔
984
        # Cholesky decomposition of R
985

986
        try:
1✔
987
            C = linalg.cholesky(R, lower=True)
1✔
988
        except (linalg.LinAlgError, ValueError) as e:
1✔
989
            print("exception : ", e)
1✔
990
            print(np.linalg.eig(R)[0])
1✔
991
            return reduced_likelihood_function_value, par
1✔
992

993
        # Get generalized least squared solution
994
        Ft = linalg.solve_triangular(C, self.F, lower=True)
1✔
995
        Q, G = linalg.qr(Ft, mode="economic")
1✔
996
        sv = linalg.svd(G, compute_uv=False)
1✔
997
        rcondG = sv[-1] / sv[0]
1✔
998
        if rcondG < 1e-10:
1✔
999
            # Check F
1000
            sv = linalg.svd(self.F, compute_uv=False)
1✔
1001
            condF = sv[0] / sv[-1]
1✔
1002
            if condF > 1e15:
1✔
1003
                raise Exception(
×
1004
                    "F is too ill conditioned. Poor combination "
1005
                    "of regression model and observations."
1006
                )
1007

1008
            else:
1009
                # Ft is too ill conditioned, get out (try different theta)
1010
                return reduced_likelihood_function_value, par
1✔
1011

1012
        Yt = linalg.solve_triangular(C, self.y_norma, lower=True)
1✔
1013
        beta = linalg.solve_triangular(G, np.dot(Q.T, Yt))
1✔
1014
        rho = Yt - np.dot(Ft, beta)
1✔
1015

1016
        # The determinant of R is equal to the squared product of the diagonal
1017
        # elements of its Cholesky decomposition C
1018
        detR = (np.diag(C) ** (2.0 / self.nt)).prod()
1✔
1019
        # Compute/Organize output
1020
        p = 0
1✔
1021
        q = 0
1✔
1022
        if self.name in ["MFK", "MFKPLS", "MFKPLSK"]:
1✔
1023
            p = self.p
1✔
1024
            q = self.q
1✔
1025
        sigma2 = (rho**2.0).sum(axis=0) / (self.nt - p - q)
1✔
1026
        reduced_likelihood_function_value = -(self.nt - p - q) * np.log10(
1✔
1027
            sigma2.sum()
1028
        ) - self.nt * np.log10(detR)
1029
        par["sigma2"] = sigma2 * self.y_std**2.0
1✔
1030
        par["beta"] = beta
1✔
1031
        par["gamma"] = linalg.solve_triangular(C.T, rho)
1✔
1032
        par["C"] = C
1✔
1033
        par["Ft"] = Ft
1✔
1034
        par["G"] = G
1✔
1035
        par["Q"] = Q
1✔
1036

1037
        if self.name in ["MGP"]:
1✔
1038
            reduced_likelihood_function_value += self._reduced_log_prior(theta)
1✔
1039

1040
        # A particular case when f_min_cobyla fail
1041
        if (self.best_iteration_fail is not None) and (
1✔
1042
            not np.isinf(reduced_likelihood_function_value)
1043
        ):
1044
            if reduced_likelihood_function_value > self.best_iteration_fail:
1✔
1045
                self.best_iteration_fail = reduced_likelihood_function_value
1✔
1046
                self._thetaMemory = np.array(tmp_var)
1✔
1047

1048
        elif (self.best_iteration_fail is None) and (
1✔
1049
            not np.isinf(reduced_likelihood_function_value)
1050
        ):
1051
            self.best_iteration_fail = reduced_likelihood_function_value
1✔
1052
            self._thetaMemory = np.array(tmp_var)
1✔
1053
        if reduced_likelihood_function_value > 1e15:
1✔
1054
            reduced_likelihood_function_value = 1e15
×
1055
        return reduced_likelihood_function_value, par
1✔
1056

1057
    def _reduced_likelihood_gradient(self, theta):
1✔
1058
        """
1059
        Evaluates the reduced_likelihood_gradient at a set of hyperparameters.
1060

1061
        Parameters
1062
        ---------
1063
        theta : list(n_comp), optional
1064
            - An array containing the autocorrelation parameters at which the
1065
              Gaussian Process model parameters should be determined.
1066

1067
        Returns
1068
        -------
1069
        grad_red : np.ndarray (dim,1)
1070
            Derivative of the reduced_likelihood
1071
        par: dict()
1072
            - A dictionary containing the requested Gaussian Process model
1073
              parameters:
1074
            sigma2
1075
            Gaussian Process variance.
1076
            beta
1077
            Generalized least-squares regression weights for
1078
            Universal Kriging or for Ordinary Kriging.
1079
            gamma
1080
            Gaussian Process weights.
1081
            C
1082
            Cholesky decomposition of the correlation matrix [R].
1083
            Ft
1084
            Solution of the linear equation system : [R] x Ft = F
1085
            Q, G
1086
            QR decomposition of the matrix Ft.
1087
            dr
1088
            List of all the correlation matrix derivative
1089
            tr
1090
            List of all the trace part in the reduce likelihood derivatives
1091
            dmu
1092
            List of all the mean derivatives
1093
            arg
1094
            List of all minus_Cinv_dRdomega_gamma
1095
            dsigma
1096
            List of all sigma derivatives
1097
        """
1098
        red, par = self._reduced_likelihood_function(theta)
1✔
1099

1100
        C = par["C"]
1✔
1101
        gamma = par["gamma"]
1✔
1102
        Q = par["Q"]
1✔
1103
        G = par["G"]
1✔
1104
        sigma_2 = par["sigma2"] + self.options["nugget"]
1✔
1105

1106
        nb_theta = len(theta)
1✔
1107
        grad_red = np.zeros(nb_theta)
1✔
1108

1109
        dr_all = []
1✔
1110
        tr_all = []
1✔
1111
        dmu_all = []
1✔
1112
        arg_all = []
1✔
1113
        dsigma_all = []
1✔
1114
        dbeta_all = []
1✔
1115
        for i_der in range(nb_theta):
1✔
1116
            # Compute R derivatives
1117
            dr = self._correlation_types[self.options["corr"]](
1✔
1118
                theta, self.D, grad_ind=i_der
1119
            )
1120

1121
            dr_all.append(dr)
1✔
1122

1123
            dR = np.zeros((self.nt, self.nt))
1✔
1124
            dR[self.ij[:, 0], self.ij[:, 1]] = dr[:, 0]
1✔
1125
            dR[self.ij[:, 1], self.ij[:, 0]] = dr[:, 0]
1✔
1126

1127
            # Compute beta derivatives
1128
            Cinv_dR_gamma = linalg.solve_triangular(C, np.dot(dR, gamma), lower=True)
1✔
1129
            dbeta = -linalg.solve_triangular(G, np.dot(Q.T, Cinv_dR_gamma))
1✔
1130
            arg_all.append(Cinv_dR_gamma)
1✔
1131

1132
            dbeta_all.append(dbeta)
1✔
1133

1134
            # Compute mu derivatives
1135
            dmu = np.dot(self.F, dbeta)
1✔
1136
            dmu_all.append(dmu)
1✔
1137

1138
            # Compute log(detR) derivatives
1139
            tr_1 = linalg.solve_triangular(C, dR, lower=True)
1✔
1140
            tr = linalg.solve_triangular(C.T, tr_1)
1✔
1141
            tr_all.append(tr)
1✔
1142

1143
            # Compute Sigma2 Derivatives
1144
            dsigma_2 = (
1✔
1145
                (1 / self.nt)
1146
                * (
1147
                    -dmu.T.dot(gamma)
1148
                    - gamma.T.dot(dmu)
1149
                    - np.dot(gamma.T, dR.dot(gamma))
1150
                )
1151
                * self.y_std**2.0
1152
            )
1153
            dsigma_all.append(dsigma_2)
1✔
1154

1155
            # Compute reduced log likelihood derivatives
1156
            grad_red[i_der] = (
1✔
1157
                -self.nt / np.log(10) * (dsigma_2 / sigma_2 + np.trace(tr) / self.nt)
1158
            ).item()
1159

1160
        par["dr"] = dr_all
1✔
1161
        par["tr"] = tr_all
1✔
1162
        par["dmu"] = dmu_all
1✔
1163
        par["arg"] = arg_all
1✔
1164
        par["dsigma"] = dsigma_all
1✔
1165
        par["dbeta_all"] = dbeta_all
1✔
1166

1167
        grad_red = np.atleast_2d(grad_red).T
1✔
1168

1169
        if self.name in ["MGP"]:
1✔
1170
            grad_red += self._reduced_log_prior(theta, grad=True)
1✔
1171
        return grad_red, par
1✔
1172

1173
    def _reduced_likelihood_hessian(self, theta):
1✔
1174
        """
1175
        Evaluates the reduced_likelihood_gradient at a set of hyperparameters.
1176

1177
        Parameters
1178
        ----------
1179
        theta : list(n_comp), optional
1180
            - An array containing the autocorrelation parameters at which the
1181
              Gaussian Process model parameters should be determined.
1182

1183
        Returns
1184
        -------
1185
        hess : np.ndarray
1186
            Hessian values.
1187
        hess_ij: np.ndarray [nb_theta * (nb_theta + 1) / 2, 2]
1188
            - The indices i and j of the vectors in theta associated to the hessian in hess.
1189
        par: dict()
1190
            - A dictionary containing the requested Gaussian Process model
1191
              parameters:
1192
            sigma2
1193
            Gaussian Process variance.
1194
            beta
1195
            Generalized least-squared regression weights for
1196
            Universal Kriging or for Ordinary Kriging.
1197
            gamma
1198
            Gaussian Process weights.
1199
            C
1200
            Cholesky decomposition of the correlation matrix [R].
1201
            Ft
1202
            Solution of the linear equation system : [R] x Ft = F
1203
            Q, G
1204
            QR decomposition of the matrix Ft.
1205
            dr
1206
            List of all the correlation matrix derivative
1207
            tr
1208
            List of all the trace part in the reduce likelihood derivatives
1209
            dmu
1210
            List of all the mean derivatives
1211
            arg
1212
            List of all minus_Cinv_dRdomega_gamma
1213
            dsigma
1214
            List of all sigma derivatives
1215
        """
1216
        dred, par = self._reduced_likelihood_gradient(theta)
1✔
1217

1218
        C = par["C"]
1✔
1219
        gamma = par["gamma"]
1✔
1220
        Q = par["Q"]
1✔
1221
        G = par["G"]
1✔
1222
        sigma_2 = par["sigma2"]
1✔
1223

1224
        nb_theta = len(theta)
1✔
1225

1226
        dr_all = par["dr"]
1✔
1227
        tr_all = par["tr"]
1✔
1228
        dmu_all = par["dmu"]
1✔
1229
        arg_all = par["arg"]
1✔
1230
        dsigma = par["dsigma"]
1✔
1231
        Rinv_dRdomega_gamma_all = []
1✔
1232
        Rinv_dmudomega_all = []
1✔
1233

1234
        n_val_hess = nb_theta * (nb_theta + 1) // 2
1✔
1235
        hess_ij = np.zeros((n_val_hess, 2), dtype=np.int32)
1✔
1236
        hess = np.zeros((n_val_hess, 1))
1✔
1237
        ind_1 = 0
1✔
1238
        if self.name in ["MGP"]:
1✔
1239
            log_prior = self._reduced_log_prior(theta, hessian=True)
1✔
1240

1241
        for omega in range(nb_theta):
1✔
1242
            ind_0 = ind_1
1✔
1243
            ind_1 = ind_0 + nb_theta - omega
1✔
1244
            hess_ij[ind_0:ind_1, 0] = omega
1✔
1245
            hess_ij[ind_0:ind_1, 1] = np.arange(omega, nb_theta)
1✔
1246

1247
            dRdomega = np.zeros((self.nt, self.nt))
1✔
1248
            dRdomega[self.ij[:, 0], self.ij[:, 1]] = dr_all[omega][:, 0]
1✔
1249
            dRdomega[self.ij[:, 1], self.ij[:, 0]] = dr_all[omega][:, 0]
1✔
1250

1251
            dmudomega = dmu_all[omega]
1✔
1252
            Cinv_dmudomega = linalg.solve_triangular(C, dmudomega, lower=True)
1✔
1253
            Rinv_dmudomega = linalg.solve_triangular(C.T, Cinv_dmudomega)
1✔
1254
            Rinv_dmudomega_all.append(Rinv_dmudomega)
1✔
1255
            Rinv_dRdomega_gamma = linalg.solve_triangular(C.T, arg_all[omega])
1✔
1256
            Rinv_dRdomega_gamma_all.append(Rinv_dRdomega_gamma)
1✔
1257

1258
            for i, eta in enumerate(hess_ij[ind_0:ind_1, 1]):
1✔
1259
                dRdeta = np.zeros((self.nt, self.nt))
1✔
1260
                dRdeta[self.ij[:, 0], self.ij[:, 1]] = dr_all[eta][:, 0]
1✔
1261
                dRdeta[self.ij[:, 1], self.ij[:, 0]] = dr_all[eta][:, 0]
1✔
1262

1263
                dr_eta_omega = self._correlation_types[self.options["corr"]](
1✔
1264
                    theta, self.D, grad_ind=omega, hess_ind=eta
1265
                )
1266
                dRdetadomega = np.zeros((self.nt, self.nt))
1✔
1267
                dRdetadomega[self.ij[:, 0], self.ij[:, 1]] = dr_eta_omega[:, 0]
1✔
1268
                dRdetadomega[self.ij[:, 1], self.ij[:, 0]] = dr_eta_omega[:, 0]
1✔
1269

1270
                # Compute beta second derivatives
1271
                dRdeta_Rinv_dmudomega = np.dot(dRdeta, Rinv_dmudomega)
1✔
1272

1273
                dmudeta = dmu_all[eta]
1✔
1274
                Cinv_dmudeta = linalg.solve_triangular(C, dmudeta, lower=True)
1✔
1275
                Rinv_dmudeta = linalg.solve_triangular(C.T, Cinv_dmudeta)
1✔
1276
                dRdomega_Rinv_dmudeta = np.dot(dRdomega, Rinv_dmudeta)
1✔
1277

1278
                dRdeta_Rinv_dRdomega_gamma = np.dot(dRdeta, Rinv_dRdomega_gamma)
1✔
1279

1280
                Rinv_dRdeta_gamma = linalg.solve_triangular(C.T, arg_all[eta])
1✔
1281
                dRdomega_Rinv_dRdeta_gamma = np.dot(dRdomega, Rinv_dRdeta_gamma)
1✔
1282

1283
                dRdetadomega_gamma = np.dot(dRdetadomega, gamma)
1✔
1284

1285
                beta_sum = (
1✔
1286
                    dRdeta_Rinv_dmudomega
1287
                    + dRdomega_Rinv_dmudeta
1288
                    + dRdeta_Rinv_dRdomega_gamma
1289
                    + dRdomega_Rinv_dRdeta_gamma
1290
                    - dRdetadomega_gamma
1291
                )
1292

1293
                Qt_Cinv_beta_sum = np.dot(
1✔
1294
                    Q.T, linalg.solve_triangular(C, beta_sum, lower=True)
1295
                )
1296
                dbetadetadomega = linalg.solve_triangular(G, Qt_Cinv_beta_sum)
1✔
1297

1298
                # Compute mu second derivatives
1299
                dmudetadomega = np.dot(self.F, dbetadetadomega)
1✔
1300

1301
                # Compute sigma2 second derivatives
1302
                sigma_arg_1 = (
1✔
1303
                    -np.dot(dmudetadomega.T, gamma)
1304
                    + np.dot(dmudomega.T, Rinv_dRdeta_gamma)
1305
                    + np.dot(dmudeta.T, Rinv_dRdomega_gamma)
1306
                )
1307

1308
                sigma_arg_2 = (
1✔
1309
                    -np.dot(gamma.T, dmudetadomega)
1310
                    + np.dot(gamma.T, dRdeta_Rinv_dmudomega)
1311
                    + np.dot(gamma.T, dRdomega_Rinv_dmudeta)
1312
                )
1313

1314
                sigma_arg_3 = np.dot(dmudeta.T, Rinv_dmudomega) + np.dot(
1✔
1315
                    dmudomega.T, Rinv_dmudeta
1316
                )
1317

1318
                sigma_arg_4_in = (
1✔
1319
                    -dRdetadomega_gamma
1320
                    + dRdeta_Rinv_dRdomega_gamma
1321
                    + dRdomega_Rinv_dRdeta_gamma
1322
                )
1323
                sigma_arg_4 = np.dot(gamma.T, sigma_arg_4_in)
1✔
1324

1325
                dsigma2detadomega = (
1✔
1326
                    (1 / self.nt)
1327
                    * (sigma_arg_1 + sigma_arg_2 + sigma_arg_3 + sigma_arg_4)
1328
                    * self.y_std**2.0
1329
                )
1330

1331
                # Compute Hessian
1332
                dreddetadomega_tr_1 = np.trace(np.dot(tr_all[eta], tr_all[omega]))
1✔
1333

1334
                dreddetadomega_tr_2 = np.trace(
1✔
1335
                    linalg.solve_triangular(
1336
                        C.T, linalg.solve_triangular(C, dRdetadomega, lower=True)
1337
                    )
1338
                )
1339

1340
                dreddetadomega_arg1 = (self.nt / sigma_2) * (
1✔
1341
                    dsigma2detadomega - (1 / sigma_2) * dsigma[omega] * dsigma[eta]
1342
                )
1343
                dreddetadomega = (
1✔
1344
                    -(dreddetadomega_arg1 - dreddetadomega_tr_1 + dreddetadomega_tr_2)
1345
                    / self.nt
1346
                )
1347

1348
                hess[ind_0 + i, 0] = (self.nt / np.log(10) * dreddetadomega).item()
1✔
1349

1350
                if self.name in ["MGP"] and eta == omega:
1✔
1351
                    hess[ind_0 + i, 0] += log_prior[eta].item()
1✔
1352
            par["Rinv_dR_gamma"] = Rinv_dRdomega_gamma_all
1✔
1353
            par["Rinv_dmu"] = Rinv_dmudomega_all
1✔
1354
        return hess, hess_ij, par
1✔
1355

1356
    def _predict_init(self, x, is_acting):
1✔
1357
        if not (self.is_continuous):
1✔
1358
            if is_acting is None:
1✔
1359
                x, is_acting = self.design_space.correct_get_acting(x)
1✔
1360
            n_eval, _ = x.shape
1✔
1361
            _, ij = cross_distances(x, self.X_train)
1✔
1362
            dx = gower_componentwise_distances(
1✔
1363
                x,
1364
                x_is_acting=is_acting,
1365
                design_space=self.design_space,
1366
                hierarchical_kernel=self.options["hierarchical_kernel"],
1367
                y=np.copy(self.X_train),
1368
                y_is_acting=self.is_acting_train,
1369
            )
1370
            listcatdecreed = self.design_space.is_conditionally_acting[
1✔
1371
                self.cat_features
1372
            ]
1373
            if np.any(listcatdecreed):
1✔
1374
                dx = self._correct_distances_cat_decreed(
1✔
1375
                    dx,
1376
                    is_acting,
1377
                    listcatdecreed,
1378
                    ij,
1379
                    is_acting_y=self.is_acting_train,
1380
                    mixint_type=MixIntKernelType.GOWER,
1381
                )
1382
            if self.options["categorical_kernel"] == MixIntKernelType.CONT_RELAX:
1✔
1383
                Xpred, _ = self.design_space.unfold_x(x)
1✔
1384
                Xpred_norma = (Xpred - self.X2_offset) / self.X2_scale
1✔
1385
                dx = differences(Xpred_norma, Y=self.X2_norma.copy())
1✔
1386
                listcatdecreed = self.design_space.is_conditionally_acting[
1✔
1387
                    self.cat_features
1388
                ]
1389
                if np.any(listcatdecreed):
1✔
1390
                    dx = self._correct_distances_cat_decreed(
1✔
1391
                        dx,
1392
                        is_acting,
1393
                        listcatdecreed,
1394
                        ij,
1395
                        is_acting_y=self.is_acting_train,
1396
                        mixint_type=MixIntKernelType.CONT_RELAX,
1397
                    )
1398

1399
            Lij, _ = cross_levels(
1✔
1400
                X=x, ij=ij, design_space=self.design_space, y=self.X_train
1401
            )
1402
            self.ij = ij
1✔
1403
        else:
1404
            n_eval, _ = x.shape
1✔
1405
            X_cont = (np.copy(x) - self.X_offset) / self.X_scale
1✔
1406
            dx = differences(X_cont, Y=self.X_norma.copy())
1✔
1407
            ij = 0
1✔
1408
            Lij = 0
1✔
1409
        return x, is_acting, n_eval, ij, Lij, dx
1✔
1410

1411
    def predict_values(self, x: np.ndarray, is_acting=None) -> np.ndarray:
1✔
1412
        """
1413
        Predict the output values at a set of points.
1414

1415
        Parameters
1416
        ----------
1417
        x : np.ndarray[nt, nx] or np.ndarray[nt]
1418
            Input values for the prediction points.
1419
        is_acting : np.ndarray[nt, nx] or np.ndarray[nt]
1420
            Matrix specifying for each design variable whether it is acting or not (for hierarchical design spaces)
1421

1422
        Returns
1423
        -------
1424
        y : np.ndarray[nt, ny]
1425
            Output values at the prediction points.
1426
        """
1427
        x = ensure_2d_array(x, "x")
1✔
1428
        self._check_xdim(x)
1✔
1429

1430
        if is_acting is not None:
1✔
1431
            is_acting = ensure_2d_array(is_acting, "is_acting")
1✔
1432
            if is_acting.shape != x.shape:
1✔
1433
                raise ValueError(
×
1434
                    f"is_acting should have the same dimensions as x: {is_acting.shape} != {x.shape}"
1435
                )
1436

1437
        n = x.shape[0]
1✔
1438
        x2 = np.copy(x)
1✔
1439
        self.printer.active = (
1✔
1440
            self.options["print_global"] and self.options["print_prediction"]
1441
        )
1442

1443
        if self.name == "MixExp":
1✔
1444
            # Mixture of experts model
1445
            self.printer._title("Evaluation of the Mixture of experts")
×
1446
        else:
1447
            self.printer._title("Evaluation")
1✔
1448
        self.printer("   %-12s : %i" % ("# eval points.", n))
1✔
1449
        self.printer()
1✔
1450

1451
        # Evaluate the unknown points using the specified model-method
1452
        with self.printer._timed_context("Predicting", key="prediction"):
1✔
1453
            y = self._predict_values(x2, is_acting=is_acting)
1✔
1454
        time_pt = self.printer._time("prediction")[-1] / n
1✔
1455
        self.printer()
1✔
1456
        self.printer("Prediction time/pt. (sec) : %10.7f" % time_pt)
1✔
1457
        self.printer()
1✔
1458
        return y.reshape((n, self.ny))
1✔
1459

1460
    def _predict_values(self, x: np.ndarray, is_acting=None) -> np.ndarray:
1✔
1461
        """
1462
        Evaluates the model at a set of points.
1463

1464
        Parameters
1465
        ----------
1466
        x : np.ndarray [n_evals, dim]
1467
            Evaluation point input variable values
1468
        is_acting : np.ndarray[nt, nx] or np.ndarray[nt]
1469
            Matrix specifying for each design variable whether it is acting or not (for hierarchical design spaces)
1470

1471
        Returns
1472
        -------
1473
        y : np.ndarray
1474
            Evaluation point output variable values
1475
        """
1476
        # Initialization
1477
        if not (self.is_continuous):
1✔
1478
            x, is_acting, n_eval, ij, Lij, dx = self._predict_init(x, is_acting)
1✔
1479

1480
            r = self._matrix_data_corr(
1✔
1481
                corr=self.options["corr"],
1482
                design_space=self.design_space,
1483
                power=self.options["pow_exp_power"],
1484
                theta=self.optimal_theta,
1485
                theta_bounds=self.options["theta_bounds"],
1486
                dx=dx,
1487
                Lij=Lij,
1488
                n_levels=self.n_levels,
1489
                cat_features=self.cat_features,
1490
                cat_kernel=self.options["categorical_kernel"],
1491
                x=x,
1492
            ).reshape(n_eval, self.nt)
1493

1494
            X_cont, _ = compute_X_cont(x, self.design_space)
1✔
1495

1496
        else:
1497
            _, _, n_eval, _, _, dx = self._predict_init(x, is_acting)
1✔
1498
            X_cont = np.copy(x)
1✔
1499
            d = self._componentwise_distance(dx)
1✔
1500
            # Compute the correlation function
1501
            r = self._correlation_types[self.options["corr"]](
1✔
1502
                self.optimal_theta, d
1503
            ).reshape(n_eval, self.nt)
1504
            y = np.zeros(n_eval)
1✔
1505
        X_cont = (X_cont - self.X_offset) / self.X_scale
1✔
1506
        # Compute the regression function
1507
        f = self._regression_types[self.options["poly"]](X_cont)
1✔
1508
        # Scaled predictor
1509
        y_ = np.dot(f, self.optimal_par["beta"]) + np.dot(r, self.optimal_par["gamma"])
1✔
1510
        # Predictor
1511
        y = (self.y_mean + self.y_std * y_).ravel()
1✔
1512
        return y
1✔
1513

1514
    def _predict_derivatives(self, x, kx):
1✔
1515
        """
1516
        Evaluates the derivatives at a set of points.
1517

1518
        Parameters
1519
        ---------
1520
        x : np.ndarray [n_evals, dim]
1521
            Evaluation point input variable values
1522
        kx : int
1523
            The 0-based index of the input variable with respect to which derivatives are desired.
1524

1525
        Returns
1526
        -------
1527
        y : np.ndarray
1528
            Derivative values.
1529
        """
1530
        # Initialization
1531
        n_eval, _ = x.shape
1✔
1532

1533
        x = (x - self.X_offset) / self.X_scale
1✔
1534
        # Get pairwise componentwise L1-distances to the input training set
1535

1536
        dx = differences(x, Y=self.X_norma.copy())
1✔
1537
        d = self._componentwise_distance(dx)
1✔
1538
        if self.options["corr"] == "squar_sin_exp":
1✔
1539
            dd = 0
1✔
1540
        else:
1541
            dd = self._componentwise_distance(
1✔
1542
                dx, theta=self.optimal_theta, return_derivative=True
1543
            )
1544

1545
        # Compute the correlation function
1546
        derivative_dic = {"dx": dx, "dd": dd}
1✔
1547

1548
        r, dr = self._correlation_types[self.options["corr"]](
1✔
1549
            self.optimal_theta, d, derivative_params=derivative_dic
1550
        )
1551
        r = r.reshape(n_eval, self.nt)
1✔
1552

1553
        drx = dr[:, kx].reshape(n_eval, self.nt)
1✔
1554

1555
        if self.options["poly"] == "constant":
1✔
1556
            df = np.zeros((1, self.nx))
1✔
1557
        elif self.options["poly"] == "linear":
1✔
1558
            df = np.zeros((self.nx + 1, self.nx))
1✔
1559
            df[1:, :] = np.eye(self.nx)
1✔
1560
        else:
1561
            raise ValueError(
×
1562
                "The derivative is only available for ordinary kriging or "
1563
                + "universal kriging using a linear trend"
1564
            )
1565
        # Beta and gamma = R^-1(y-FBeta)
1566
        beta = self.optimal_par["beta"]
1✔
1567
        gamma = self.optimal_par["gamma"]
1✔
1568
        df_dx = np.dot(df.T, beta)
1✔
1569

1570
        y = (df_dx[kx] + np.dot(drx, gamma)) * self.y_std / self.X_scale[kx]
1✔
1571
        return y
1✔
1572

1573
    def predict_variances(self, x: np.ndarray, is_acting=None) -> np.ndarray:
1✔
1574
        """
1575
        Predict the variances at a set of points.
1576

1577
        Parameters
1578
        ----------
1579
        x : np.ndarray[nt, nx] or np.ndarray[nt]
1580
            Input values for the prediction points.
1581
        is_acting : np.ndarray[nt, nx] or np.ndarray[nt]
1582
            Matrix specifying for each design variable whether it is acting or not (for hierarchical design spaces)
1583

1584
        Returns
1585
        -------
1586
        s2 : np.ndarray[nt, ny]
1587
            Variances.
1588
        """
1589
        check_support(self, "variances")
1✔
1590
        x = ensure_2d_array(x, "x")
1✔
1591
        self._check_xdim(x)
1✔
1592

1593
        if is_acting is not None:
1✔
1594
            is_acting = ensure_2d_array(is_acting, "is_acting")
1✔
1595
            if is_acting.shape != x.shape:
1✔
1596
                raise ValueError(
×
1597
                    f"is_acting should have the same dimensions as x: {is_acting.shape} != {x.shape}"
1598
                )
1599

1600
        n = x.shape[0]
1✔
1601
        x2 = np.copy(x)
1✔
1602
        s2 = self._predict_variances(x2, is_acting=is_acting)
1✔
1603
        return s2.reshape((n, self.ny))
1✔
1604

1605
    def _predict_variances(self, x: np.ndarray, is_acting=None) -> np.ndarray:
1✔
1606
        """
1607
        Provide uncertainty of the model at a set of points
1608
        Parameters
1609
        ----------
1610
        x : np.ndarray [n_evals, dim]
1611
            Evaluation point input variable values
1612
        is_acting : np.ndarray[nt, nx] or np.ndarray[nt]
1613
            Matrix specifying for each design variable whether it is acting or not (for hierarchical design spaces)
1614
        Returns
1615
        -------
1616
        MSE : np.ndarray
1617
            Evaluation point output variable MSE
1618
        """
1619
        # Initialization
1620
        if not (self.is_continuous):
1✔
1621
            x, is_acting, n_eval, ij, Lij, dx = self._predict_init(x, is_acting)
1✔
1622
            X_cont = x
1✔
1623

1624
            r = self._matrix_data_corr(
1✔
1625
                corr=self.options["corr"],
1626
                design_space=self.design_space,
1627
                power=self.options["pow_exp_power"],
1628
                theta=self.optimal_theta,
1629
                theta_bounds=self.options["theta_bounds"],
1630
                dx=dx,
1631
                Lij=Lij,
1632
                n_levels=self.n_levels,
1633
                cat_features=self.cat_features,
1634
                cat_kernel=self.options["categorical_kernel"],
1635
                x=x,
1636
            ).reshape(n_eval, self.nt)
1637

1638
            X_cont, _ = compute_X_cont(x, self.design_space)
1✔
1639
        else:
1640
            _, _, n_eval, _, _, dx = self._predict_init(x, is_acting)
1✔
1641
            X_cont = np.copy(x)
1✔
1642
            d = self._componentwise_distance(dx)
1✔
1643
            # Compute the correlation function
1644
            r = self._correlation_types[self.options["corr"]](
1✔
1645
                self.optimal_theta, d
1646
            ).reshape(n_eval, self.nt)
1647
        X_cont = (X_cont - self.X_offset) / self.X_scale
1✔
1648
        C = self.optimal_par["C"]
1✔
1649
        rt = linalg.solve_triangular(C, r.T, lower=True)
1✔
1650

1651
        u = linalg.solve_triangular(
1✔
1652
            self.optimal_par["G"].T,
1653
            np.dot(self.optimal_par["Ft"].T, rt)
1654
            - self._regression_types[self.options["poly"]](X_cont).T,
1655
        )
1656
        A = self.optimal_par["sigma2"]
1✔
1657
        B = 1.0 - (rt**2.0).sum(axis=0) + (u**2.0).sum(axis=0)
1✔
1658
        # machine precision: force to zero!
1659
        B[B < 1e-12] = 0
1✔
1660
        MSE = np.einsum("i,j -> ji", A, B)
1✔
1661
        # Mean Squared Error might be slightly negative depending on
1662
        # machine precision: force to zero!
1663
        MSE[MSE < 0.0] = 0.0
1✔
1664
        return MSE
1✔
1665

1666
    def _predict_variance_derivatives(self, x, kx):
1✔
1667
        """
1668
        Provide the derivatives of the variance of the model at a set of points
1669

1670
        Parameters
1671
        -----------
1672
        x : np.ndarray [n_evals, dim]
1673
            Evaluation point input variable values
1674
        kx : int
1675
            The 0-based index of the input variable with respect to which derivatives are desired.
1676

1677
        Returns
1678
        -------
1679
        derived_variance:  np.ndarray
1680
            The derivatives wrt kx-th component of the variance of the kriging model
1681
        """
1682
        return self._internal_predict_variance(x, kx)
1✔
1683

1684
    def _predict_variance_gradient(self, x):
1✔
1685
        """
1686
        Provide the gradient of the variance of the model at a given point
1687
        (ie the derivatives wrt to all component at a unique point x)
1688

1689
        Parameters
1690
        -----------
1691
        x : np.ndarray [1, dim]
1692
            Evaluation point input variable values
1693

1694
        Returns
1695
        -------
1696
         derived_variance:  np.ndarray
1697
            The gradient of the variance of the kriging model
1698
        """
1699
        return self._internal_predict_variance(x)
1✔
1700

1701
    def _internal_predict_variance(self, x, kx=None):
1✔
1702
        """
1703
        When kx is None gradient is computed at the location x
1704
        otherwise partial derivatives wrt kx-th component of a set of points x
1705
        """
1706

1707
        # Initialization
1708
        n_eval, _ = x.shape
1✔
1709
        x = (x - self.X_offset) / self.X_scale
1✔
1710
        theta = self.optimal_theta
1✔
1711
        # Get pairwise componentwise L1-distances to the input training set
1712
        dx = differences(x, Y=self.X_norma.copy())
1✔
1713
        d = self._componentwise_distance(dx)
1✔
1714
        if self.options["corr"] == "squar_sin_exp":
1✔
1715
            dd = 0
1✔
1716
        else:
1717
            dd = self._componentwise_distance(
1✔
1718
                dx, theta=self.optimal_theta, return_derivative=True
1719
            )
1720
        derivative_dic = {"dx": dx, "dd": dd}
1✔
1721

1722
        sigma2 = self.optimal_par["sigma2"]
1✔
1723
        C = self.optimal_par["C"]
1✔
1724

1725
        # p1 : derivative of (rt**2.0).sum(axis=0)
1726
        r, dr = self._correlation_types[self.options["corr"]](
1✔
1727
            theta, d, derivative_params=derivative_dic
1728
        )
1729
        if kx is None:
1✔
1730
            rt = linalg.solve_triangular(C, r, lower=True)
1✔
1731
            drx = dr.T
1✔
1732
        else:
1733
            r = r.reshape(n_eval, self.nt)
1✔
1734
            rt = linalg.solve_triangular(C, r.T, lower=True)
1✔
1735
            drx = dr[:, kx].reshape(n_eval, self.nt)
1✔
1736

1737
        invKr = linalg.solve_triangular(C.T, rt)
1✔
1738
        p1 = 2 * np.dot(drx, invKr).T
1✔
1739

1740
        # p2 : derivative of (u**2.0).sum(axis=0)
1741
        f_x = self._regression_types[self.options["poly"]](x).T
1✔
1742
        F = self.F
1✔
1743
        rho2 = linalg.solve_triangular(C, F, lower=True)
1✔
1744
        invKF = linalg.solve_triangular(C.T, rho2)
1✔
1745

1746
        if kx is None:
1✔
1747
            A = f_x.T - np.dot(r.T, invKF)
1✔
1748
        else:
1749
            A = f_x.T - np.dot(r, invKF)
1✔
1750

1751
        B = np.dot(F.T, invKF)
1✔
1752
        rho3 = linalg.cholesky(B, lower=True)
1✔
1753
        invBAt = linalg.solve_triangular(rho3, A.T, lower=True)
1✔
1754
        D = linalg.solve_triangular(rho3.T, invBAt)
1✔
1755

1756
        if self.options["poly"] == "constant":
1✔
1757
            df = np.zeros((1, self.nx))
1✔
1758
        elif self.options["poly"] == "linear":
1✔
1759
            df = np.zeros((self.nx + 1, self.nx))
1✔
1760
            df[1:, :] = np.eye(self.nx)
1✔
1761
        else:
1762
            raise ValueError(
×
1763
                "The derivative is only available for ordinary kriging or "
1764
                + "universal kriging using a linear trend"
1765
            )
1766

1767
        if kx is None:
1✔
1768
            dA = df.T - np.dot(dr.T, invKF)
1✔
1769
        else:
1770
            dA = df[:, kx].T - np.dot(drx, invKF)
1✔
1771

1772
        p3 = 2 * np.dot(dA, D).T
1✔
1773

1774
        # prime : derivative of MSE
1775
        # MSE ~1.0 - (rt**2.0).sum(axis=0) + (u**2.0).sum(axis=0)
1776
        prime = 0 - p1 + p3
1✔
1777
        ## scaling factors
1778
        if kx is None:
1✔
1779
            derived_variance = []
1✔
1780
            x_std = np.resize(self.X_scale, self.nx)
1✔
1781
            for i in range(len(x_std)):
1✔
1782
                derived_variance.append(sigma2 * prime.T[i] / x_std[i])
1✔
1783
            return np.array(derived_variance).T
1✔
1784
        else:
1785
            x_std = self.X_scale[kx]
1✔
1786
            derived_variance = np.array((np.outer(sigma2, np.diag(prime.T)) / x_std))
1✔
1787
            return np.atleast_2d(derived_variance).T
1✔
1788

1789
    def _optimize_hyperparam(self, D):
1✔
1790
        """
1791
        This function evaluates the Gaussian Process model at x.
1792

1793
        Parameters
1794
        ----------
1795
        D: np.ndarray [n_obs * (n_obs - 1) / 2, dim]
1796
            - The componentwise cross-spatial-correlation-distance between the
1797
              vectors in X.
1798
           For SGP surrogate, D is not used
1799

1800
        Returns
1801
        -------
1802
        best_optimal_rlf_value: real
1803
            - The value of the reduced likelihood function associated to the
1804
              best autocorrelation parameters theta.
1805
        best_optimal_par: dict()
1806
            - A dictionary containing the requested Gaussian Process model
1807
              parameters.
1808
        best_optimal_theta: list(n_comp) or list(dim)
1809
            - The best hyperparameters found by the optimization.
1810
        """
1811
        # reinitialize optimization best values
1812
        self.best_iteration_fail = None
1✔
1813
        self._thetaMemory = None
1✔
1814
        # Initialize the hyperparameter-optimization
1815
        if self.name in ["MGP"]:
1✔
1816

1817
            def minus_reduced_likelihood_function(theta):
1✔
1818
                res = -self._reduced_likelihood_function(theta)[0]
1✔
1819
                return res
1✔
1820

1821
            def grad_minus_reduced_likelihood_function(theta):
1✔
1822
                grad = -self._reduced_likelihood_gradient(theta)[0]
1✔
1823
                return grad
1✔
1824

1825
            def hessian_minus_reduced_likelihood_function(theta):
1✔
1826
                hess = -self._reduced_likelihood_hessian(theta)[0]
×
1827
                return hess
×
1828

1829
        else:
1830

1831
            def minus_reduced_likelihood_function(log10t):
1✔
1832
                return -self._reduced_likelihood_function(theta=10.0**log10t)[0]
1✔
1833

1834
            def grad_minus_reduced_likelihood_function(log10t):
1✔
1835
                log10t_2d = np.atleast_2d(log10t).T
1✔
1836
                res = (
1✔
1837
                    -np.log(10.0)
1838
                    * (10.0**log10t_2d)
1839
                    * (self._reduced_likelihood_gradient(10.0**log10t_2d)[0])
1840
                )
1841
                return res
1✔
1842

1843
            def hessian_minus_reduced_likelihood_function(log10t):
1✔
1844
                log10t_2d = np.atleast_2d(log10t).T
×
1845
                res = (
×
1846
                    -np.log(10.0)
1847
                    * (10.0**log10t_2d)
1848
                    * (self._reduced_likelihood_hessian(10.0**log10t_2d)[0])
1849
                )
1850
                return res
×
1851

1852
        limit, _rhobeg = max(12 * len(self.options["theta0"]), 50), 0.5
1✔
1853
        exit_function = False
1✔
1854
        if "KPLSK" in self.name:
1✔
1855
            n_iter = 1
1✔
1856
        else:
1857
            n_iter = 0
1✔
1858

1859
        (
1✔
1860
            best_optimal_theta,
1861
            best_optimal_rlf_value,
1862
            best_optimal_par,
1863
            constraints,
1864
        ) = (
1865
            [],
1866
            [],
1867
            [],
1868
            [],
1869
        )
1870

1871
        for ii in range(n_iter, -1, -1):
1✔
1872
            bounds_hyp = []
1✔
1873

1874
            self.theta0 = deepcopy(self.options["theta0"])
1✔
1875
            for i in range(len(self.theta0)):
1✔
1876
                # In practice, in 1D and for X in [0,1], theta^{-2} in [1e-2,infty),
1877
                # i.e. theta in (0,1e1], is a good choice to avoid overfitting.
1878
                # By standardising X in R, X_norm = (X-X_mean)/X_std, then
1879
                # X_norm in [-1,1] if considering one std intervals. This leads
1880
                # to theta in (0,2e1]
1881
                theta_bounds = self.options["theta_bounds"]
1✔
1882
                if self.theta0[i] < theta_bounds[0] or self.theta0[i] > theta_bounds[1]:
1✔
1883
                    if ii == 0 and "KPLSK" in self.name:
1✔
1884
                        if self.theta0[i] - theta_bounds[1] > 0:
1✔
UNCOV
1885
                            self.theta0[i] = theta_bounds[1] - 1e-10
×
1886
                        else:
1887
                            self.theta0[i] = theta_bounds[0] + 1e-10
1✔
1888
                    else:
1889
                        warnings.warn(
×
1890
                            f"theta0 is out the feasible bounds ({self.theta0}[{i}] out of \
1891
                                [{theta_bounds[0]}, {theta_bounds[1]}]). \
1892
                                    A random initialisation is used instead."
1893
                        )
1894
                        self.theta0[i] = self.random_state.rand()
×
1895
                        self.theta0[i] = (
×
1896
                            self.theta0[i] * (theta_bounds[1] - theta_bounds[0])
1897
                            + theta_bounds[0]
1898
                        )
1899

1900
                if self.name in ["MGP"]:  # to be discussed with R. Priem
1✔
1901
                    constraints.append(lambda theta, i=i: theta[i] + theta_bounds[1])
1✔
1902
                    constraints.append(lambda theta, i=i: theta_bounds[1] - theta[i])
1✔
1903
                    bounds_hyp.append((-theta_bounds[1], theta_bounds[1]))
1✔
1904
                else:
1905
                    log10t_bounds = np.log10(theta_bounds)
1✔
1906
                    constraints.append(lambda log10t, i=i: log10t[i] - log10t_bounds[0])
1✔
1907
                    constraints.append(lambda log10t, i=i: log10t_bounds[1] - log10t[i])
1✔
1908
                    bounds_hyp.append(log10t_bounds)
1✔
1909

1910
            if self.name in ["MGP"]:
1✔
1911
                theta0_rand = m_norm.rvs(
1✔
1912
                    self.options["prior"]["mean"] * len(self.theta0),
1913
                    self.options["prior"]["var"],
1914
                    1,
1915
                )
1916
                theta0 = self.theta0
1✔
1917
            else:
1918
                theta_bounds = self.options["theta_bounds"]
1✔
1919
                log10t_bounds = np.log10(theta_bounds)
1✔
1920
                theta0_rand = self.random_state.rand(len(self.theta0))
1✔
1921
                theta0_rand = (
1✔
1922
                    theta0_rand * (log10t_bounds[1] - log10t_bounds[0])
1923
                    + log10t_bounds[0]
1924
                )
1925
                theta0 = np.log10(self.theta0)
1✔
1926

1927
            if self.name not in ["SGP"]:
1✔
1928
                if not (self.is_continuous):
1✔
1929
                    self.D = D
1✔
1930
                else:
1931
                    ##from abs distance to kernel distance
1932
                    self.D = self._componentwise_distance(D, opt=ii)
1✔
1933
            else:  # SGP case, D is not used
1934
                pass
1✔
1935

1936
            # Initialization
1937
            k, incr, stop, best_optimal_rlf_value, max_retry = 0, 0, 1, -1e20, 10
1✔
1938
            while k < stop:
1✔
1939
                # Use specified starting point as first guess
1940
                self.noise0 = np.array(self.options["noise0"])
1✔
1941
                noise_bounds = self.options["noise_bounds"]
1✔
1942

1943
                # SGP: GP variance is optimized too
1944
                offset = 0
1✔
1945
                if self.name in ["SGP"]:
1✔
1946
                    sigma2_0 = np.log10(np.array([self.y_std[0] ** 2]))
1✔
1947
                    theta0_sigma2 = np.concatenate([theta0, sigma2_0])
1✔
1948
                    sigma2_bounds = np.log10(
1✔
1949
                        np.array([1e-12, (3.0 * self.y_std[0]) ** 2])
1950
                    )
1951
                    constraints.append(
1✔
1952
                        lambda log10t: log10t[len(self.theta0)] - sigma2_bounds[0]
1953
                    )
1954
                    constraints.append(
1✔
1955
                        lambda log10t: sigma2_bounds[1] - log10t[len(self.theta0)]
1956
                    )
1957
                    bounds_hyp.append(sigma2_bounds)
1✔
1958
                    offset = 1
1✔
1959
                    theta0 = theta0_sigma2
1✔
1960
                    theta0_rand = np.concatenate([theta0_rand, sigma2_0])
1✔
1961

1962
                if self.options["eval_noise"] and not self.options["use_het_noise"]:
1✔
1963
                    self.noise0[self.noise0 == 0.0] = noise_bounds[0]
1✔
1964
                    for i in range(len(self.noise0)):
1✔
1965
                        if (
1✔
1966
                            self.noise0[i] < noise_bounds[0]
1967
                            or self.noise0[i] > noise_bounds[1]
1968
                        ):
1969
                            self.noise0[i] = noise_bounds[0]
×
1970
                            warnings.warn(
×
1971
                                "Warning: noise0 is out the feasible bounds. The lowest possible value is used instead."
1972
                            )
1973

1974
                    theta0 = np.concatenate(
1✔
1975
                        [theta0, np.log10(np.array([self.noise0]).flatten())]
1976
                    )
1977
                    theta0_rand = np.concatenate(
1✔
1978
                        [
1979
                            theta0_rand,
1980
                            np.log10(np.array([self.noise0]).flatten()),
1981
                        ]
1982
                    )
1983

1984
                    for i in range(len(self.noise0)):
1✔
1985
                        noise_bounds = np.log10(noise_bounds)
1✔
1986
                        constraints.append(
1✔
1987
                            lambda log10t, i=i: log10t[offset + i + len(self.theta0)]
1988
                            - noise_bounds[0]
1989
                        )
1990
                        constraints.append(
1✔
1991
                            lambda log10t, i=i: noise_bounds[1]
1992
                            - log10t[offset + i + len(self.theta0)]
1993
                        )
1994
                        bounds_hyp.append(noise_bounds)
1✔
1995
                theta_limits = np.repeat(
1✔
1996
                    np.log10([theta_bounds]), repeats=len(theta0), axis=0
1997
                )
1998
                theta_all_loops = np.vstack((theta0, theta0_rand))
1✔
1999
                if ii == 1 or "KPLSK" not in self.name:
1✔
2000
                    if self.options["n_start"] > 1:
1✔
2001
                        sampling = LHS(
1✔
2002
                            xlimits=theta_limits,
2003
                            criterion="maximin",
2004
                            random_state=self.random_state,
2005
                        )
2006
                        theta_lhs_loops = sampling(self.options["n_start"])
1✔
2007
                        theta_all_loops = np.vstack((theta_all_loops, theta_lhs_loops))
1✔
2008

2009
                optimal_theta_res = {"fun": float("inf")}
1✔
2010
                optimal_theta_res_loop = None
1✔
2011
                try:
1✔
2012
                    if self.options["hyper_opt"] == "Cobyla":
1✔
2013
                        for theta0_loop in theta_all_loops:
1✔
2014
                            optimal_theta_res_loop = optimize.minimize(
1✔
2015
                                minus_reduced_likelihood_function,
2016
                                theta0_loop,
2017
                                constraints=[
2018
                                    {"fun": con, "type": "ineq"} for con in constraints
2019
                                ],
2020
                                method="COBYLA",
2021
                                options={
2022
                                    "rhobeg": _rhobeg,
2023
                                    "tol": 1e-4,
2024
                                    "maxiter": limit,
2025
                                },
2026
                            )
2027
                            if optimal_theta_res_loop["fun"] < optimal_theta_res["fun"]:
1✔
2028
                                optimal_theta_res = optimal_theta_res_loop
1✔
2029

2030
                    elif self.options["hyper_opt"] == "TNC":
1✔
2031
                        if self.options["use_het_noise"]:
1✔
2032
                            raise ValueError(
×
2033
                                "For heteroscedastic noise, please use Cobyla"
2034
                            )
2035
                        for theta0_loop in theta_all_loops:
1✔
2036
                            optimal_theta_res_loop = optimize.minimize(
1✔
2037
                                minus_reduced_likelihood_function,
2038
                                theta0_loop,
2039
                                method="TNC",
2040
                                jac=grad_minus_reduced_likelihood_function,
2041
                                ###The hessian information is available but never used
2042
                                #
2043
                                ####hess=hessian_minus_reduced_likelihood_function,
2044
                                bounds=bounds_hyp,
2045
                                options={"maxfun": limit},
2046
                            )
2047
                            if optimal_theta_res_loop["fun"] < optimal_theta_res["fun"]:
1✔
2048
                                optimal_theta_res = optimal_theta_res_loop
1✔
2049

2050
                    if "x" not in optimal_theta_res:
1✔
2051
                        raise ValueError(
×
2052
                            f"Optimizer encountered a problem: {optimal_theta_res_loop!s}"
2053
                        )
2054
                    optimal_theta = optimal_theta_res["x"]
1✔
2055

2056
                    if self.name not in ["MGP"]:
1✔
2057
                        optimal_theta = 10**optimal_theta
1✔
2058

2059
                    optimal_rlf_value, optimal_par = self._reduced_likelihood_function(
1✔
2060
                        theta=optimal_theta
2061
                    )
2062
                    # Compare the new optimizer to the best previous one
2063
                    if k > 0:
1✔
2064
                        if np.isinf(optimal_rlf_value):
×
2065
                            stop += 1
×
2066
                            if incr != 0:
×
2067
                                return
×
2068
                            if stop > max_retry:
×
2069
                                raise ValueError(
×
2070
                                    "%d attempts to train the model failed" % max_retry
2071
                                )
2072
                        else:
2073
                            if optimal_rlf_value >= self.best_iteration_fail:
×
2074
                                if optimal_rlf_value > best_optimal_rlf_value:
×
2075
                                    best_optimal_rlf_value = optimal_rlf_value
×
2076
                                    best_optimal_par = optimal_par
×
2077
                                    best_optimal_theta = optimal_theta
×
2078
                                else:
2079
                                    if (
×
2080
                                        self.best_iteration_fail
2081
                                        > best_optimal_rlf_value
2082
                                    ):
2083
                                        best_optimal_theta = self._thetaMemory
×
2084
                                        (
×
2085
                                            best_optimal_rlf_value,
2086
                                            best_optimal_par,
2087
                                        ) = self._reduced_likelihood_function(
2088
                                            theta=best_optimal_theta
2089
                                        )
2090
                    else:
2091
                        if np.isinf(optimal_rlf_value):
1✔
2092
                            stop += 1
×
2093
                        else:
2094
                            best_optimal_rlf_value = optimal_rlf_value
1✔
2095
                            best_optimal_par = optimal_par
1✔
2096
                            best_optimal_theta = optimal_theta
1✔
2097
                    k += 1
1✔
2098
                except ValueError as ve:
1✔
2099
                    # raise ve
2100
                    # If iteration is max when fmin_cobyla fail is not reached
2101
                    if self.nb_ill_matrix > 0:
1✔
2102
                        self.nb_ill_matrix -= 1
1✔
2103
                        k += 1
1✔
2104
                        stop += 1
1✔
2105
                        # One evaluation objectif function is done at least
2106
                        if self.best_iteration_fail is not None:
1✔
2107
                            if self.best_iteration_fail > best_optimal_rlf_value:
1✔
2108
                                best_optimal_theta = self._thetaMemory
1✔
2109
                                (
1✔
2110
                                    best_optimal_rlf_value,
2111
                                    best_optimal_par,
2112
                                ) = self._reduced_likelihood_function(
2113
                                    theta=best_optimal_theta
2114
                                )
2115
                    # Optimization fail
2116
                    elif np.size(best_optimal_par) == 0:
1✔
2117
                        print("Optimization failed. Try increasing the ``nugget``")
×
2118
                        raise ve
×
2119
                    # Break the while loop
2120
                    else:
2121
                        k = stop + 1
1✔
2122
                        print("fmin_cobyla failed but the best value is retained")
1✔
2123

2124
            if "KPLSK" in self.name:
1✔
2125
                if self.options["eval_noise"]:
1✔
2126
                    # best_optimal_theta contains [theta, noise] if eval_noise = True
2127
                    theta = best_optimal_theta[:-1]
1✔
2128
                else:
2129
                    # best_optimal_theta contains [theta] if eval_noise = False
2130
                    theta = best_optimal_theta
1✔
2131

2132
                if exit_function:
1✔
2133
                    return best_optimal_rlf_value, best_optimal_par, best_optimal_theta
1✔
2134

2135
                if self.options["corr"] == "squar_exp":
1✔
2136
                    self.options["theta0"] = (theta * self.coeff_pls**2).sum(1)
1✔
2137
                else:
2138
                    self.options["theta0"] = (theta * np.abs(self.coeff_pls)).sum(1)
×
2139

2140
                self.options["n_comp"] = int(self.nx)
1✔
2141
                limit = 10 * self.options["n_comp"]
1✔
2142
                self.best_iteration_fail = None
1✔
2143
                exit_function = True
1✔
2144
        return best_optimal_rlf_value, best_optimal_par, best_optimal_theta
1✔
2145

2146
    def _check_param(self):
1✔
2147
        """
2148
        This function checks some parameters of the model
2149
        and amend theta0 if possible (see _amend_theta0_option).
2150
        """
2151
        d = self.options["n_comp"] if "n_comp" in self.options else self.nx
1✔
2152
        if self.name in ["KPLS"]:
1✔
2153
            if self.options["corr"] not in ["pow_exp", "squar_exp", "abs_exp"]:
1✔
2154
                raise ValueError(
×
2155
                    "KPLS only works with a squared exponential, or an absolute exponential kernel with variable power"
2156
                )
2157
            if (
1✔
2158
                self.options["categorical_kernel"]
2159
                not in [
2160
                    MixIntKernelType.EXP_HOMO_HSPHERE,
2161
                    MixIntKernelType.HOMO_HSPHERE,
2162
                ]
2163
                and self.name == "KPLS"
2164
            ):
2165
                if self.options["cat_kernel_comps"] is not None:
1✔
2166
                    raise ValueError(
×
2167
                        "cat_kernel_comps option is for homoscedastic kernel."
2168
                    )
2169

2170
        mat_dim = (
1✔
2171
            self.options["cat_kernel_comps"]
2172
            if "cat_kernel_comps" in self.options
2173
            else None
2174
        )
2175

2176
        n_comp = self.options["n_comp"] if "n_comp" in self.options else None
1✔
2177
        n_param = compute_n_param(
1✔
2178
            self.design_space,
2179
            self.options["categorical_kernel"],
2180
            d,
2181
            n_comp,
2182
            mat_dim,
2183
        )
2184

2185
        if self.options["corr"] == "squar_sin_exp":
1✔
2186
            if (
1✔
2187
                self.is_continuous
2188
                or self.options["categorical_kernel"] == MixIntKernelType.GOWER
2189
            ):
2190
                self.options["theta0"] *= np.ones(2 * n_param)
1✔
2191
            else:
2192
                n_param += len([self.design_space.is_cat_mask])
1✔
2193
                self.options["theta0"] *= np.ones(n_param)
1✔
2194

2195
        else:
2196
            self.options["theta0"] *= np.ones(n_param)
1✔
2197
        if (
1✔
2198
            self.options["corr"] not in ["squar_exp", "abs_exp", "pow_exp"]
2199
            and not (self.is_continuous)
2200
            and self.options["categorical_kernel"]
2201
            not in [
2202
                MixIntKernelType.GOWER,
2203
                MixIntKernelType.COMPOUND_SYMMETRY,
2204
                MixIntKernelType.HOMO_HSPHERE,
2205
            ]
2206
        ):
2207
            raise ValueError(
1✔
2208
                "Categorical kernels should be matrix or exponential based."
2209
            )
2210

2211
        if len(self.options["theta0"]) != d and (
1✔
2212
            self.options["categorical_kernel"]
2213
            in [MixIntKernelType.GOWER, MixIntKernelType.COMPOUND_SYMMETRY]
2214
            or self.is_continuous
2215
        ):
2216
            if len(self.options["theta0"]) == 1:
1✔
2217
                self.options["theta0"] *= np.ones(d)
×
2218
            else:
2219
                if self.options["corr"] != "squar_sin_exp":
1✔
2220
                    raise ValueError(
1✔
2221
                        "the length of theta0 (%s) should be equal to the number of dim (%s)."
2222
                        % (len(self.options["theta0"]), d)
2223
                    )
2224
        if (
1✔
2225
            self.options["eval_noise"] or np.max(self.options["noise0"]) > 1e-12
2226
        ) and self.options["hyper_opt"] == "TNC":
2227
            warnings.warn(
1✔
2228
                "TNC not available yet for noise handling. Switching to Cobyla"
2229
            )
2230
            self.options["hyper_opt"] = "Cobyla"
1✔
2231

2232
        if self.options["use_het_noise"] and not self.options["eval_noise"]:
1✔
2233
            if len(self.options["noise0"]) != self.nt:
×
2234
                if len(self.options["noise0"]) == 1:
×
2235
                    self.options["noise0"] *= np.ones(self.nt)
×
2236
                else:
2237
                    raise ValueError(
×
2238
                        "for the heteroscedastic case, the length of noise0 (%s) \
2239
                            should be equal to the number of observations (%s)."
2240
                        % (len(self.options["noise0"]), self.nt)
2241
                    )
2242
        if not self.options["use_het_noise"]:
1✔
2243
            if len(self.options["noise0"]) != 1:
1✔
2244
                raise ValueError(
×
2245
                    "for the homoscedastic noise case, the length of noise0 (%s) should be equal to one."
2246
                    % (len(self.options["noise0"]))
2247
                )
2248

2249
        if self.supports["training_derivatives"]:
1✔
2250
            if 1 not in self.training_points[None]:
1✔
2251
                raise Exception("Derivative values are needed for using the GEK model.")
×
2252

2253
    def _check_F(self, n_samples_F, p):
1✔
2254
        """
2255
        This function check the F-parameters of the model.
2256
        """
2257

2258
        if n_samples_F != self.nt:
1✔
2259
            raise Exception(
×
2260
                "Number of rows in F and X do not match. Most "
2261
                "likely something is going wrong with the "
2262
                "regression model."
2263
            )
2264
        if p > n_samples_F:
1✔
2265
            raise Exception(
×
2266
                (
2267
                    "Ordinary least squares problem is undetermined "
2268
                    "n_samples=%d must be greater than the "
2269
                    "regression model size p=%d."
2270
                )
2271
                % (self.nt, p)
2272
            )
2273

2274

2275
def compute_n_param(design_space, cat_kernel, d, n_comp, mat_dim):
1✔
2276
    """
2277
    Returns the he number of parameters needed for an homoscedastic or full group kernel.
2278
    Parameters
2279
     ----------
2280
    design_space: BaseDesignSpace
2281
            - design space definition
2282
    cat_kernel : string
2283
            -The kernel to use for categorical inputs. Only for non continuous Kriging,
2284
    d: int
2285
            - n_comp or nx
2286
    n_comp : int
2287
            - if PLS, then it is the number of components else None,
2288
    mat_dim : int
2289
            - if PLS, then it is the number of components for matrix kernel (mixed integer) else None,
2290
    Returns
2291
    -------
2292
     n_param: int
2293
            - The number of parameters.
2294
    """
2295
    n_param = design_space.n_dv
1✔
2296
    if n_comp is not None:
1✔
2297
        n_param = d
1✔
2298
        if cat_kernel == MixIntKernelType.CONT_RELAX:
1✔
2299
            return n_param
1✔
2300
        if mat_dim is not None:
1✔
2301
            return int(np.sum([i * (i - 1) / 2 for i in mat_dim]) + n_param)
1✔
2302
    if cat_kernel in [MixIntKernelType.GOWER, MixIntKernelType.COMPOUND_SYMMETRY]:
1✔
2303
        return n_param
1✔
2304
    for i, dv in enumerate(design_space.design_variables):
1✔
2305
        if isinstance(dv, CategoricalVariable):
1✔
2306
            n_values = dv.n_values
1✔
2307
            if design_space.n_dv == d:
1✔
2308
                n_param -= 1
1✔
2309
            if cat_kernel in [
1✔
2310
                MixIntKernelType.EXP_HOMO_HSPHERE,
2311
                MixIntKernelType.HOMO_HSPHERE,
2312
            ]:
2313
                n_param += int(n_values * (n_values - 1) / 2)
1✔
2314
            if cat_kernel == MixIntKernelType.CONT_RELAX:
1✔
2315
                n_param += int(n_values)
1✔
2316
    return n_param
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc