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

libKriging / libKriging / 16194641062

10 Jul 2025 12:00PM UTC coverage: 37.371%. Remained the same
16194641062

push

github

yannrichet
sync version in R

3295 of 8817 relevant lines covered (37.37%)

59122.67 hits per line

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

0.0
/src/lib/NuggetKriging.cpp
1
// clang-format off
2
// MUST BE at the beginning before any other <cmath> include (e.g. in armadillo's headers)
3
#define _USE_MATH_DEFINES // required for Visual Studio
4

5
#include <cmath>
6
// clang-format on
7

8
#include "libKriging/utils/lk_armadillo.hpp"
9

10
#include "libKriging/Bench.hpp"
11
#include "libKriging/CacheFunction.hpp"
12
#include "libKriging/Covariance.hpp"
13
#include "libKriging/KrigingException.hpp"
14
#include "libKriging/LinearAlgebra.hpp"
15
#include "libKriging/NuggetKriging.hpp"
16
#include "libKriging/Optim.hpp"
17
#include "libKriging/Random.hpp"
18
#include "libKriging/Trend.hpp"
19
#include "libKriging/utils/custom_hash_function.hpp"
20
#include "libKriging/utils/data_from_arma_vec.hpp"
21
#include "libKriging/utils/jsonutils.hpp"
22
#include "libKriging/utils/nlohmann/json.hpp"
23
#include "libKriging/utils/utils.hpp"
24

25
#include <cassert>
26
#include <lbfgsb_cpp/lbfgsb.hpp>
27
#include <map>
28
#include <tuple>
29
#include <vector>
30

31
/************************************************/
32
/**      NuggetKriging implementation        **/
33
/************************************************/
34

35
// This will create the dist(xi,xj) function above. Need to parse "covType".
36
void NuggetKriging::make_Cov(const std::string& covType) {
×
37
  m_covType = covType;
×
38
  if (covType.compare("gauss") == 0) {
×
39
    _Cov = Covariance::Cov_gauss;
×
40
    _DlnCovDtheta = Covariance::DlnCovDtheta_gauss;
×
41
    _DlnCovDx = Covariance::DlnCovDx_gauss;
×
42
    _Cov_pow = 2;
×
43
  } else if (covType.compare("exp") == 0) {
×
44
    _Cov = Covariance::Cov_exp;
×
45
    _DlnCovDtheta = Covariance::DlnCovDtheta_exp;
×
46
    _DlnCovDx = Covariance::DlnCovDx_exp;
×
47
    _Cov_pow = 1;
×
48
  } else if (covType.compare("matern3_2") == 0) {
×
49
    _Cov = Covariance::Cov_matern32;
×
50
    _DlnCovDtheta = Covariance::DlnCovDtheta_matern32;
×
51
    _DlnCovDx = Covariance::DlnCovDx_matern32;
×
52
    _Cov_pow = 1.5;
×
53
  } else if (covType.compare("matern5_2") == 0) {
×
54
    _Cov = Covariance::Cov_matern52;
×
55
    _DlnCovDtheta = Covariance::DlnCovDtheta_matern52;
×
56
    _DlnCovDx = Covariance::DlnCovDx_matern52;
×
57
    _Cov_pow = 2.5;
×
58
  } else
59
    throw std::invalid_argument("Unsupported covariance kernel: " + covType);
×
60

61
  // arma::cout << "make_Cov done." << arma::endl;
62
}
×
63

64
LIBKRIGING_EXPORT arma::mat NuggetKriging::covMat(const arma::mat& X1, const arma::mat& X2) {
×
65
  arma::mat Xn1 = X1;
×
66
  arma::mat Xn2 = X2;
×
67
  Xn1.each_row() -= m_centerX;
×
68
  Xn1.each_row() /= m_scaleX;
×
69
  Xn2.each_row() -= m_centerX;
×
70
  Xn2.each_row() /= m_scaleX;
×
71

72
  arma::mat R = arma::mat(X1.n_rows, X2.n_rows, arma::fill::none);
×
73
  for (arma::uword i = 0; i < Xn1.n_rows; i++) {
×
74
    for (arma::uword j = 0; j < Xn2.n_rows; j++) {
×
75
      R.at(i, j) = _Cov((Xn1.row(i) - Xn2.row(j)).t(), m_theta);
×
76
    }
77
  }
78
  return R * m_sigma2;
×
79
}
×
80

81
// at least, just call make_Cov(kernel)
82
LIBKRIGING_EXPORT NuggetKriging::NuggetKriging(const std::string& covType) {
×
83
  make_Cov(covType);
×
84
}
×
85

86
LIBKRIGING_EXPORT NuggetKriging::NuggetKriging(const arma::vec& y,
×
87
                                               const arma::mat& X,
88
                                               const std::string& covType,
89
                                               const Trend::RegressionModel& regmodel,
90
                                               bool normalize,
91
                                               const std::string& optim,
92
                                               const std::string& objective,
93
                                               const Parameters& parameters) {
×
94
  if (y.n_elem != X.n_rows)
×
95
    throw std::runtime_error("Dimension of new data should be the same:\n X: (" + std::to_string(X.n_rows) + "x"
×
96
                             + std::to_string(X.n_cols) + "), y: (" + std::to_string(y.n_elem) + ")");
×
97

98
  make_Cov(covType);
×
99
  fit(y, X, regmodel, normalize, optim, objective, parameters);
×
100
}
×
101

102
LIBKRIGING_EXPORT NuggetKriging::NuggetKriging(const NuggetKriging& other, ExplicitCopySpecifier)
×
103
    : NuggetKriging{other} {}
×
104

105
arma::vec NuggetKriging::ones = arma::ones<arma::vec>(0);
106

107
NuggetKriging::KModel NuggetKriging::make_Model(const arma::vec& theta,
×
108
                                                const double alpha,
109
                                                std::map<std::string, double>* bench) const {
110
  arma::mat R;
×
111
  arma::mat L;
×
112
  arma::mat Linv;
×
113
  arma::mat Fstar;
×
114
  arma::vec ystar;
×
115
  arma::mat Rstar;
×
116
  arma::mat Qstar;
×
117
  arma::vec Estar;
×
118
  double SSEstar{};
×
119
  arma::vec betahat;
×
120
  NuggetKriging::KModel m{R, L, Linv, Fstar, ystar, Rstar, Qstar, Estar, SSEstar, betahat};
×
121

122
  arma::uword n = m_X.n_rows;
×
123
  arma::uword d = m_X.n_cols;
×
124
  arma::uword p = m_F.n_cols;
×
125

126
  auto t0 = Bench::tic();
×
127
  m.R = arma::mat(n, n, arma::fill::none);
×
128
  // check if we want to recompute model for same theta, for augmented Xy (using cholesky fast update).
129
  bool update = false;
×
130
  if (!m_is_empty)
×
131
    update = (m_sigma2 / (m_sigma2 + m_nugget) == alpha) && (m_theta.size() == theta.size())
×
132
             && (theta - m_theta).is_zero() && (this->m_T.memptr() != nullptr) && (n > this->m_T.n_rows);
×
133
  if (update) {
×
134
    m.L = LinearAlgebra::update_cholCov(&(m.R), m_dX, theta, _Cov, alpha, NuggetKriging::ones, m_T, m_R);
×
135
  } else
136
    m.L = LinearAlgebra::cholCov(&(m.R), m_dX, theta, _Cov, alpha, NuggetKriging::ones);
×
137
  t0 = Bench::toc(bench, "R = _Cov(dX) & L = Chol(R)", t0);
×
138

139
  // Compute intermediate useful matrices
140
  arma::mat Fystar = LinearAlgebra::solve(m.L, arma::join_rows(m_F, m_y));
×
141
  t0 = Bench::toc(bench, "Fy* = L \\ [F,y]", t0);
×
142
  m.Fstar = Fystar.head_cols(p);
×
143
  m.ystar = Fystar.tail_cols(1);
×
144

145
  arma::mat Q_qr;
×
146
  arma::mat R_qr;
×
147
  arma::qr_econ(Q_qr, R_qr, Fystar);
×
148
  t0 = Bench::toc(bench, "Q_qr,R_qr = QR(Fy*)", t0);
×
149

150
  m.Rstar = R_qr.head_cols(p);
×
151
  m.Qstar = Q_qr.head_cols(p);
×
152
  m.Estar = Q_qr.tail_cols(1) * R_qr.at(p, p);
×
153
  m.SSEstar = R_qr.at(p, p) * R_qr.at(p, p);
×
154

155
  if (m_est_beta) {
×
156
    m.betahat = LinearAlgebra::solve(m.Rstar, R_qr.tail_cols(1));
×
157
    t0 = Bench::toc(bench, "^b = R* \\ R_qr[1:p, p+1]", t0);
×
158
  } else {
159
    m.betahat = arma::vec(p, arma::fill::zeros);  // whatever: not used
×
160
  }
161

162
  return m;
×
163
}
×
164

165
// Objective function for fit : -logLikelihood
166

167
double NuggetKriging::_logLikelihood(const arma::vec& _theta_alpha,
×
168
                                     arma::vec* grad_out,
169
                                     NuggetKriging::KModel* model,
170
                                     std::map<std::string, double>* bench) const {
171
  // arma::cout << " theta, alpha: " << _theta_alpha.t() << arma::endl;
172

173
  arma::uword d = m_X.n_cols;
×
174
  double _alpha = _theta_alpha.at(d);
×
175
  // closure: (alpha,sigma2,nugget)
176
  // * alpha = sigma2 / (nugget + sigma2)
177
  // * sigma2 = nugget * alpha / (1-alpha)
178
  // * nugget = sigma2 * (1-alpha) / alpha
179
  double _sigma2 = m_sigma2;
×
180
  double _nugget = m_nugget;
×
181
  if (m_est_sigma2) {
×
182
    if (m_est_nugget) {
×
183
      // nothing to do now: use alpha as input, compute var = sigma2 + nugget later and then sigma2 and nugget
184
    } else {
185
      _sigma2 = m_nugget * _alpha / (1 - _alpha);
×
186
    }
187
  } else {
188
    if (m_est_nugget) {
×
189
      _nugget = m_sigma2 * (1 - _alpha) / _alpha;
×
190
    } else {
191
      _alpha
192
          = m_sigma2
×
193
            / (m_nugget + m_sigma2);  // force alpha value, and its derivative will be 0 (to force gradient convergence)
×
194
    }
195
  }
196
  arma::vec _theta = _theta_alpha.head(d);
×
197

198
  NuggetKriging::KModel m = make_Model(_theta, _alpha, bench);
×
199
  if (model != nullptr)
×
200
    *model = m;
×
201

202
  arma::uword n = m_X.n_rows;
×
203

204
  if (m_est_sigma2 && m_est_nugget) {  // DiceKriging: model@case == "LLconcentration_beta_alpha"
×
205
    // back to case 1: sigma2 + nugget = var (=z*z/n)
206
    double var = as_scalar(LinearAlgebra::crossprod(m.Estar)) / n;
×
207
    _sigma2 = _alpha * var;
×
208
    _nugget = (1 - _alpha) * var;
×
209
  }
210

211
  double ll = -0.5
212
              * (n * log(2 * M_PI * _sigma2 / _alpha) + 2 * sum(log(m.L.diag()))
×
213
                 + as_scalar(LinearAlgebra::crossprod(m.Estar)) * (_alpha / _sigma2));
×
214

215
  if (grad_out != nullptr) {
×
216
    auto t0 = Bench::tic();
×
217
    arma::vec terme1 = arma::vec(d);
×
218

219
    if ((m.Linv.memptr() == nullptr) || (arma::size(m.Linv) != arma::size(m.L))) {
×
220
      m.Linv = LinearAlgebra::solve(m.L, arma::mat(n, n, arma::fill::eye));
×
221
      t0 = Bench::toc(bench, "L ^-1", t0);
×
222
    }
223

224
    arma::mat Rinv = LinearAlgebra::crossprod(m.Linv);
×
225
    t0 = Bench::toc(bench, "R^-1 = t(L^-1) * L^-1", t0);
×
226

227
    arma::mat x = LinearAlgebra::solve(m.L.t(), m.Estar);
×
228
    t0 = Bench::toc(bench, "x = tL \\ z", t0);
×
229

230
    arma::cube gradR = arma::cube(n, n, d, arma::fill::none);
×
231
    const arma::vec zeros = arma::vec(d, arma::fill::zeros);
×
232
    for (arma::uword i = 0; i < n; i++) {
×
233
      gradR.tube(i, i) = zeros;
×
234
      for (arma::uword j = 0; j < i; j++) {
×
235
        gradR.tube(i, j) = m.R.at(i, j) * _DlnCovDtheta(m_dX.col(i * n + j), _theta);
×
236
        gradR.tube(j, i) = gradR.tube(i, j);
×
237
      }
238
    }
239
    t0 = Bench::toc(bench, "gradR = R * dlnCov(dX)", t0);
×
240

241
    for (arma::uword k = 0; k < d; k++) {
×
242
      t0 = Bench::tic();
×
243
      arma::mat gradR_k = gradR.slice(k);
×
244
      t0 = Bench::toc(bench, "gradR_k = gradR[k]", t0);
×
245

246
      // should make a fast function trace_prod(A,B) -> sum_i(sum_j(Ai,j*Bj,i))
247
      terme1.at(k) = as_scalar(x.t() * gradR_k * x) / (_sigma2 + _nugget);
×
248
      double terme2 = -arma::trace(Rinv * gradR_k);  //-arma::accu(Rinv % gradR_k_upper)
×
249
      (*grad_out).at(k) = (terme1.at(k) + terme2) / 2;
×
250
      t0 = Bench::toc(bench, "grad_ll[k] = xt * gradR_k / S2 + tr(Ri * gradR_k)", t0);
×
251
    }
×
252

253
    //  # partial derivative with respect to (alpha=) v = sigma^2 + delta^2
254
    //  dCdv <- R0 - diag(model@n)
255
    //  term1 <- -t(x) %*% dCdv %*% x / v
256
    //  term2 <- sum(Cinv * dCdv) # economic computation of trace(Cinv%*%C0)
257
    //  logLik.derivative[nparam] <- -0.5 * (term1 + term2) # /sigma2
258

259
    if (m_est_sigma2 && m_est_nugget) {
×
260
      arma::mat dRdv = m.R / _alpha;
×
261
      dRdv.diag().zeros();
×
262
      double _terme1 = -as_scalar((trans(x) * dRdv) * x) / (_sigma2 + _nugget);
×
263
      double _terme2 = arma::accu(arma::dot(Rinv, dRdv));
×
264
      (*grad_out).at(d) = -0.5 * (_terme1 + _terme2);
×
265
    } else if (m_est_sigma2 && !m_est_nugget) {
×
266
      arma::mat dRdv = m.R / _alpha;
×
267
      dRdv.diag().ones();
×
268
      double _terme1 = -as_scalar((trans(x) * dRdv) * x) / ((_sigma2 + _nugget) * (_sigma2 + _nugget));
×
269
      double _terme2 = arma::accu(arma::dot(Rinv / (_sigma2 + _nugget), dRdv));
×
270
      (*grad_out).at(d) = -0.5 * (_terme1 + _terme2) * _nugget / (1 - _alpha) / (1 - _alpha);
×
271
    } else                    // we do not support explicitely the case where nugget is estimated and sigma2 is fixed
×
272
      (*grad_out).at(d) = 0;  // if sigma2 and nugget are defined & fixed by user
×
273

274
    // arma::cout << " grad_out:" << *grad_out << arma::endl;
275
  }
×
276
  return ll;
×
277
}
×
278

279
LIBKRIGING_EXPORT std::tuple<double, arma::vec> NuggetKriging::logLikelihoodFun(const arma::vec& _theta_alpha,
×
280
                                                                                const bool _grad,
281
                                                                                const bool _bench) {
282
  double ll = -1;
×
283
  arma::vec grad;
×
284

285
  if (_bench) {
×
286
    std::map<std::string, double> bench;
×
287
    if (_grad) {
×
288
      grad = arma::vec(_theta_alpha.n_elem);
×
289
      ll = _logLikelihood(_theta_alpha, &grad, nullptr, &bench);
×
290
    } else
291
      ll = _logLikelihood(_theta_alpha, nullptr, nullptr, &bench);
×
292

293
    size_t num = 0;
×
294
    for (auto& kv : bench)
×
295
      num = std::max(kv.first.size(), num);
×
296
    for (auto& kv : bench)
×
297
      arma::cout << "| " << Bench::pad(kv.first, num, ' ') << " | " << kv.second << " |" << arma::endl;
×
298

299
  } else {
×
300
    if (_grad) {
×
301
      grad = arma::vec(_theta_alpha.n_elem);
×
302
      ll = _logLikelihood(_theta_alpha, &grad, nullptr, nullptr);
×
303
    } else
304
      ll = _logLikelihood(_theta_alpha, nullptr, nullptr, nullptr);
×
305
  }
306

307
  return std::make_tuple(ll, std::move(grad));
×
308
}
×
309

310
// Objective function for fit: bayesian-like approach fromm RobustGaSP
311

312
double NuggetKriging::_logMargPost(const arma::vec& _theta_alpha,
×
313
                                   arma::vec* grad_out,
314
                                   NuggetKriging::KModel* model,
315
                                   std::map<std::string, double>* bench) const {
316
  // arma::cout << " theta: " << _theta << arma::endl;
317

318
  // In RobustGaSP:
319
  // neg_log_marginal_post_approx_ref <- function(param,nugget,
320
  // nugget.est,R0,X,zero_mean,output,CL,a,b,kernel_type,alpha) {
321
  //  lml=log_marginal_lik(param,nugget,nugget.est,R0,X,zero_mean,output,kernel_type,alpha);
322
  //  lp=log_approx_ref_prior(param,nugget,nugget.est,CL,a,b);
323
  //  -(lml+lp)
324
  //}
325
  // double log_marginal_lik(const Vec param,double nugget, const bool nugget_est, const List R0, const
326
  // Eigen::Map<Eigen::MatrixXd> & X,const String zero_mean,const Eigen::Map<Eigen::MatrixXd> & output, Eigen::VectorXi
327
  // kernel_type,const Eigen::VectorXd alpha ){
328
  //  double nu=nugget;
329
  //  int param_size=param.size();
330
  //  Eigen::VectorXd beta= param.array().exp().matrix();
331
  //  ...beta
332
  //  R=R+nu*MatrixXd::Identity(num_obs,num_obs);  //not sure
333
  //
334
  //  LLT<MatrixXd> lltOfR(R);             // compute the cholesky decomposition of R called lltofR
335
  //  MatrixXd L = lltOfR.matrixL();   //retrieve factor L  in the decomposition
336
  //
337
  //  if(zero_mean=="Yes"){...}else{
338
  //
339
  //  int q=X.cols();
340
  //
341
  //  MatrixXd R_inv_X=L.transpose().triangularView<Upper>().solve(L.triangularView<Lower>().solve(X)); //one forward
342
  //  and one backward to compute R.inv%*%X MatrixXd Xt_R_inv_X=X.transpose()*R_inv_X; //Xt%*%R.inv%*%X
343
  //
344
  //  LLT<MatrixXd> lltOfXRinvX(Xt_R_inv_X); // cholesky decomposition of Xt_R_inv_X called lltOfXRinvX
345
  //  MatrixXd LX = lltOfXRinvX.matrixL();  //  retrieve factor LX  in the decomposition
346
  //  MatrixXd Rinv_X_Xt_Rinv_X_inv_Xt_Rinv=
347
  //  R_inv_X*(LX.transpose().triangularView<Upper>().solve(LX.triangularView<Lower>().solve(R_inv_X.transpose())));
348
  //  //compute  Rinv_X_Xt_Rinv_X_inv_Xt_Rinv through one forward and one backward solve MatrixXd yt_R_inv=
349
  //  (L.transpose().triangularView<Upper>().solve(L.triangularView<Lower>().solve(output))).transpose(); MatrixXd S_2=
350
  //  (yt_R_inv*output-output.transpose()*Rinv_X_Xt_Rinv_X_inv_Xt_Rinv*output); double log_S_2=log(S_2(0,0)); return
351
  //  (-(L.diagonal().array().log().matrix().sum())-(LX.diagonal().array().log().matrix().sum())-(num_obs-q)/2.0*log_S_2);
352
  //  }
353
  //}
354
  // double log_approx_ref_prior(const Vec param,double nugget, bool nugget_est, const Eigen::VectorXd CL,const double
355
  // a,const double b ){
356
  //  double nu=nugget;
357
  //  int param_size=param.size();beta
358
  //  Eigen::VectorX beta= param.array().exp().matrix();
359
  //  ...
360
  //  double t=CL.cwiseProduct(beta).sum()+nu;
361
  //  return -b*t + a*log(t);
362
  //}
363

364
  arma::uword d = m_X.n_cols;
×
365
  double _alpha = _theta_alpha.at(d);
×
366
  // closure: (alpha,sigma2,nugget)
367
  // * alpha = sigma2 / (nugget + sigma2)
368
  // * sigma2 = nugget * alpha / (1-alpha)
369
  // * nugget = sigma2 * (1-alpha) / alpha
370
  double _sigma2;
371
  double _nugget;
372
  if (m_est_sigma2) {
×
373
    if (m_est_nugget) {
×
374
      // nothing to do now: use alpha as input, compute var = sigma2 + nugget later and then sigma2 and nugget
375
    } else {
376
      _sigma2 = m_nugget * _alpha / (1 - _alpha);
×
377
    }
378
  } else {
379
    if (m_est_nugget) {
×
380
      _nugget = m_sigma2 * (1 - _alpha) / _alpha;
×
381
    } else {
382
      _alpha
383
          = m_sigma2
×
384
            / (m_nugget + m_sigma2);  // force alpha value, and its derivative will be 0 (to force gradient convergence)
×
385
    }
386
  }
387
  arma::vec _theta = _theta_alpha.head(d);
×
388

389
  NuggetKriging::KModel m = make_Model(_theta, _alpha, bench);
×
390
  if (model != nullptr)
×
391
    *model = m;
×
392

393
  arma::uword n = m_X.n_rows;
×
394
  arma::uword p = m_F.n_cols;
×
395

396
  // RobustGaSP naming...
397
  // arma::mat X = m_F;
398
  // arma::mat L = fd->T;
399

400
  auto t0 = Bench::tic();
×
401
  // m.Fstar : fd->M = solve(L, X, LinearAlgebra::default_solve_opts);
402

403
  // arma::mat Rinv_X = solve(trans(L), fd->M, LinearAlgebra::default_solve_opts);
404
  arma::mat Rinv_X = LinearAlgebra::solve(m.L.t(), m.Fstar);
×
405

406
  // arma::mat Xt_Rinv_X = trans(X) * Rinv_X;  // Xt%*%R.inv%*%X
407
  arma::mat Xt_Rinv_X = m_F.t() * Rinv_X;
×
408

409
  // arma::mat LX = chol(Xt_Rinv_X, "lower");  //  retrieve factor LX  in the decomposition
410
  arma::mat LX = LinearAlgebra::safe_chol_lower(Xt_Rinv_X);
×
411

412
  // arma::mat Rinv_X_Xt_Rinv_X_inv_Xt_Rinv
413
  //     = Rinv_X
414
  //       * (solve(trans(LX),
415
  //                solve(LX, trans(Rinv_X), LinearAlgebra::default_solve_opts),
416
  //                LinearAlgebra::default_solve_opts));  // compute  Rinv_X_Xt_Rinv_X_inv_Xt_Rinv through one forward
417
  arma::mat Rinv_X_Xt_Rinv_X_inv_Xt_Rinv
418
      = Rinv_X
419
        * (LinearAlgebra::solve(
×
420
            trans(LX),
×
421
            LinearAlgebra::solve(LX, trans(Rinv_X))));  // compute  Rinv_X_Xt_Rinv_X_inv_Xt_Rinv through one forward
×
422

423
  arma::mat yt_Rinv = trans(solve(trans(m.L), m.ystar));
×
424
  t0 = Bench::toc(bench, "YtRi = Yt \\ Tt", t0);
×
425

426
  arma::mat S_2 = (yt_Rinv * m_y - trans(m_y) * Rinv_X_Xt_Rinv_X_inv_Xt_Rinv * m_y);
×
427
  t0 = Bench::toc(bench, "S2 = YtRi * y - yt * RiFFtRiFiFtRi * y", t0);
×
428

429
  if (m_est_sigma2 && m_est_nugget) {
×
430
    _sigma2 = S_2(0, 0) / (n - p);  //? - 2);
×
431
    _nugget = _sigma2 * (1 - _alpha) / _alpha;
×
432
  }
433
  double log_S_2 = log(_sigma2 * (n - p));
×
434

435
  double log_marginal_lik = -sum(log(m.L.diag())) - sum(log(LX.diag())) - (n - p) / 2.0 * log_S_2;
×
436
  t0 = Bench::toc(bench, "lml = -Sum(log(diag(T))) - Sum(log(diag(TF)))...", t0);
×
437
  // arma::cout << " log_marginal_lik:" << log_marginal_lik << arma::endl;
438

439
  // Default prior params
440
  double a = 0.2;
×
441
  double b = 1.0 / pow(n, 1.0 / d) * (a + d);
×
442
  // t0 = Bench::toc(bench,"b             ", t0);
443

444
  arma::vec CL = trans(max(m_X, 0) - min(m_X, 0)) / pow(n, 1.0 / d);
×
445
  t0 = Bench::toc(bench, "CL = (max(X) - min(X)) / n^1/d", t0);
×
446

447
  double t = arma::accu(CL / _theta) + _nugget / _sigma2;
×
448
  // arma::cout << " a:" << a << arma::endl;
449
  // arma::cout << " b:" << b << arma::endl;
450
  // arma::cout << " t:" << t << arma::endl;
451
  double log_approx_ref_prior = -b * t + a * log(t);
×
452
  // arma::cout << " log_approx_ref_prior:" << log_approx_ref_prior << arma::endl;
453

454
  if (grad_out != nullptr) {
×
455
    // Eigen::VectorXd log_marginal_lik_deriv(const Eigen::VectorXd param,double nugget,  bool nugget_est, const List
456
    // R0, const Eigen::Map<Eigen::MatrixXd> & X,const String zero_mean,const Eigen::Map<Eigen::MatrixXd> & output,
457
    // Eigen::VectorXi kernel_type,const Eigen::VectorXd alpha){
458
    // ...
459
    // VectorXd ans=VectorXd::Ones(param_size);
460
    // ...
461
    // MatrixXd Q_output= yt_R_inv.transpose()-Rinv_X_Xt_Rinv_X_inv_Xt_Rinv*output;
462
    // MatrixXd dev_R_i;
463
    // MatrixXd Wb_ti;
464
    // //allow different choices of kernels
465
    //
466
    // for(int ti=0;ti<p;ti++){
467
    //   //kernel_type_ti=kernel_type[ti];
468
    //   if(kernel_type[ti]==3){
469
    //     dev_R_i=matern_5_2_deriv( R0[ti],R_ori,beta[ti]);  //now here I have R_ori instead of R
470
    //   }else {...}
471
    //   Wb_ti=(L.transpose().triangularView<Upper>().solve(L.triangularView<Lower>().solve(dev_R_i))).transpose()-dev_R_i*Rinv_X_Xt_Rinv_X_inv_Xt_Rinv;
472
    //   ans[ti]=-0.5*Wb_ti.diagonal().sum()+(num_obs-q)/2.0*(output.transpose()*Wb_ti.transpose()*Q_output/S_2(0,0))(0,0);
473
    // }
474

475
    t0 = Bench::tic();
×
476
    arma::vec ans = arma::vec(d, arma::fill::ones);
×
477
    arma::mat Q_output = trans(yt_Rinv) - Rinv_X_Xt_Rinv_X_inv_Xt_Rinv * m_y;
×
478
    t0 = Bench::toc(bench, "Qo = YtRi - RiFFtRiFiFtRi * y", t0);
×
479

480
    arma::cube gradR = arma::cube(n, n, d, arma::fill::zeros);
×
481
    // const arma::vec zeros = arma::vec(d,arma::fill::zeros);
482
    for (arma::uword i = 0; i < n; i++) {
×
483
      // gradR.tube(i, i) = zeros;
484
      for (arma::uword j = 0; j < i; j++) {
×
485
        gradR.tube(i, j) = m.R.at(i, j) * _DlnCovDtheta(m_dX.col(i * n + j), _theta);
×
486
        gradR.tube(j, i) = gradR.tube(i, j);
×
487
      }
488
    }
489
    t0 = Bench::toc(bench, "gradR = R * dlnCov(dX)", t0);
×
490

491
    arma::mat Wb_k;
×
492
    for (arma::uword k = 0; k < d; k++) {
×
493
      t0 = Bench::tic();
×
494
      arma::mat gradR_k = gradR.slice(k);
×
495
      t0 = Bench::toc(bench, "gradR_k = gradR[k]", t0);
×
496

497
      Wb_k = trans(LinearAlgebra::solve(trans(m.L), LinearAlgebra::solve(m.L, gradR_k)))
×
498
             - gradR_k * Rinv_X_Xt_Rinv_X_inv_Xt_Rinv;
×
499
      t0 = Bench::toc(bench, "Wb_k = gradR_k \\ L \\ Tt - gradR_k * RiFFtRiFiFtRi", t0);
×
500

501
      ans[k] = -sum(Wb_k.diag()) / 2.0 + as_scalar(trans(m_y) * trans(Wb_k) * Q_output) / (2.0 * _sigma2);
×
502
      t0 = Bench::toc(bench, "ans[k] = Sum(diag(Wb_k)) + yt * Wb_kt * Qo / S2...", t0);
×
503
    }
×
504
    // arma::cout << " log_marginal_lik_deriv:" << -ans * pow(_theta,2) << arma::endl;
505
    // arma::cout << " log_approx_ref_prior_deriv:" <<  a*CL/t - b*CL << arma::endl;
506

507
    (*grad_out).head(d) = ans - (a * CL / t - b * CL) / square(_theta);
×
508

509
    if (m_est_sigma2 || m_est_nugget) {
×
510
      arma::mat gradR_d = m.R / _alpha;
×
511
      gradR_d.diag().zeros();
×
512
      Wb_k = trans(LinearAlgebra::solve(trans(m.L), LinearAlgebra::solve(m.L, gradR_d)))
×
513
             - gradR_d * Rinv_X_Xt_Rinv_X_inv_Xt_Rinv;
×
514
      double ans_d = -sum(Wb_k.diag()) / 2.0 + as_scalar(trans(m_y) * trans(Wb_k) * Q_output) / (2.0 * _sigma2);
×
515
      (*grad_out).at(d) = ans_d - (a / t - b) / pow(_alpha, 2.0);
×
516
    } else
×
517
      (*grad_out).at(d) = 0;  // if sigma2 and nugget are defined & fixed by user
×
518
    // arma::cout << " grad_out:" << *grad_out << arma::endl;
519
  }
×
520

521
  // arma::cout << " lmp:" << (log_marginal_lik+log_approx_ref_prior) << arma::endl;
522
  return (log_marginal_lik + log_approx_ref_prior);
×
523
}
×
524

525
LIBKRIGING_EXPORT std::tuple<double, arma::vec> NuggetKriging::logMargPostFun(const arma::vec& _theta_alpha,
×
526
                                                                              const bool _grad,
527
                                                                              const bool _bench) {
528
  double lmp = -1;
×
529
  arma::vec grad;
×
530

531
  if (_bench) {
×
532
    std::map<std::string, double> bench;
×
533
    if (_grad) {
×
534
      grad = arma::vec(_theta_alpha.n_elem);
×
535
      lmp = _logMargPost(_theta_alpha, &grad, nullptr, &bench);
×
536
    } else
537
      lmp = _logMargPost(_theta_alpha, nullptr, nullptr, &bench);
×
538

539
    size_t num = 0;
×
540
    for (auto& kv : bench)
×
541
      num = std::max(kv.first.size(), num);
×
542
    for (auto& kv : bench) {
×
543
      arma::cout << "| " << Bench::pad(kv.first, num, ' ') << " | " << kv.second << " |" << arma::endl;
×
544
    }
545

546
  } else {
×
547
    if (_grad) {
×
548
      grad = arma::vec(_theta_alpha.n_elem);
×
549
      lmp = _logMargPost(_theta_alpha, &grad, nullptr, nullptr);
×
550
    } else
551
      lmp = _logMargPost(_theta_alpha, nullptr, nullptr, nullptr);
×
552
  }
553

554
  return std::make_tuple(lmp, std::move(grad));
×
555
}
×
556

557
LIBKRIGING_EXPORT double NuggetKriging::logLikelihood() {
×
558
  int d = m_theta.n_elem;
×
559
  arma::vec _theta_alpha = arma::vec(d + 1);
×
560
  _theta_alpha.head(d) = m_theta;
×
561
  _theta_alpha.at(d) = m_sigma2 / (m_sigma2 + m_nugget);
×
562
  return std::get<0>(NuggetKriging::logLikelihoodFun(_theta_alpha, false, false));
×
563
}
×
564

565
LIBKRIGING_EXPORT double NuggetKriging::logMargPost() {
×
566
  int d = m_theta.n_elem;
×
567
  arma::vec _theta_alpha = arma::vec(d + 1);
×
568
  _theta_alpha.head(d) = m_theta;
×
569
  _theta_alpha.at(d) = m_sigma2 / (m_sigma2 + m_nugget);
×
570
  return std::get<0>(NuggetKriging::logMargPostFun(_theta_alpha, false, false));
×
571
}
×
572

573
std::function<arma::vec(const arma::vec&)> NuggetKriging::reparam_to = [](const arma::vec& _theta_alpha) {
×
574
  arma::vec _theta_malpha = _theta_alpha;
×
575
  const arma::uword d = _theta_alpha.n_elem - 1;
×
576
  _theta_malpha.at(d) = 1 + alpha_lower - _theta_malpha.at(d);
×
577
  return Optim::reparam_to(_theta_malpha);
×
578
};
×
579

580
std::function<arma::vec(const arma::vec&)> NuggetKriging::reparam_from = [](const arma::vec& _gamma) {
×
581
  arma::vec _theta_alpha = Optim::reparam_from(_gamma);
×
582
  const arma::uword d = _theta_alpha.n_elem - 1;
×
583
  _theta_alpha.at(d) = 1 + alpha_lower - _theta_alpha.at(d);
×
584
  return _theta_alpha;
×
585
};
586

587
std::function<arma::vec(const arma::vec&, const arma::vec&)> NuggetKriging::reparam_from_deriv
588
    = [](const arma::vec& _theta_alpha, const arma::vec& _grad) {
×
589
        arma::vec D_theta_alpha = arma::conv_to<arma::vec>::from(-_grad % _theta_alpha);
×
590
        const arma::uword d = D_theta_alpha.n_elem - 1;
×
591
        D_theta_alpha.at(d) = (1 + alpha_lower - _theta_alpha.at(d)) * _grad.at(d);
×
592
        return D_theta_alpha;
×
593
      };
594

595
double NuggetKriging::alpha_upper = 1.0;
596
double NuggetKriging::alpha_lower = 1E-3;
597

598
/** Fit the kriging object on (X,y):
599
 * @param y is n length column vector of output
600
 * @param X is n*d matrix of input
601
 * @param regmodel is the regression model to be used for the GP mean (choice between contant, linear, quadratic)
602
 * @param normalize is a boolean to enforce inputs/output normalization
603
 * @param optim is an optimizer name from OptimLib, or 'none' to keep parameters unchanged
604
 * @param objective is 'LOO' or 'LL'. Ignored if optim=='none'.
605
 * @param parameters starting values for hyper-parameters for optim, or final values if optim=='none'.
606
 */
607
LIBKRIGING_EXPORT void NuggetKriging::fit(const arma::vec& y,
×
608
                                          const arma::mat& X,
609
                                          const Trend::RegressionModel& regmodel,
610
                                          bool normalize,
611
                                          const std::string& optim,
612
                                          const std::string& objective,
613
                                          const Parameters& parameters) {
614
  const arma::uword n = X.n_rows;
×
615
  const arma::uword d = X.n_cols;
×
616

617
  std::function<double(const arma::vec& _gamma, arma::vec* grad_out, NuggetKriging::KModel* okm_data)> fit_ofn;
×
618
  m_optim = optim;
×
619
  m_objective = objective;
×
620
  if (objective.compare("LL") == 0) {
×
621
    if (Optim::reparametrize) {
×
622
      fit_ofn = CacheFunction([this](const arma::vec& _gamma, arma::vec* grad_out, NuggetKriging::KModel* okm_data) {
×
623
        // Change variable for opt: . -> 1/exp(.)
624
        // DEBUG: if (Optim::log_level>3) arma::cout << "> gamma: " << _gamma << arma::endl;
625
        const arma::vec _theta_alpha = NuggetKriging::reparam_from(_gamma);
×
626
        // DEBUG: if (Optim::log_level>3) arma::cout << "> theta_alpha: " << _theta_alpha << arma::endl;
627
        double ll = this->_logLikelihood(_theta_alpha, grad_out, okm_data, nullptr);
×
628
        // DEBUG: if (Optim::log_level>3) arma::cout << "  > -ll: " << -ll << arma::endl;
629
        if (grad_out != nullptr) {
×
630
          *grad_out = -NuggetKriging::reparam_from_deriv(_theta_alpha, *grad_out);
×
631
          // DEBUG:
632
          // if (Optim::log_level>3) {
633
          //  arma::cout << "  > grad -ll: " << *grad_out << arma::endl;
634
          //  //// Check with numerical gradient:
635
          //  //for (size_t i = 0; i <_gamma.n_elem; i++) {
636
          //  //  arma::vec eps = arma::zeros(_gamma.n_elem);
637
          //  //  eps(i) = 0.000001;
638
          //  //  const arma::vec _theta_alpha_eps = reparam_from(_gamma + eps);
639
          //  //  double ll_eps = this->_logLikelihood(_theta_alpha_eps, nullptr, okm_data);
640
          //  //  arma::cout << "  > num_grad -ll: " << -(ll_eps - ll)/0.000001 << arma::endl;
641
          //  //}
642
          //}
643
        }
644
        return -ll;
×
645
      });
×
646
    } else {
647
      fit_ofn = CacheFunction([this](const arma::vec& _gamma, arma::vec* grad_out, NuggetKriging::KModel* okm_data) {
×
648
        const arma::vec _theta_alpha = _gamma;
×
649
        // DEBUG: if (Optim::log_level>3) arma::cout << "> theta_alpha: " << _theta_alpha << arma::endl;
650
        double ll = this->_logLikelihood(_theta_alpha, grad_out, okm_data, nullptr);
×
651
        // DEBUG: if (Optim::log_level>3) arma::cout << "  > ll: " << ll << arma::endl;
652
        if (grad_out != nullptr) {
×
653
          // DEBUG: if (Optim::log_level>3) arma::cout << "  > grad ll: " << grad_out << arma::endl;
654
          *grad_out = -*grad_out;
×
655
        }
656
        return -ll;
×
657
      });
×
658
    }
659
  } else if (objective.compare("LMP") == 0) {
×
660
    // Our impl. of https://github.com/cran/RobustGaSP/blob/5cf21658e6a6e327be6779482b93dfee25d24592/R/rgasp.R#L303
661
    //@see Mengyang Gu, Xiao-jing Wang and Jim Berger, 2018, Annals of Statistics.
662
    if (Optim::reparametrize) {
×
663
      fit_ofn = CacheFunction([this](const arma::vec& _gamma, arma::vec* grad_out, NuggetKriging::KModel* okm_data) {
×
664
        // Change variable for opt: . -> 1/exp(.)
665
        // DEBUG: if (Optim::log_level>3) arma::cout << "> gamma: " << _gamma << arma::endl;
666
        const arma::vec _theta_alpha = NuggetKriging::reparam_from(_gamma);
×
667
        // DEBUG: if (Optim::log_level>3) arma::cout << "> theta_alpha: " << _theta_alpha << arma::endl;
668
        double lmp = this->_logMargPost(_theta_alpha, grad_out, okm_data, nullptr);
×
669
        // DEBUG: if (Optim::log_level>3) arma::cout << "  > lmp: " << lmp << arma::endl;
670
        if (grad_out != nullptr) {
×
671
          // DEBUG: if (Optim::log_level>3) arma::cout << "  > grad lmp: " << grad_out << arma::endl;
672
          *grad_out = -NuggetKriging::reparam_from_deriv(_theta_alpha, *grad_out);
×
673
        }
674
        return -lmp;
×
675
      });
×
676
    } else {
677
      fit_ofn = CacheFunction([this](const arma::vec& _gamma, arma::vec* grad_out, NuggetKriging::KModel* okm_data) {
×
678
        const arma::vec _theta_alpha = _gamma;
×
679
        // DEBUG: if (Optim::log_level>3) arma::cout << "> theta_alpha: " << _theta_alpha << arma::endl;
680
        double lmp = this->_logMargPost(_theta_alpha, grad_out, okm_data, nullptr);
×
681
        // DEBUG: if (Optim::log_level>3) arma::cout << "  > lmp: " << lmp << arma::endl;
682
        if (grad_out != nullptr) {
×
683
          // DEBUG: if (Optim::log_level>3) arma::cout << "  > grad lmp: " << grad_out << arma::endl;
684
          *grad_out = -*grad_out;
×
685
        }
686
        return -lmp;
×
687
      });
×
688
    }
689
  } else
690
    throw std::invalid_argument("Unsupported fit objective: " + objective + " (supported are: LL, LMP)");
×
691

692
  arma::rowvec centerX;
×
693
  arma::rowvec scaleX;
×
694
  double centerY;
695
  double scaleY;
696
  // Normalization of inputs and output
697
  m_normalize = normalize;
×
698
  if (m_normalize) {
×
699
    centerX = min(X, 0);
×
700
    scaleX = max(X, 0) - min(X, 0);
×
701
    centerY = min(y);
×
702
    scaleY = max(y) - min(y);
×
703
  } else {
704
    centerX = arma::rowvec(d, arma::fill::zeros);
×
705
    scaleX = arma::rowvec(d, arma::fill::ones);
×
706
    centerY = 0;
×
707
    scaleY = 1;
×
708
  }
709
  m_centerX = centerX;
×
710
  m_scaleX = scaleX;
×
711
  m_centerY = centerY;
×
712
  m_scaleY = scaleY;
×
713
  {  // FIXME why copies of newX and newy
714
    arma::mat newX = X;
×
715
    newX.each_row() -= centerX;
×
716
    newX.each_row() /= scaleX;
×
717
    arma::vec newy = (y - centerY) / scaleY;
×
718
    this->m_X = newX;
×
719
    this->m_y = newy;
×
720
  }
×
721

722
  // Now we compute the distance matrix between points. Will be used to compute R(theta) later (e.g. when fitting)
723
  // Note: m_dX is transposed compared to m_X
724
  m_dX = arma::mat(d, n * n, arma::fill::zeros);
×
725
  for (arma::uword ij = 0; ij < m_dX.n_cols; ij++) {
×
726
    int i = (int)ij / n;
×
727
    int j = ij % n;  // i,j <-> i*n+j
×
728
    if (i < j) {
×
729
      m_dX.col(ij) = trans(m_X.row(i) - m_X.row(j));
×
730
      m_dX.col(j * n + i) = m_dX.col(ij);
×
731
    }
732
  }
733
  m_maxdX = arma::max(arma::abs(m_dX), 1);
×
734

735
  // Define regression matrix
736
  m_regmodel = regmodel;
×
737
  m_F = Trend::regressionModelMatrix(regmodel, m_X);
×
738
  m_est_beta = parameters.is_beta_estim && (m_regmodel != Trend::RegressionModel::None);
×
739
  if (!m_est_beta && parameters.beta.has_value()
×
740
      && parameters.beta.value().n_elem > 0) {  // Then force beta to be fixed (not estimated, no variance)
×
741
    m_est_beta = false;
×
742
    m_beta = parameters.beta.value();
×
743
    if (m_normalize)
×
744
      m_beta /= scaleY;
×
745
  } else
746
    m_est_beta = true;
×
747

748
  arma::mat theta0;
×
749
  if (parameters.theta.has_value()) {
×
750
    theta0 = parameters.theta.value();
×
751
    if ((theta0.n_cols != d) && (theta0.n_rows == d))
×
752
      theta0 = theta0.t();
×
753
    if (m_normalize)
×
754
      theta0.each_row() /= scaleX;
×
755
    if (theta0.n_cols != d)
×
756
      throw std::runtime_error("Dimension of theta should be nx" + std::to_string(d) + " instead of "
×
757
                               + std::to_string(theta0.n_rows) + "x" + std::to_string(theta0.n_cols));
×
758
  }
759

760
  if (optim == "none") {  // just keep given theta, no optimisation of ll (but estim beta still possible)
×
761
    if (!parameters.theta.has_value())
×
762
      throw std::runtime_error("Theta should be given (1x" + std::to_string(d) + ") matrix, when optim=none");
×
763
    if (!parameters.nugget.has_value())
×
764
      throw std::runtime_error("Nugget should be given, when optim=none");
×
765
    if (!parameters.sigma2.has_value())
×
766
      throw std::runtime_error("Sigma2 should be given, when optim=none");
×
767

768
    m_theta = trans(theta0.row(0));
×
769
    m_est_theta = false;
×
770

771
    double sigma2 = -1;
×
772
    m_est_sigma2 = parameters.is_sigma2_estim;
×
773
    if (parameters.sigma2.has_value()) {
×
774
      sigma2 = parameters.sigma2.value()[0];  // otherwise sigma2 will be re-calculated using given theta
×
775
      if (m_normalize)
×
776
        sigma2 /= (scaleY * scaleY);
×
777
    } else
778
      m_est_sigma2 = true;
×
779
    double nugget = 0;
×
780
    m_est_nugget = parameters.is_nugget_estim;
×
781
    if (parameters.nugget.has_value()) {
×
782
      nugget = parameters.nugget.value()[0];
×
783
      if (m_normalize)
×
784
        nugget /= (scaleY * scaleY);
×
785
    } else
786
      m_est_nugget = true;
×
787

788
    NuggetKriging::KModel m = make_Model(m_theta, sigma2 / (nugget + sigma2), nullptr);
×
789
    m_is_empty = false;
×
790
    m_T = std::move(m.L);
×
791
    m_R = std::move(m.R);
×
792
    m_M = std::move(m.Fstar);
×
793
    m_circ = std::move(m.Rstar);
×
794
    m_star = std::move(m.Qstar);
×
795
    if (m_est_beta) {
×
796
      m_beta = std::move(m.betahat);
×
797
      m_z = std::move(m.Estar);
×
798
    } else {
799
      // m_beta = parameters.beta.value(); already done above
800
      m_z = std::move(m.ystar) - m_M * m_beta;
×
801
    }
802
    if (m_est_sigma2) {
×
803
      m_sigma2 = m.SSEstar / n;
×
804
    } else {
805
      m_sigma2 = sigma2;
×
806
    }
807
    if (m_est_nugget) {
×
808
      m_nugget = 0;
×
809
    } else {
810
      m_nugget = nugget;
×
811
    }
812

813
  } else if (optim.rfind("BFGS", 0) == 0) {
×
814
    Random::init();
×
815

816
    arma::vec theta_lower = Optim::theta_lower_factor * m_maxdX;
×
817
    arma::vec theta_upper = Optim::theta_upper_factor * m_maxdX;
×
818

819
    if (Optim::variogram_bounds_heuristic) {
×
820
      arma::vec dy2 = arma::vec(n * n, arma::fill::zeros);
×
821
      for (arma::uword ij = 0; ij < dy2.n_elem; ij++) {
×
822
        int i = (int)ij / n;
×
823
        int j = ij % n;  // i,j <-> i*n+j
×
824
        if (i < j) {
×
825
          dy2[ij] = m_y.at(i) - m_y.at(j);
×
826
          dy2[ij] *= dy2[ij];
×
827
          dy2[j * n + i] = dy2[ij];
×
828
        }
829
      }
830
      // dy2 /= arma::var(m_y);
831
      arma::vec dy2dX2_slope = dy2 / arma::sum(m_dX % m_dX, 0).t();
×
832
      // arma::cout << "dy2dX_slope:" << dy2dX_slope << arma::endl;
833
      dy2dX2_slope.replace(arma::datum::nan, 0.0);  // we are not interested in same points where dX=0, and dy=0
×
834
      arma::vec w = dy2dX2_slope / sum(dy2dX2_slope);
×
835
      arma::mat steepest_dX_mean = arma::abs(m_dX) * w;
×
836
      // arma::cout << "steepest_dX_mean:" << steepest_dX_mean << arma::endl;
837

838
      theta_lower = arma::max(theta_lower, Optim::theta_lower_factor * steepest_dX_mean);
×
839
      // no, only relevant for inf bound: theta_upper = arma::min(theta_upper, Optim::theta_upper_factor *
840
      // steepest_dX_mean);
841
      theta_lower = arma::min(theta_lower, theta_upper);
×
842
      theta_upper = arma::max(theta_lower, theta_upper);
×
843
    }
×
844
    // arma::cout << "theta_lower:" << theta_lower << arma::endl;
845
    // arma::cout << "theta_upper:" << theta_upper << arma::endl;
846

847
    int multistart = 1;
×
848
    try {
849
      multistart = std::stoi(optim.substr(4));
×
850
    } catch (std::invalid_argument&) {
×
851
      // let multistart = 1
852
    }
×
853

854
    arma::mat theta0_rand
855
        = arma::repmat(trans(theta_lower), multistart, 1)
×
856
          + Random::randu_mat(multistart, d) % arma::repmat(trans(theta_upper - theta_lower), multistart, 1);
×
857
    // theta0 = arma::abs(0.5 + Random::randn_mat(multistart, d) / 6.0)
858
    //          % arma::repmat(max(m_X, 0) - min(m_X, 0), multistart, 1);
859

860
    if (parameters.theta.has_value()) {  // just use given theta(s) as starting values for multi-bfgs
×
861
      multistart = std::max(multistart, (int)theta0.n_rows);
×
862
      theta0 = arma::join_cols(theta0, theta0_rand);  // append random starting points to given ones
×
863
      theta0.resize(multistart, theta0.n_cols);       // keep only multistart first rows
×
864
    } else {
865
      theta0 = theta0_rand;
×
866
    }
867
    // arma::cout << "theta0:" << theta0 << arma::endl;
868

869
    arma::vec alpha0;
×
870
    if (parameters.sigma2.has_value() && parameters.nugget.has_value()) {
×
871
      alpha0 = arma::vec(parameters.sigma2.value().n_elem * parameters.nugget.value().n_elem);
×
872
      for (size_t i = 0; i < parameters.sigma2.value().n_elem; i++) {
×
873
        for (size_t j = 0; j < parameters.nugget.value().n_elem; j++) {
×
874
          if ((parameters.sigma2.value()[i] < 0) || (parameters.nugget.value()[j] < 0)
×
875
              || (parameters.sigma2.value()[i] + parameters.nugget.value()[j] < 0))
×
876
            alpha0[i + j * parameters.sigma2.value().n_elem]
×
877
                = alpha_lower + (alpha_upper - alpha_lower) * (1 - std::pow(Random::randu(), 3.0));
×
878
          else
879
            alpha0[i + j * parameters.sigma2.value().n_elem]
×
880
                = parameters.sigma2.value()[i] / (parameters.sigma2.value()[i] + parameters.nugget.value()[j]);
×
881
        }
882
      }
883
    } else {
884
      alpha0 = alpha_lower + (alpha_upper - alpha_lower) * (1 - arma::pow(Random::randu_vec(theta0.n_rows), 3.0));
×
885
    }
886
    // arma::cout << "alpha0:" << alpha0 << arma::endl;
887

888
    arma::vec gamma_lower = arma::vec(d + 1);
×
889
    gamma_lower.head(d) = theta_lower;
×
890
    gamma_lower.at(d) = alpha_lower;
×
891
    arma::vec gamma_upper = arma::vec(d + 1);
×
892
    gamma_upper.head(d) = theta_upper;
×
893
    gamma_upper.at(d) = alpha_upper;
×
894
    if (Optim::reparametrize) {
×
895
      arma::vec gamma_lower_tmp = gamma_lower;
×
896
      gamma_lower = NuggetKriging::reparam_to(gamma_upper);
×
897
      gamma_upper = NuggetKriging::reparam_to(gamma_lower_tmp);
×
898
      double gamma_lower_at_d = gamma_lower.at(d);
×
899
      gamma_lower.at(d) = gamma_upper.at(d);
×
900
      gamma_upper.at(d) = gamma_lower_at_d;
×
901
    }
×
902

903
    double min_ofn = std::numeric_limits<double>::infinity();
×
904

905
    for (arma::uword i = 0; i < multistart; i++) {
×
906
      arma::vec gamma_tmp = arma::vec(d + 1);
×
907
      gamma_tmp.head(d) = theta0.row(i % multistart).t();
×
908
      gamma_tmp.at(d) = alpha0[i % alpha0.n_elem];
×
909
      if (Optim::reparametrize) {
×
910
        gamma_tmp = NuggetKriging::reparam_to(gamma_tmp);
×
911
      }
912

913
      gamma_lower = arma::min(gamma_tmp, gamma_lower);
×
914
      gamma_upper = arma::max(gamma_tmp, gamma_upper);
×
915

916
      if (Optim::log_level > 0) {
×
917
        arma::cout << "BFGS:" << arma::endl;
×
918
        arma::cout << "  max iterations: " << Optim::max_iteration << arma::endl;
×
919
        arma::cout << "  null gradient tolerance: " << Optim::gradient_tolerance << arma::endl;
×
920
        arma::cout << "  constant objective tolerance: " << Optim::objective_rel_tolerance << arma::endl;
×
921
        arma::cout << "  reparametrize: " << Optim::reparametrize << arma::endl;
×
922
        arma::cout << "  normalize: " << m_normalize << arma::endl;
×
923
        arma::cout << "  lower_bounds: " << theta_lower.t() << "";
×
924
        arma::cout << "                " << alpha_lower << arma::endl;
×
925
        arma::cout << "  upper_bounds: " << theta_upper.t() << "";
×
926
        arma::cout << "                " << alpha_upper << arma::endl;
×
927
        arma::cout << "  start_point: " << theta0.row(i % multistart) << "";
×
928
        arma::cout << "               " << alpha0[i % alpha0.n_elem] << arma::endl;
×
929
      }
930

931
      m_est_sigma2 = parameters.is_sigma2_estim;
×
932
      if ((!m_est_sigma2) && (parameters.sigma2.has_value())) {
×
933
        m_sigma2 = parameters.sigma2.value()[0];
×
934
        if (m_normalize)
×
935
          m_sigma2 /= (scaleY * scaleY);
×
936
      } else {
937
        m_est_sigma2 = true;  // force estim if no value given
×
938
      }
939
      m_est_nugget = parameters.is_nugget_estim;
×
940
      if ((!m_est_nugget) && (parameters.nugget.has_value())) {
×
941
        m_nugget = parameters.nugget.value()[0];
×
942
        if (m_normalize)
×
943
          m_nugget /= (scaleY * scaleY);
×
944
      } else {
945
        m_est_nugget = true;  // force estim if no value given
×
946
      }
947

948
      lbfgsb::Optimizer optimizer{d + 1};
×
949
      optimizer.iprint = Optim::log_level - 2;
×
950
      optimizer.max_iter = Optim::max_iteration;
×
951
      optimizer.pgtol = Optim::gradient_tolerance;
×
952
      optimizer.factr = Optim::objective_rel_tolerance / 1E-13;
×
953
      arma::ivec bounds_type{d + 1, arma::fill::value(2)};  // means both upper & lower bounds
×
954

955
      int retry = 0;
×
956
      double best_f_opt = std::numeric_limits<double>::infinity();
×
957
      arma::vec best_gamma = gamma_tmp;
×
958
      NuggetKriging::KModel m = make_Model(theta0.row(i % multistart).t(), alpha0[i % alpha0.n_elem], nullptr);
×
959

960
      while (retry <= Optim::max_restart) {
×
961
        arma::vec gamma_0 = gamma_tmp;
×
962
        auto result = optimizer.minimize(
963
            [&m, &fit_ofn](const arma::vec& vals_inp, arma::vec& grad_out) -> double {
×
964
              return fit_ofn(vals_inp, &grad_out, &m);
×
965
            },
966
            gamma_tmp,
967
            gamma_lower.memptr(),
×
968
            gamma_upper.memptr(),
×
969
            bounds_type.memptr());
×
970

971
        if (Optim::log_level > 0) {
×
972
          arma::cout << "     iterations: " << result.num_iters << arma::endl;
×
973
          arma::cout << "     status: " << result.task << arma::endl;
×
974
          if (Optim::reparametrize) {
×
975
            arma::cout << "     start_point: " << NuggetKriging::reparam_from(gamma_0).t() << " ";
×
976
            arma::cout << "     solution: " << NuggetKriging::reparam_from(gamma_tmp).t() << " ";
×
977
          } else {
978
            arma::cout << "     start_point: " << gamma_0.t() << " ";
×
979
            arma::cout << "     solution: " << gamma_tmp.t() << " ";
×
980
          }
981
        }
982

983
        if (result.f_opt < best_f_opt) {
×
984
          best_f_opt = result.f_opt;
×
985
          best_gamma = gamma_tmp;
×
986
        }
987

988
        double sol_to_lb_theta = arma::min(arma::abs(gamma_tmp.head(d) - gamma_lower.head(d)));
×
989
        double sol_to_ub_theta = arma::min(arma::abs(gamma_tmp.head(d) - gamma_upper.head(d)));
×
990
        double sol_to_b_theta = std::min(sol_to_ub_theta, sol_to_lb_theta);
×
991
        //= Optim::reparametrize ? sol_to_ub_theta : sol_to_lb_theta;         // just consider theta lower bound
992
        double sol_to_lb_alpha = std::abs(gamma_tmp.at(d) - gamma_lower.at(d));
×
993
        double sol_to_ub_alpha = std::abs(gamma_tmp.at(d) - gamma_upper.at(d));
×
994
        double sol_to_b_alpha = std::min(sol_to_ub_alpha, sol_to_lb_alpha);
×
995
        //= Optim::reparametrize ? sol_to_ub_alpha : sol_to_lb_alpha;         // just consider alpha lower bound
996
        // Optim::reparametrize
997
        //    ? std::abs(gamma_tmp.at(d) - gamma_upper.at(d))
998
        //    : std::abs(gamma_tmp.at(d) - gamma_lower.at(d));
999
        double sol_to_b = sol_to_b_theta < sol_to_b_alpha ? sol_to_b_theta : sol_to_b_alpha;
×
1000
        if ((retry < Optim::max_restart)  //&& (result.num_iters <= 2 * d)
×
1001
            && ((result.task.rfind("ABNORMAL_TERMINATION_IN_LNSRCH", 0) == 0)
×
1002
                || ((sol_to_b < arma::datum::eps) && (result.num_iters <= 2))  // we fastly converged to one bound
×
1003
                || (result.f_opt > best_f_opt))) {
×
1004
          gamma_tmp.head(d)
×
1005
              = (theta0.row(i % multistart).t() + theta_lower)
×
1006
                / pow(2.0, retry + 1);  // so, re-use previous starting point and change it to middle-point
×
1007
          gamma_tmp.at(d) = alpha_upper - (alpha0[i % alpha0.n_elem] + alpha_upper) / pow(2.0, retry + 1);
×
1008

1009
          if (Optim::reparametrize)
×
1010
            gamma_tmp = NuggetKriging::reparam_to(gamma_tmp);
×
1011

1012
          gamma_lower = arma::min(gamma_tmp, gamma_lower);
×
1013
          gamma_upper = arma::max(gamma_tmp, gamma_upper);
×
1014

1015
          retry++;
×
1016
        } else {
1017
          if (Optim::log_level > 1) {
×
1018
            result.print();
×
1019
          }
1020
          break;
×
1021
        }
1022
      }
×
1023

1024
      // last call to ensure that T and z are up-to-date with solution.
1025
      double min_ofn_tmp = fit_ofn(best_gamma, nullptr, &m);
×
1026

1027
      if (Optim::log_level > 0) {
×
1028
        arma::cout << "  best objective: " << min_ofn_tmp << arma::endl;
×
1029
        if (Optim::reparametrize) {
×
1030
          arma::cout << "  best solution: " << NuggetKriging::reparam_from(best_gamma).t() << " ";
×
1031
        } else {
1032
          arma::cout << "  best solution: " << best_gamma.t() << " ";
×
1033
        }
1034
      }
1035

1036
      if (min_ofn_tmp < min_ofn) {
×
1037
        arma::vec best_theta_alpha = best_gamma;
×
1038
        if (Optim::reparametrize)
×
1039
          best_theta_alpha = NuggetKriging::reparam_from(best_gamma);
×
1040
        m_theta = best_theta_alpha.head(d);
×
1041
        m_est_theta = true;
×
1042
        min_ofn = min_ofn_tmp;
×
1043

1044
        m_is_empty = false;
×
1045
        m_T = std::move(m.L);
×
1046
        m_R = std::move(m.R);
×
1047
        m_M = std::move(m.Fstar);
×
1048
        m_circ = std::move(m.Rstar);
×
1049
        m_star = std::move(m.Qstar);
×
1050
        if (m_est_beta) {
×
1051
          m_beta = std::move(m.betahat);
×
1052
          m_z = std::move(m.Estar);
×
1053
        } else {
1054
          // m_beta = parameters.beta.value(); already done above
1055
          m_z = std::move(m.ystar) - m_M * m_beta;
×
1056
        }
1057
        double m_alpha = best_theta_alpha.at(d);
×
1058
        if (m_est_sigma2) {
×
1059
          if (m_est_nugget) {
×
1060
            m_sigma2 = m_alpha * as_scalar(LinearAlgebra::crossprod(m_z)) / n;
×
1061
            if (m_objective.compare("LMP") == 0) {
×
1062
              m_sigma2 = m_sigma2 * n / (n - m_F.n_cols - 2);
×
1063
            }
1064
            m_nugget = m_sigma2 / m_alpha - m_sigma2;
×
1065
          } else {
1066
            m_sigma2 = m_nugget * m_alpha / (1 - m_alpha);
×
1067
          }
1068
        } else {
1069
          if (m_est_nugget) {
×
1070
            m_nugget = m_sigma2 * (1 - m_alpha) / m_alpha;
×
1071
          } else {
1072
            // don't care about alpha
1073
          }
1074
        }
1075
      }
×
1076
    }
×
1077
  } else
×
1078
    throw std::runtime_error("Unsupported optim: " + optim + " (supported are: none, BFGS[#])");
×
1079

1080
  // arma::cout << "theta:" << m_theta << arma::endl;
1081
}
×
1082

1083
/** Compute the prediction for given points X'
1084
 * @param X_n is n_n*d matrix of points where to predict output
1085
 * @param return_stdev is true if return also stdev column vector
1086
 * @param return_cov is true if return also cov matrix between X_n
1087
 * @param return_deriv is true if return also derivative of prediction wrt x
1088
 * @return output prediction: n_n means, [n_n standard deviations], [n_n*n_n full covariance matrix]
1089
 */
1090
LIBKRIGING_EXPORT std::tuple<arma::vec, arma::vec, arma::mat, arma::mat, arma::mat>
1091
NuggetKriging::predict(const arma::mat& X_n, bool return_stdev, bool return_cov, bool return_deriv) {
×
1092
  arma::uword n_n = X_n.n_rows;
×
1093
  arma::uword n_o = m_X.n_rows;
×
1094
  arma::uword d = m_X.n_cols;
×
1095
  if (X_n.n_cols != d)
×
1096
    throw std::runtime_error("Predict locations have wrong dimension: " + std::to_string(X_n.n_cols) + " instead of "
×
1097
                             + std::to_string(d));
×
1098

1099
  arma::vec yhat_n(n_n);
×
1100
  arma::vec ysd2_n = arma::vec(n_n, arma::fill::zeros);
×
1101
  arma::mat Sigma_n = arma::mat(n_n, n_n, arma::fill::zeros);
×
1102
  arma::mat Dyhat_n = arma::mat(n_n, d, arma::fill::zeros);
×
1103
  arma::mat Dysd2_n = arma::mat(n_n, d, arma::fill::zeros);
×
1104

1105
  arma::mat Xn_o = trans(m_X);  // already normalized if needed
×
1106
  arma::mat Xn_n = X_n;
×
1107
  // Normalize X_n
1108
  Xn_n.each_row() -= m_centerX;
×
1109
  Xn_n.each_row() /= m_scaleX;
×
1110

1111
  double sigma2 = m_sigma2 * (m_objective.compare("LMP") == 0 ? (n_o - m_F.n_cols) / (n_o - m_F.n_cols - 2) : 1.0);
×
1112

1113
  arma::mat F_n = Trend::regressionModelMatrix(m_regmodel, Xn_n);
×
1114
  Xn_n = trans(Xn_n);
×
1115

1116
  double m_alpha = m_sigma2 / (m_sigma2 + m_nugget);
×
1117

1118
  auto t0 = Bench::tic();
×
1119
  arma::mat R_on = arma::mat(n_o, n_n, arma::fill::none);
×
1120
  for (arma::uword i = 0; i < n_o; i++) {
×
1121
    for (arma::uword j = 0; j < n_n; j++) {
×
1122
      arma::vec dij = Xn_o.col(i) - Xn_n.col(j);
×
1123
      if (dij.is_zero(arma::datum::eps))
×
1124
        R_on.at(i, j) = 1.0;
×
1125
      else
1126
        R_on.at(i, j) = _Cov(dij, m_theta) * m_alpha;
×
1127
    }
×
1128
  }
1129
  t0 = Bench::toc(nullptr, "R_on       ", t0);
×
1130

1131
  arma::mat Rstar_on = LinearAlgebra::solve(m_T, R_on);
×
1132
  t0 = Bench::toc(nullptr, "Rstar_on   ", t0);
×
1133

1134
  yhat_n = F_n * m_beta + trans(Rstar_on) * m_z;
×
1135
  t0 = Bench::toc(nullptr, "yhat_n     ", t0);
×
1136

1137
  // Un-normalize predictor
1138
  yhat_n = m_centerY + m_scaleY * yhat_n;
×
1139

1140
  arma::mat Fhat_n = trans(Rstar_on) * m_M;
×
1141
  arma::mat E_n = F_n - Fhat_n;
×
1142
  arma::mat Ecirc_n = LinearAlgebra::rsolve(m_circ, E_n);
×
1143
  t0 = Bench::toc(nullptr, "Ecirc_n    ", t0);
×
1144

1145
  if (return_stdev) {
×
1146
    ysd2_n = 1.0 - sum(Rstar_on % Rstar_on, 0).as_col() + sum(Ecirc_n % Ecirc_n, 1).as_col();
×
1147
    ysd2_n.transform([](double val) { return (std::isnan(val) || val < 0 ? 0.0 : val); });
×
1148
    ysd2_n *= sigma2 / m_alpha * m_scaleY * m_scaleY;
×
1149
    t0 = Bench::toc(nullptr, "ysd2_n     ", t0);
×
1150
  }
1151

1152
  if (return_cov) {
×
1153
    // Compute the covariance matrix between new data points
1154
    arma::mat R_nn = arma::mat(n_n, n_n, arma::fill::none);
×
1155
    for (arma::uword i = 0; i < n_n; i++) {
×
1156
      // R_nn.at(i, i) = 1;
1157
      for (arma::uword j = 0; j < i; j++) {
×
1158
        R_nn.at(i, j) = R_nn.at(j, i) = _Cov((Xn_n.col(i) - Xn_n.col(j)), m_theta);
×
1159
      }
1160
    }
1161
    R_nn *= m_alpha;
×
1162
    R_nn.diag().ones();
×
1163
    t0 = Bench::toc(nullptr, "R_nn       ", t0);
×
1164

1165
    Sigma_n = R_nn - trans(Rstar_on) * Rstar_on + Ecirc_n * trans(Ecirc_n);
×
1166
    Sigma_n *= sigma2 / m_alpha * m_scaleY * m_scaleY;
×
1167
    t0 = Bench::toc(nullptr, "Sigma_n    ", t0);
×
1168
  }
×
1169

1170
  if (return_deriv) {
×
1171
    //// https://github.com/libKriging/dolka/blob/bb1dbf0656117756165bdcff0bf5e0a1f963fbef/R/kmStuff.R#L322C1-L363C10
1172
    // for (i in 1:n_n) {
1173
    //
1174
    //   ## =================================================================
1175
    //   ## 'DF_n_i' and 'DR_on_i' are matrices with
1176
    //   ## dimension c(n_n, d)
1177
    //   ## =================================================================
1178
    //
1179
    //   DF_n_i <- trend.deltax(x = XNew[i, ], model = object)
1180
    //   KOldNewi <- as.vector(KOldNew[ , i])
1181
    //   DR_on_i <- covVector.dx(x = as.vector(XNew[i, ]),
1182
    //                               X = X,
1183
    //                               object = object@covariance,
1184
    //                               c = KOldNewi)
1185
    //
1186
    //   KOldNewStarDer[ , i, i, ] <-
1187
    //       backsolve(L, DR_on_i, upper.tri = FALSE)
1188
    //
1189
    //   ## Gradient of the kriging trend and mean
1190
    //   muNewHatDer[i, i, ] <- crossprod(DF_n_i, betaHat)
1191
    //   ## dim in product c(d, n) and NULL(length d)
1192
    //   mean_nHatDer[i, i, ] <- muNewHatDer[i, i, ] +
1193
    //       crossprod(KOldNewStarDer[ , i, i,  ], zStar)
1194
    //   ## dim in product c(d, n) and NULL(length n)
1195
    //   s2Der[i, i,  ] <-
1196
    //       - 2 * crossprod(KOldNewStarDer[ , i, i, ],
1197
    //                       drop(KOldNewStar[ , i]))
1198
    //
1199
    //   ## dim in product c(d, n) and c(n, p)
1200
    //
1201
    //   if (type == "UK") {
1202
    //       ENewDagDer[i, i, , ] <-
1203
    //           backsolve(t(RStar),
1204
    //                     DF_n_i -
1205
    //                     t(crossprod(KOldNewStarDer[ , i, i, ], FStar)),
1206
    //                     upper.tri = FALSE)
1207
    //       ## dim in product NULL (length p) and c(p, d) because of 'drop'
1208
    //       s2Der[i, i, ] <- s2Der[i, i, ] + 2 * drop(ENewDagT[ , i]) %*%
1209
    //           drop(ENewDagDer[i, i, , ])
1210
    //   }
1211
    //  numerical derivative step : value is sensitive only for non linear trends. Otherwise, it gives exact results.
1212
    const double h = 1.0E-5;
×
1213
    arma::mat h_eye_d = h * arma::mat(d, d, arma::fill::eye);
×
1214

1215
    // Compute the derivatives of the covariance and trend functions
1216
    for (arma::uword i = 0; i < n_n; i++) {  // for each predict point... should be parallel ?
×
1217
      arma::mat DR_on_i = arma::mat(n_o, d, arma::fill::none);
×
1218
      for (arma::uword j = 0; j < n_o; j++) {
×
1219
        DR_on_i.row(j) = R_on.at(j, i) * trans(_DlnCovDx(Xn_n.col(i) - Xn_o.col(j), m_theta));
×
1220
      }
1221
      t0 = Bench::toc(nullptr, "DR_on_i    ", t0);
×
1222

1223
      arma::mat tXn_n_repd_i
1224
          = arma::trans(Xn_n.col(i) * arma::mat(1, d, arma::fill::ones));  // just duplicate X_n.row(i) d times
×
1225
      arma::mat DF_n_i = (Trend::regressionModelMatrix(m_regmodel, tXn_n_repd_i + h_eye_d)
×
1226
                          - Trend::regressionModelMatrix(m_regmodel, tXn_n_repd_i - h_eye_d))
×
1227
                         / (2 * h);
×
1228
      t0 = Bench::toc(nullptr, "DF_n_i     ", t0);
×
1229

1230
      arma::mat W_i = LinearAlgebra::solve(m_T, DR_on_i);
×
1231
      t0 = Bench::toc(nullptr, "W_i        ", t0);
×
1232
      Dyhat_n.row(i) = trans(DF_n_i * m_beta + trans(W_i) * m_z);
×
1233
      t0 = Bench::toc(nullptr, "Dyhat_n    ", t0);
×
1234

1235
      if (return_stdev) {
×
1236
        arma::mat DEcirc_n_i = LinearAlgebra::solve(m_circ.t(), trans(DF_n_i - W_i.t() * m_M));
×
1237
        Dysd2_n.row(i) = -2 * Rstar_on.col(i).t() * W_i + 2 * Ecirc_n.row(i) * DEcirc_n_i;
×
1238
        t0 = Bench::toc(nullptr, "Dysd2_n    ", t0);
×
1239
      }
×
1240
    }
×
1241
    Dyhat_n *= m_scaleY;
×
1242
    Dysd2_n *= sigma2 / m_alpha * m_scaleY * m_scaleY;
×
1243
  }
×
1244

1245
  return std::make_tuple(std::move(yhat_n),
×
1246
                         std::move(arma::sqrt(ysd2_n)),
×
1247
                         std::move(Sigma_n),
×
1248
                         std::move(Dyhat_n),
×
1249
                         std::move(Dysd2_n / (2 * arma::sqrt(ysd2_n) * arma::mat(1, d, arma::fill::ones))));
×
1250
  /*if (return_stdev)
1251
    if (return_cov)
1252
      return std::make_tuple(std::move(yhat_n), std::move(pred_stdev), std::move(pred_cov));
1253
    else
1254
      return std::make_tuple(std::move(yhat_n), std::move(pred_stdev), nullptr);
1255
  else if (return_cov)
1256
    return std::make_tuple(std::move(yhat_n), std::move(pred_cov), nullptr);
1257
  else
1258
    return std::make_tuple(std::move(yhat_n), nullptr, nullptr);*/
1259
}
×
1260

1261
/** Draw sample trajectories of kriging at given points X'
1262
 * @param X_n is n_n*d matrix of points where to simulate output
1263
 * @param seed is seed for random number generator
1264
 * @param nsim is number of simulations to draw
1265
 * @param with_nugget is true if we want to include nugget effect in simulations
1266
 * @param will_update is true if we want to keep simulations data for future update
1267
 * @return output is n_n*nsim matrix of simulations at X_n
1268
 */
1269
LIBKRIGING_EXPORT arma::mat NuggetKriging::simulate(const int nsim,
×
1270
                                                    const int seed,
1271
                                                    const arma::mat& X_n,
1272
                                                    const bool with_nugget,
1273
                                                    const bool will_update) {
1274
  arma::uword n_n = X_n.n_rows;
×
1275
  arma::uword n_o = m_X.n_rows;
×
1276
  arma::uword d = m_X.n_cols;
×
1277
  if (X_n.n_cols != d)
×
1278
    throw std::runtime_error("Simulate locations have wrong dimension: " + std::to_string(X_n.n_cols) + " instead of "
×
1279
                             + std::to_string(d));
×
1280

1281
  arma::mat Xn_o = trans(m_X);  // already normalized if needed
×
1282
  arma::mat Xn_n = X_n;
×
1283
  // Normalize X_n
1284
  Xn_n.each_row() -= m_centerX;
×
1285
  Xn_n.each_row() /= m_scaleX;
×
1286

1287
  // Define regression matrix
1288
  arma::mat F_n = Trend::regressionModelMatrix(m_regmodel, Xn_n);
×
1289

1290
  Xn_n = trans(Xn_n);
×
1291

1292
  double m_alpha = m_sigma2 / (m_sigma2 + m_nugget);
×
1293

1294
  auto t0 = Bench::tic();
×
1295
  // Compute covariance between new data
1296
  arma::mat R_nn = arma::mat(n_n, n_n, arma::fill::none);
×
1297
  for (arma::uword i = 0; i < n_n; i++) {
×
1298
    // R_nn.at(i, i) = 1.0;
1299
    for (arma::uword j = 0; j < i; j++) {
×
1300
      R_nn.at(i, j) = R_nn.at(j, i) = _Cov((Xn_n.col(i) - Xn_n.col(j)), m_theta);
×
1301
    }
1302
  }
1303
  R_nn.diag().ones();  // replaces R_nn.at(i, i) = 1.0;
×
1304
  R_nn *= m_alpha;
×
1305
  if (with_nugget)
×
1306
    R_nn.diag().ones();  // increase diag to have nugget
×
1307
  t0 = Bench::toc(nullptr, "R_nn          ", t0);
×
1308

1309
  // Compute covariance between training data and new data to predict
1310
  arma::mat R_on = arma::mat(n_o, n_n, arma::fill::none);
×
1311
  for (arma::uword i = 0; i < n_o; i++) {
×
1312
    for (arma::uword j = 0; j < n_n; j++) {
×
1313
      arma::mat dij = Xn_o.col(i) - Xn_n.col(j);
×
1314
      if (with_nugget && dij.is_zero(arma::datum::eps))
×
1315
        R_on.at(i, j) = 1.0;
×
1316
      else
1317
        R_on.at(i, j) = _Cov(dij, m_theta) * m_alpha;  // force m_alpha here, to be consistent with R_oo (=T*t(T))
×
1318
      // R_on.at(i, j) = _Cov(Xn_o.col(i) - Xn_n.col(j), m_theta);
1319
    }
×
1320
  }
1321
  // R_on *= alpha;
1322
  t0 = Bench::toc(nullptr, "R_on       ", t0);
×
1323

1324
  arma::mat Rstar_on = LinearAlgebra::solve(m_T, R_on);
×
1325
  t0 = Bench::toc(nullptr, "Rstar_on   ", t0);
×
1326

1327
  arma::vec yhat_n = F_n * m_beta + trans(Rstar_on) * m_z;
×
1328
  t0 = Bench::toc(nullptr, "yhat_n        ", t0);
×
1329

1330
  arma::mat Fhat_n = trans(Rstar_on) * m_M;
×
1331
  arma::mat E_n = F_n - Fhat_n;
×
1332
  arma::mat Ecirc_n = LinearAlgebra::rsolve(m_circ, E_n);
×
1333
  t0 = Bench::toc(nullptr, "Ecirc_n       ", t0);
×
1334

1335
  arma::mat SigmaNoTrend_nKo = R_nn - trans(Rstar_on) * Rstar_on;
×
1336

1337
  arma::mat Sigma_nKo = SigmaNoTrend_nKo + Ecirc_n * trans(Ecirc_n);
×
1338
  t0 = Bench::toc(nullptr, "Sigma_nKo     ", t0);
×
1339

1340
  arma::mat LSigma_nKo = LinearAlgebra::safe_chol_lower(Sigma_nKo / m_alpha);
×
1341
  t0 = Bench::toc(nullptr, "LSigma_nKo     ", t0);
×
1342

1343
  arma::mat y_n = arma::mat(n_n, nsim, arma::fill::none);
×
1344
  y_n.each_col() = yhat_n;
×
1345
  Random::reset_seed(seed);
×
1346
  y_n += LSigma_nKo * Random::randn_mat(n_n, nsim) * std::sqrt(m_sigma2);
×
1347

1348
  // Un-normalize simulations
1349
  y_n = m_centerY + m_scaleY * y_n;
×
1350

1351
  if (will_update) {
×
1352
    lastsimup_Xn_u.clear();  // force reset to force update_simulate consider new data
×
1353
    lastsim_y_n = y_n;
×
1354

1355
    lastsim_Xn_n = Xn_n;
×
1356
    lastsim_seed = seed;
×
1357
    lastsim_nsim = nsim;
×
1358
    lastsim_with_nugget = with_nugget;
×
1359

1360
    lastsim_R_nn = R_nn;
×
1361
    lastsim_F_n = F_n;
×
1362

1363
    lastsim_L_oCn = Rstar_on;
×
1364
    lastsim_L_nCn = LinearAlgebra::safe_chol_lower(SigmaNoTrend_nKo);
×
1365
    t0 = Bench::toc(nullptr, "L_nCn     ", t0);
×
1366

1367
    lastsim_L_on = arma::join_rows(arma::join_cols(m_T, lastsim_L_oCn.t()),
×
1368
                                   arma::join_cols(arma::zeros(n_o, n_n), lastsim_L_nCn));
×
1369

1370
    arma::mat Linv_on = LinearAlgebra::solve(lastsim_L_on, arma::mat(n_o + n_n, n_o + n_n, arma::fill::eye));
×
1371
    t0 = Bench::toc(nullptr, "Linv_on     ", t0);
×
1372
    lastsim_Rinv_on = Linv_on.t() * Linv_on;
×
1373
    t0 = Bench::toc(nullptr, "Rinv_on     ", t0);
×
1374

1375
    lastsim_F_on = arma::join_cols(m_F, lastsim_F_n);
×
1376
    lastsim_Fstar_on = LinearAlgebra::solve(lastsim_L_on, lastsim_F_on);
×
1377
    t0 = Bench::toc(nullptr, "Fstar_on     ", t0);
×
1378
    arma::mat Q_Fstar_on;
×
1379
    arma::qr(Q_Fstar_on, lastsim_circ_on, lastsim_Fstar_on);
×
1380
    lastsim_Fcirc_on = LinearAlgebra::rsolve(lastsim_circ_on, lastsim_F_on);
×
1381
    t0 = Bench::toc(nullptr, "Fcirc_on     ", t0);
×
1382

1383
    lastsim_Fhat_nKo = lastsim_L_oCn.t() * m_M;
×
1384
    t0 = Bench::toc(nullptr, "Fhat_nKo     ", t0);
×
1385
    lastsim_Ecirc_nKo = LinearAlgebra::rsolve(m_circ, F_n - lastsim_Fhat_nKo);
×
1386
    t0 = Bench::toc(nullptr, "Ecirc_nKo     ", t0);
×
1387
  }
×
1388

1389
  // Un-normalize simulations
1390
  return y_n;
×
1391
}
×
1392

1393
LIBKRIGING_EXPORT arma::mat NuggetKriging::update_simulate(const arma::vec& y_u, const arma::mat& X_u) {
×
1394
  if (y_u.n_elem != X_u.n_rows)
×
1395
    throw std::runtime_error("Dimension of new data should be the same:\n X: (" + std::to_string(X_u.n_rows) + "x"
×
1396
                             + std::to_string(X_u.n_cols) + "), y: (" + std::to_string(y_u.n_elem) + ")");
×
1397

1398
  if (X_u.n_cols != m_X.n_cols)
×
1399
    throw std::runtime_error("Dimension of new data should be the same:\n X: (...x" + std::to_string(m_X.n_cols)
×
1400
                             + "), new X: (...x" + std::to_string(X_u.n_cols) + ")");
×
1401

1402
  if (lastsim_y_n.is_empty() || lastsim_y_n.n_rows == 0)
×
1403
    throw std::runtime_error("No previous simulation data available");
×
1404

1405
  if (lastsim_Xn_n.n_rows != X_u.n_cols)
×
1406
    throw std::runtime_error("Dimension of new data should be the same:\n X: (...x" + std::to_string(X_u.n_cols)
×
1407
                             + "), last sim X: (...x" + std::to_string(lastsim_Xn_n.n_rows) + ")");
×
1408

1409
  arma::uword n_n = lastsim_Xn_n.n_cols;
×
1410
  arma::uword n_o = m_X.n_rows;
×
1411
  arma::uword d = m_X.n_cols;
×
1412
  arma::mat Xn_o = trans(m_X);    // already normalized if needed
×
1413
  arma::mat Xn_n = lastsim_Xn_n;  // already normalized
×
1414

1415
  arma::uword n_on = n_o + n_n;
×
1416
  // arma::mat Xn_on = arma::join_cols(Xn_o, Xn_n);
1417
  arma::mat F_on = arma::join_cols(m_F, lastsim_F_n);
×
1418

1419
  arma::uword n_u = X_u.n_rows;
×
1420
  // Normalize X_u
1421
  arma::mat Xn_u = X_u;
×
1422
  Xn_u.each_row() -= m_centerX;
×
1423
  Xn_u.each_row() /= m_scaleX;
×
1424

1425
  // Define regression matrix
1426
  arma::mat F_u = Trend::regressionModelMatrix(m_regmodel, Xn_u);
×
1427

1428
  auto t0 = Bench::tic();
×
1429
  Xn_u = trans(Xn_u);
×
1430
  t0 = Bench::toc(nullptr, "Xn_u.t()      ", t0);
×
1431

1432
  double m_alpha = m_sigma2 / (m_sigma2 + m_nugget);
×
1433

1434
  bool use_lastsimup = (!lastsimup_Xn_u.is_empty()) && (lastsimup_Xn_u - Xn_u).is_zero(arma::datum::eps);
×
1435
  if (!use_lastsimup) {
×
1436
    lastsimup_Xn_u = Xn_u;
×
1437

1438
    // Compute covariance between updated data
1439
    lastsimup_R_uu = arma::mat(n_u, n_u, arma::fill::none);
×
1440
    for (arma::uword i = 0; i < n_u; i++) {
×
1441
      // lastsimup_R_uu.at(i, i) = 1.0;
1442
      for (arma::uword j = 0; j < i; j++) {
×
1443
        lastsimup_R_uu.at(i, j) = lastsimup_R_uu.at(j, i) = _Cov((Xn_u.col(i) - Xn_u.col(j)), m_theta);
×
1444
      }
1445
    }
1446
    lastsimup_R_uu.diag().ones();  // replaces R_uu.at(i, i) = 1.0;
×
1447
    lastsimup_R_uu *= m_alpha;
×
1448
    if (lastsim_with_nugget)
×
1449
      lastsimup_R_uu.diag().ones();  // so diag will include nugget
×
1450
    t0 = Bench::toc(nullptr, "R_uu          ", t0);
×
1451

1452
    // Compute covariance between updated/old data
1453
    lastsimup_R_uo = arma::mat(n_u, n_o, arma::fill::none);
×
1454
    for (arma::uword i = 0; i < n_u; i++) {
×
1455
      for (arma::uword j = 0; j < n_o; j++) {
×
1456
        lastsimup_R_uo.at(i, j) = _Cov((Xn_u.col(i) - Xn_o.col(j)), m_theta);
×
1457
      }
1458
    }
1459
    lastsimup_R_uo *= m_alpha;
×
1460
    t0 = Bench::toc(nullptr, "R_uo          ", t0);
×
1461

1462
    // Compute covariance between updated/new data
1463
    lastsimup_R_un = arma::mat(n_u, n_n, arma::fill::none);
×
1464
    for (arma::uword i = 0; i < n_u; i++) {
×
1465
      for (arma::uword j = 0; j < n_n; j++) {
×
1466
        arma::vec dij = Xn_u.col(i) - Xn_n.col(j);
×
1467
        if (lastsim_with_nugget && dij.is_zero(arma::datum::eps))
×
1468
          lastsimup_R_un.at(i, j) = 1.0;
×
1469
        else
1470
          lastsimup_R_un.at(i, j) = _Cov(dij, m_theta) * m_alpha;
×
1471
        // lastsimup_R_un.at(i, j) = _Cov(Xn_u.col(i) - Xn_n.col(j), m_theta) * m_alpha;
1472
      }
×
1473
    }
1474
    t0 = Bench::toc(nullptr, "R_un          ", t0);
×
1475
  }
1476

1477
  // ======================================================================
1478
  // FOXY step #1 Extend the simulation to the design 'X_u' IF
1479
  // NECESSARY. Remind that the simulation number j is
1480
  // conditional on the given 'y_o' and on 'y_n = Y_sim[ , j]'
1481
  //
1482
  // CAUTION. To avoid unnecessary re-computations we here use
1483
  // auxiliary variables that where computed in the creation of
1484
  // the KM0 step AND later in the simulation. The first ones are
1485
  // in 'theKM0$Extra' and the second ones are in 'Ex'
1486
  //
1487
  // In indices 'C' means comma and 'K' means pipe '|'
1488
  //
1489
  // ======================================================================
1490

1491
  if (!use_lastsimup) {
×
1492
    arma::mat R_onCu = arma::join_rows(lastsimup_R_uo, lastsimup_R_un).t();
×
1493
    arma::mat Rstar_onCu = LinearAlgebra::solve(lastsim_L_on, R_onCu);
×
1494
    t0 = Bench::toc(nullptr, "Rstar_onCu          ", t0);
×
1495

1496
    arma::mat Ecirc_uKon = LinearAlgebra::rsolve(lastsim_circ_on, F_u - Rstar_onCu.t() * lastsim_Fstar_on);
×
1497
    t0 = Bench::toc(nullptr, "Ecirc_uKon          ", t0);
×
1498

1499
    arma::mat Sigma_uKon = lastsimup_R_uu - Rstar_onCu.t() * Rstar_onCu + Ecirc_uKon * Ecirc_uKon.t();
×
1500
    t0 = Bench::toc(nullptr, "Sigma_uKon          ", t0);
×
1501

1502
    arma::mat LSigma_uKon = LinearAlgebra::safe_chol_lower(Sigma_uKon / m_alpha);
×
1503
    t0 = Bench::toc(nullptr, "LSigma_uKon          ", t0);
×
1504

1505
    arma::mat W_uCon = (R_onCu.t() + Ecirc_uKon * lastsim_Fcirc_on.t()) * lastsim_Rinv_on;
×
1506
    t0 = Bench::toc(nullptr, "W_uCon          ", t0);
×
1507

1508
    arma::mat m_u = W_uCon.head_cols(n_o) * m_y;
×
1509
    arma::mat M_u = arma::repmat(m_u, 1, lastsim_nsim) + W_uCon.tail_cols(n_n) * lastsim_y_n;
×
1510

1511
    Random::reset_seed(lastsim_seed);
×
1512
    lastsimup_y_u = M_u + LSigma_uKon * Random::randn_mat(n_u, lastsim_nsim) * std::sqrt(m_sigma2);
×
1513
    t0 = Bench::toc(nullptr, "y_u          ", t0);
×
1514
  }
×
1515

1516
  // ======================================================================
1517
  // FOXY step #2   Update the simulated paths on 'X_n'
1518
  // ======================================================================
1519

1520
  if (!use_lastsimup) {
×
1521
    arma::mat Rstar_ou = LinearAlgebra::solve(m_T, lastsimup_R_uo.t());
×
1522
    t0 = Bench::toc(nullptr, "Rstar_ou          ", t0);
×
1523

1524
    arma::mat Fhat_uKo = Rstar_ou.t() * m_M;
×
1525
    arma::mat Ecirc_uKo = LinearAlgebra::rsolve(m_circ, F_u - Fhat_uKo);
×
1526
    t0 = Bench::toc(nullptr, "Ecirc_uKo          ", t0);
×
1527

1528
    arma::mat Rtild_uCu = lastsimup_R_uu - Rstar_ou.t() * Rstar_ou + Ecirc_uKo * Ecirc_uKo.t();
×
1529
    t0 = Bench::toc(nullptr, "Rtild_uCu          ", t0);
×
1530

1531
    arma::mat Rtild_nCu = lastsimup_R_un - Rstar_ou.t() * lastsim_L_oCn + Ecirc_uKo * lastsim_Ecirc_nKo.t();
×
1532
    t0 = Bench::toc(nullptr, "Rtild_nCu          ", t0);
×
1533

1534
    lastsimup_Wtild_nKu = LinearAlgebra::solve(Rtild_uCu, Rtild_nCu).t();
×
1535
    t0 = Bench::toc(nullptr, "Wtild_nKu          ", t0);
×
1536
  }
×
1537

1538
  return lastsim_y_n + lastsimup_Wtild_nKu * (arma::repmat(y_u, 1, lastsim_nsim) - lastsimup_y_u);
×
1539
}
×
1540

1541
/** Add new conditional data points to previous (X,y), then perform new fit.
1542
 * @param y_u is n_u length column vector of new output
1543
 * @param X_u is n_u*d matrix of new input
1544
 * @param refit is true if we want to re-fit the model
1545
 */
1546
LIBKRIGING_EXPORT void NuggetKriging::update(const arma::vec& y_u, const arma::mat& X_u, const bool refit) {
×
1547
  if (y_u.n_elem != X_u.n_rows)
×
1548
    throw std::runtime_error("Dimension of new data should be the same:\n X: (" + std::to_string(X_u.n_rows) + "x"
×
1549
                             + std::to_string(X_u.n_cols) + "), y: (" + std::to_string(y_u.n_elem) + ")");
×
1550

1551
  if (X_u.n_cols != m_X.n_cols)
×
1552
    throw std::runtime_error("Dimension of new data should be the same:\n X: (...x" + std::to_string(m_X.n_cols)
×
1553
                             + "), new X: (...x" + std::to_string(X_u.n_cols) + ")");
×
1554
  // rebuild starting parameters
1555
  Parameters parameters;
×
1556
  if (refit) {  // re-fit
×
1557
    if (m_est_beta)
×
1558
      parameters = Parameters{
×
1559
          std::make_optional(arma::vec(1, arma::fill::value(this->m_nugget * this->m_scaleY * this->m_scaleY))),
×
1560
          this->m_est_nugget,
×
1561
          std::make_optional(arma::vec(1, arma::fill::value(this->m_sigma2 * this->m_scaleY * this->m_scaleY))),
×
1562
          this->m_est_sigma2,
×
1563
          std::make_optional(trans(this->m_theta) % this->m_scaleX),
×
1564
          this->m_est_theta,
×
1565
          std::make_optional(arma::ones<arma::vec>(0)),
×
1566
          true};
×
1567
    else
1568
      parameters = Parameters{
×
1569
          std::make_optional(arma::vec(1, arma::fill::value(this->m_nugget * this->m_scaleY * this->m_scaleY))),
×
1570
          this->m_est_nugget,
×
1571
          std::make_optional(arma::vec(1, arma::fill::value(this->m_sigma2 * this->m_scaleY * this->m_scaleY))),
×
1572
          this->m_est_sigma2,
×
1573
          std::make_optional(trans(this->m_theta) % this->m_scaleX),
×
1574
          this->m_est_theta,
×
1575
          std::make_optional(trans(this->m_beta) * this->m_scaleY),
×
1576
          false};
×
1577
    this->fit(arma::join_cols(m_y * this->m_scaleY + this->m_centerY,
×
1578
                              y_u),  // de-normalize previous data according to suite unnormed new data
1579
              arma::join_cols((m_X.each_row() % this->m_scaleX).each_row() + this->m_centerX, X_u),
×
1580
              m_regmodel,
×
1581
              m_normalize,
×
1582
              m_optim,
×
1583
              m_objective,
×
1584
              parameters);
1585
  } else {  // just update
1586
    parameters = Parameters{
×
1587
        std::make_optional(arma::vec(1, arma::fill::value(this->m_nugget * this->m_scaleY * this->m_scaleY))),
×
1588
        false,
1589
        std::make_optional(arma::vec(1, arma::fill::value(this->m_sigma2 * this->m_scaleY * this->m_scaleY))),
×
1590
        false,
1591
        std::make_optional(trans(this->m_theta) % this->m_scaleX),
×
1592
        false,
1593
        std::make_optional(arma::ones<arma::vec>(0)),
×
1594
        false};
×
1595
    this->fit(arma::join_cols(m_y * this->m_scaleY + this->m_centerY,
×
1596
                              y_u),  // de-normalize previous data according to suite unnormed new data
1597
              arma::join_cols((m_X.each_row() % this->m_scaleX).each_row() + this->m_centerX, X_u),
×
1598
              m_regmodel,
×
1599
              m_normalize,
×
1600
              "none",
1601
              m_objective,
×
1602
              parameters);
1603
  }
1604
}
×
1605

1606
LIBKRIGING_EXPORT std::string NuggetKriging::summary() const {
×
1607
  std::ostringstream oss;
×
1608
  auto vec_printer = [&oss](const arma::vec& v) {
×
1609
    v.for_each([&oss, i = 0](const arma::vec::elem_type& val) mutable {
×
1610
      if (i++ > 0)
×
1611
        oss << ", ";
×
1612
      oss << val;
×
1613
    });
×
1614
  };
×
1615

1616
  if (m_X.is_empty() || m_X.n_rows == 0) {  // not yet fitted
×
1617
    oss << "* covariance:\n";
×
1618
    oss << "  * kernel: " << m_covType << "\n";
×
1619
  } else {
1620
    oss << "* data";
×
1621
    oss << ((m_normalize) ? " (normalized): " : ": ") << m_X.n_rows << "x";
×
1622
    arma::rowvec Xmins = arma::min(m_X, 0);
×
1623
    arma::rowvec Xmaxs = arma::max(m_X, 0);
×
1624
    for (arma::uword i = 0; i < m_X.n_cols; i++) {
×
1625
      oss << "[" << Xmins[i] << "," << Xmaxs[i] << "]";
×
1626
      if (i < m_X.n_cols - 1)
×
1627
        oss << ",";
×
1628
    }
1629
    oss << " -> " << m_y.n_elem << "x[" << arma::min(m_y) << "," << arma::max(m_y) << "]\n";
×
1630
    oss << "* trend " << Trend::toString(m_regmodel);
×
1631
    oss << ((m_est_beta) ? " (est.): " : ": ");
×
1632
    vec_printer(m_beta);
×
1633
    oss << "\n";
×
1634
    oss << "* variance";
×
1635
    oss << ((m_est_sigma2) ? " (est.): " : ": ");
×
1636
    oss << m_sigma2;
×
1637
    oss << "\n";
×
1638
    oss << "* covariance:\n";
×
1639
    oss << "  * kernel: " << m_covType << "\n";
×
1640
    oss << "  * range";
×
1641
    oss << ((m_est_theta) ? " (est.): " : ": ");
×
1642
    vec_printer(m_theta);
×
1643
    oss << "\n";
×
1644
    oss << "  * nugget";
×
1645
    oss << ((m_est_nugget) ? " (est.): " : ": ");
×
1646
    oss << m_nugget;
×
1647
    oss << "\n";
×
1648
    oss << "  * fit:\n";
×
1649
    oss << "    * objective: " << m_objective << "\n";
×
1650
    oss << "    * optim: " << m_optim << "\n";
×
1651
  }
×
1652
  return oss.str();
×
1653
}
×
1654

1655
void NuggetKriging::save(const std::string filename) const {
×
1656
  nlohmann::json j;
×
1657

1658
  j["version"] = 2;
×
1659
  j["content"] = "NuggetKriging";
×
1660

1661
  // _Cov_pow & std::function embedded by make_Cov
1662
  j["covType"] = m_covType;
×
1663
  j["X"] = to_json(m_X);
×
1664
  j["centerX"] = to_json(m_centerX);
×
1665
  j["scaleX"] = to_json(m_scaleX);
×
1666
  j["y"] = to_json(m_y);
×
1667
  j["centerY"] = m_centerY;
×
1668
  j["scaleY"] = m_scaleY;
×
1669
  j["normalize"] = m_normalize;
×
1670
  j["regmodel"] = Trend::toString(m_regmodel);
×
1671
  j["optim"] = m_optim;
×
1672
  j["objective"] = m_objective;
×
1673
  // Auxiliary data
1674
  j["dX"] = to_json(m_dX);
×
1675
  j["maxdX"] = to_json(m_maxdX);
×
1676
  j["F"] = to_json(m_F);
×
1677
  j["T"] = to_json(m_T);
×
1678
  j["R"] = to_json(m_R);
×
1679
  j["M"] = to_json(m_M);
×
1680
  j["star"] = to_json(m_star);
×
1681
  j["circ"] = to_json(m_circ);
×
1682
  j["z"] = to_json(m_z);
×
1683
  j["beta"] = to_json(m_beta);
×
1684
  j["est_beta"] = m_est_beta;
×
1685
  j["theta"] = to_json(m_theta);
×
1686
  j["est_theta"] = m_est_theta;
×
1687
  j["sigma2"] = m_sigma2;
×
1688
  j["est_sigma2"] = m_est_sigma2;
×
1689
  j["nugget"] = m_nugget;
×
1690
  j["est_nugget"] = m_est_nugget;
×
1691

1692
  std::ofstream f(filename);
×
1693
  f << std::setw(4) << j;
×
1694
}
×
1695

1696
NuggetKriging NuggetKriging::load(const std::string filename) {
×
1697
  std::ifstream f(filename);
×
1698
  nlohmann::json j = nlohmann::json::parse(f);
×
1699

1700
  uint32_t version = j["version"].template get<uint32_t>();
×
1701
  if (version != 2) {
×
1702
    throw std::runtime_error(asString("Bad version to load from '", filename, "'; found ", version, ", requires 2"));
×
1703
  }
1704
  std::string content = j["content"].template get<std::string>();
×
1705
  if (content != "NuggetKriging") {
×
1706
    throw std::runtime_error(
×
1707
        asString("Bad content to load from '", filename, "'; found '", content, "', requires 'NuggetKriging'"));
×
1708
  }
1709

1710
  std::string covType = j["covType"].template get<std::string>();
×
1711
  NuggetKriging kr(covType);  // _Cov_pow & std::function embedded by make_Cov
×
1712

1713
  kr.m_X = mat_from_json(j["X"]);
×
1714
  kr.m_centerX = rowvec_from_json(j["centerX"]);
×
1715
  kr.m_scaleX = rowvec_from_json(j["scaleX"]);
×
1716
  kr.m_y = colvec_from_json(j["y"]);
×
1717
  kr.m_centerY = j["centerY"].template get<decltype(kr.m_centerY)>();
×
1718
  kr.m_scaleY = j["scaleY"].template get<decltype(kr.m_scaleY)>();
×
1719
  kr.m_normalize = j["normalize"].template get<decltype(kr.m_normalize)>();
×
1720

1721
  std::string model = j["regmodel"].template get<std::string>();
×
1722
  kr.m_regmodel = Trend::fromString(model);
×
1723

1724
  kr.m_optim = j["optim"].template get<decltype(kr.m_optim)>();
×
1725
  kr.m_objective = j["objective"].template get<decltype(kr.m_objective)>();
×
1726
  // Auxiliary data
1727
  kr.m_dX = mat_from_json(j["dX"]);
×
1728
  kr.m_maxdX = colvec_from_json(j["maxdX"]);
×
1729
  kr.m_F = mat_from_json(j["F"]);
×
1730
  kr.m_T = mat_from_json(j["T"]);
×
1731
  kr.m_R = mat_from_json(j["R"]);
×
1732
  kr.m_M = mat_from_json(j["M"]);
×
1733
  kr.m_star = mat_from_json(j["star"]);
×
1734
  kr.m_circ = mat_from_json(j["circ"]);
×
1735
  kr.m_z = colvec_from_json(j["z"]);
×
1736
  kr.m_beta = colvec_from_json(j["beta"]);
×
1737
  kr.m_est_beta = j["est_beta"].template get<decltype(kr.m_est_beta)>();
×
1738
  kr.m_theta = colvec_from_json(j["theta"]);
×
1739
  kr.m_est_theta = j["est_theta"].template get<decltype(kr.m_est_theta)>();
×
1740
  kr.m_sigma2 = j["sigma2"].template get<decltype(kr.m_sigma2)>();
×
1741
  kr.m_est_sigma2 = j["est_sigma2"].template get<decltype(kr.m_est_sigma2)>();
×
1742
  kr.m_nugget = j["nugget"].template get<decltype(kr.m_nugget)>();
×
1743
  kr.m_est_nugget = j["est_nugget"].template get<decltype(kr.m_est_nugget)>();
×
1744
  kr.m_is_empty = false;
×
1745

1746
  return kr;
×
1747
}
×
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

© 2026 Coveralls, Inc