summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/optimization/optimization_bobyqa.h
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/optimization/optimization_bobyqa.h')
-rw-r--r--ml/dlib/dlib/optimization/optimization_bobyqa.h3423
1 files changed, 3423 insertions, 0 deletions
diff --git a/ml/dlib/dlib/optimization/optimization_bobyqa.h b/ml/dlib/dlib/optimization/optimization_bobyqa.h
new file mode 100644
index 000000000..6fbc40c06
--- /dev/null
+++ b/ml/dlib/dlib/optimization/optimization_bobyqa.h
@@ -0,0 +1,3423 @@
+// Copyright (C) 2009 M.J.D. Powell, Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_OPTIMIZATIOn_BOBYQA_Hh_
+#define DLIB_OPTIMIZATIOn_BOBYQA_Hh_
+
+/*
+ The code in this file is derived from Powell's BOBYQA Fortran code.
+ It was created by running f2c on the original Fortran code and then
+ massaging the resulting C code into what you can see below.
+
+
+ The following paper, published in 2009 by Powell, describes the
+ detailed workings of the BOBYQA algorithm.
+
+ The BOBYQA algorithm for bound constrained optimization
+ without derivatives by M.J.D. Powell
+*/
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+
+#include "../matrix.h"
+#include "optimization_bobyqa_abstract.h"
+#include "optimization.h"
+
+// ----------------------------------------------------------------------------------------
+
+namespace dlib
+{
+
+ class bobyqa_failure : public error {
+ public: bobyqa_failure(const std::string& s):error(s){}
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ class bobyqa_implementation
+ {
+ typedef long integer;
+ typedef double doublereal;
+
+ public:
+
+ template <
+ typename funct,
+ typename T,
+ typename U
+ >
+ double find_min (
+ const funct& f,
+ T& x,
+ long npt,
+ const U& xl_,
+ const U& xu_,
+ const double rhobeg,
+ const double rhoend,
+ const long max_f_evals
+ ) const
+ {
+ const unsigned long n = x.size();
+ const unsigned long w_size = (npt+5)*(npt+n)+3*n*(n+5)/2;
+ std::unique_ptr<doublereal[]> w(new doublereal[w_size]);
+
+ // make these temporary matrices becuse U might be some
+ // kind of matrix_exp that doesn't support taking the address
+ // of the first element.
+ matrix<double,0,1> xl(xl_);
+ matrix<double,0,1> xu(xu_);
+
+
+ return bobyqa_ (f,
+ x.size(),
+ npt,
+ &x(0),
+ &xl(0),
+ &xu(0),
+ rhobeg,
+ rhoend,
+ max_f_evals,
+ w.get() );
+ }
+
+ private:
+
+
+ template <typename funct>
+ doublereal bobyqa_(
+ const funct& calfun,
+ const integer n,
+ const integer npt,
+ doublereal *x,
+ const doublereal *xl,
+ const doublereal *xu,
+ const doublereal rhobeg,
+ const doublereal rhoend,
+ const integer maxfun,
+ doublereal *w
+ ) const
+ {
+
+ /* System generated locals */
+ integer i__1;
+ doublereal d__1, d__2;
+
+ /* Local variables */
+ integer j, id_, np, iw, igo, ihq, ixb, ixa, ifv, isl, jsl, ipq, ivl, ixn, ixo, ixp, isu, jsu, ndim;
+ doublereal temp, zero;
+ integer ibmat, izmat;
+
+
+ /* This subroutine seeks the least value of a function of many variables, */
+ /* by applying a trust region method that forms quadratic models by */
+ /* interpolation. There is usually some freedom in the interpolation */
+ /* conditions, which is taken up by minimizing the Frobenius norm of */
+ /* the change to the second derivative of the model, beginning with the */
+ /* zero matrix. The values of the variables are constrained by upper and */
+ /* lower bounds. The arguments of the subroutine are as follows. */
+
+ /* N must be set to the number of variables and must be at least two. */
+ /* NPT is the number of interpolation conditions. Its value must be in */
+ /* the interval [N+2,(N+1)(N+2)/2]. Choices that exceed 2*N+1 are not */
+ /* recommended. */
+ /* Initial values of the variables must be set in X(1),X(2),...,X(N). They */
+ /* will be changed to the values that give the least calculated F. */
+ /* For I=1,2,...,N, XL(I) and XU(I) must provide the lower and upper */
+ /* bounds, respectively, on X(I). The construction of quadratic models */
+ /* requires XL(I) to be strictly less than XU(I) for each I. Further, */
+ /* the contribution to a model from changes to the I-th variable is */
+ /* damaged severely by rounding errors if XU(I)-XL(I) is too small. */
+ /* RHOBEG and RHOEND must be set to the initial and final values of a trust */
+ /* region radius, so both must be positive with RHOEND no greater than */
+ /* RHOBEG. Typically, RHOBEG should be about one tenth of the greatest */
+ /* expected change to a variable, while RHOEND should indicate the */
+ /* accuracy that is required in the final values of the variables. An */
+ /* error return occurs if any of the differences XU(I)-XL(I), I=1,...,N, */
+ /* is less than 2*RHOBEG. */
+ /* MAXFUN must be set to an upper bound on the number of calls of CALFUN. */
+ /* The array W will be used for working space. Its length must be at least */
+ /* (NPT+5)*(NPT+N)+3*N*(N+5)/2. */
+
+ /* Parameter adjustments */
+ --w;
+ --xu;
+ --xl;
+ --x;
+
+ /* Function Body */
+ np = n + 1;
+
+ /* Return if the value of NPT is unacceptable. */
+ if (npt < n + 2 || npt > (n + 2) * np / 2) {
+ throw bobyqa_failure("Return from BOBYQA because NPT is not in the required interval");
+ //goto L40;
+ }
+
+ /* Partition the working space array, so that different parts of it can */
+ /* be treated separately during the calculation of BOBYQB. The partition */
+ /* requires the first (NPT+2)*(NPT+N)+3*N*(N+5)/2 elements of W plus the */
+ /* space that is taken by the last array in the argument list of BOBYQB. */
+
+ ndim = npt + n;
+ ixb = 1;
+ ixp = ixb + n;
+ ifv = ixp + n * npt;
+ ixo = ifv + npt;
+ igo = ixo + n;
+ ihq = igo + n;
+ ipq = ihq + n * np / 2;
+ ibmat = ipq + npt;
+ izmat = ibmat + ndim * n;
+ isl = izmat + npt * (npt - np);
+ isu = isl + n;
+ ixn = isu + n;
+ ixa = ixn + n;
+ id_ = ixa + n;
+ ivl = id_ + n;
+ iw = ivl + ndim;
+
+ /* Return if there is insufficient space between the bounds. Modify the */
+ /* initial X if necessary in order to avoid conflicts between the bounds */
+ /* and the construction of the first quadratic model. The lower and upper */
+ /* bounds on moves from the updated X are set now, in the ISL and ISU */
+ /* partitions of W, in order to provide useful and exact information about */
+ /* components of X that become within distance RHOBEG from their bounds. */
+
+ zero = 0.;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ temp = xu[j] - xl[j];
+ if (temp < rhobeg + rhobeg) {
+ throw bobyqa_failure("Return from BOBYQA because one of the differences in x_lower and x_upper is less than 2*rho_begin");
+ //goto L40;
+ }
+ jsl = isl + j - 1;
+ jsu = jsl + n;
+ w[jsl] = xl[j] - x[j];
+ w[jsu] = xu[j] - x[j];
+ if (w[jsl] >= -(rhobeg)) {
+ if (w[jsl] >= zero) {
+ x[j] = xl[j];
+ w[jsl] = zero;
+ w[jsu] = temp;
+ } else {
+ x[j] = xl[j] + rhobeg;
+ w[jsl] = -(rhobeg);
+ /* Computing MAX */
+ d__1 = xu[j] - x[j];
+ w[jsu] = std::max(d__1,rhobeg);
+ }
+ } else if (w[jsu] <= rhobeg) {
+ if (w[jsu] <= zero) {
+ x[j] = xu[j];
+ w[jsl] = -temp;
+ w[jsu] = zero;
+ } else {
+ x[j] = xu[j] - rhobeg;
+ /* Computing MIN */
+ d__1 = xl[j] - x[j], d__2 = -(rhobeg);
+ w[jsl] = std::min(d__1,d__2);
+ w[jsu] = rhobeg;
+ }
+ }
+ /* L30: */
+ }
+
+ /* Make the call of BOBYQB. */
+
+ return bobyqb_(calfun, n, npt, &x[1], &xl[1], &xu[1], rhobeg, rhoend, maxfun, &w[
+ ixb], &w[ixp], &w[ifv], &w[ixo], &w[igo], &w[ihq], &w[ipq], &w[
+ ibmat], &w[izmat], ndim, &w[isl], &w[isu], &w[ixn], &w[ixa], &w[
+ id_], &w[ivl], &w[iw]);
+ //L40:
+ ;
+ } /* bobyqa_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ template <typename funct>
+ doublereal bobyqb_(
+ const funct& calfun,
+ const integer n,
+ const integer npt,
+ doublereal *x,
+ const doublereal *xl,
+ const doublereal *xu,
+ const doublereal rhobeg,
+ const doublereal rhoend,
+ const integer maxfun,
+ doublereal *xbase,
+ doublereal *xpt,
+ doublereal *fval,
+ doublereal *xopt,
+ doublereal *gopt,
+ doublereal *hq,
+ doublereal *pq,
+ doublereal *bmat,
+ doublereal *zmat,
+ const integer ndim,
+ doublereal *sl,
+ doublereal *su,
+ doublereal *xnew,
+ doublereal *xalt,
+ doublereal *d__,
+ doublereal *vlag,
+ doublereal *w
+ ) const
+ {
+ /* System generated locals */
+ integer xpt_dim1, xpt_offset, bmat_dim1, bmat_offset, zmat_dim1,
+ zmat_offset, i__1, i__2, i__3;
+ doublereal d__1, d__2, d__3, d__4;
+
+ /* Local variables */
+ doublereal f = 0;
+ integer i__, j, k, ih, nf, jj, nh, ip, jp;
+ doublereal dx;
+ integer np;
+ doublereal den = 0, one = 0, ten = 0, dsq = 0, rho = 0, sum = 0, two = 0, diff = 0, half = 0, beta = 0, gisq = 0;
+ integer knew = 0;
+ doublereal temp, suma, sumb, bsum, fopt;
+ integer kopt = 0, nptm;
+ doublereal zero, curv;
+ integer ksav;
+ doublereal gqsq = 0, dist = 0, sumw = 0, sumz = 0, diffa = 0, diffb = 0, diffc = 0, hdiag = 0;
+ integer kbase;
+ doublereal alpha = 0, delta = 0, adelt = 0, denom = 0, fsave = 0, bdtol = 0, delsq = 0;
+ integer nresc, nfsav;
+ doublereal ratio = 0, dnorm = 0, vquad = 0, pqold = 0, tenth = 0;
+ integer itest;
+ doublereal sumpq, scaden;
+ doublereal errbig, cauchy, fracsq, biglsq, densav;
+ doublereal bdtest;
+ doublereal crvmin, frhosq;
+ doublereal distsq;
+ integer ntrits;
+ doublereal xoptsq;
+
+
+
+ /* The arguments N, NPT, X, XL, XU, RHOBEG, RHOEND, IPRINT and MAXFUN */
+ /* are identical to the corresponding arguments in SUBROUTINE BOBYQA. */
+ /* XBASE holds a shift of origin that should reduce the contributions */
+ /* from rounding errors to values of the model and Lagrange functions. */
+ /* XPT is a two-dimensional array that holds the coordinates of the */
+ /* interpolation points relative to XBASE. */
+ /* FVAL holds the values of F at the interpolation points. */
+ /* XOPT is set to the displacement from XBASE of the trust region centre. */
+ /* GOPT holds the gradient of the quadratic model at XBASE+XOPT. */
+ /* HQ holds the explicit second derivatives of the quadratic model. */
+ /* PQ contains the parameters of the implicit second derivatives of the */
+ /* quadratic model. */
+ /* BMAT holds the last N columns of H. */
+ /* ZMAT holds the factorization of the leading NPT by NPT submatrix of H, */
+ /* this factorization being ZMAT times ZMAT^T, which provides both the */
+ /* correct rank and positive semi-definiteness. */
+ /* NDIM is the first dimension of BMAT and has the value NPT+N. */
+ /* SL and SU hold the differences XL-XBASE and XU-XBASE, respectively. */
+ /* All the components of every XOPT are going to satisfy the bounds */
+ /* SL(I) .LEQ. XOPT(I) .LEQ. SU(I), with appropriate equalities when */
+ /* XOPT is on a constraint boundary. */
+ /* XNEW is chosen by SUBROUTINE TRSBOX or ALTMOV. Usually XBASE+XNEW is the */
+ /* vector of variables for the next call of CALFUN. XNEW also satisfies */
+ /* the SL and SU constraints in the way that has just been mentioned. */
+ /* XALT is an alternative to XNEW, chosen by ALTMOV, that may replace XNEW */
+ /* in order to increase the denominator in the updating of UPDATE. */
+ /* D is reserved for a trial step from XOPT, which is usually XNEW-XOPT. */
+ /* VLAG contains the values of the Lagrange functions at a new point X. */
+ /* They are part of a product that requires VLAG to be of length NDIM. */
+ /* W is a one-dimensional array that is used for working space. Its length */
+ /* must be at least 3*NDIM = 3*(NPT+N). */
+
+ /* Set some constants. */
+
+ /* Parameter adjustments */
+ zmat_dim1 = npt;
+ zmat_offset = 1 + zmat_dim1;
+ zmat -= zmat_offset;
+ xpt_dim1 = npt;
+ xpt_offset = 1 + xpt_dim1;
+ xpt -= xpt_offset;
+ --x;
+ --xl;
+ --xu;
+ --xbase;
+ --fval;
+ --xopt;
+ --gopt;
+ --hq;
+ --pq;
+ bmat_dim1 = ndim;
+ bmat_offset = 1 + bmat_dim1;
+ bmat -= bmat_offset;
+ --sl;
+ --su;
+ --xnew;
+ --xalt;
+ --d__;
+ --vlag;
+ --w;
+
+ /* Function Body */
+ half = .5;
+ one = 1.;
+ ten = 10.;
+ tenth = .1;
+ two = 2.;
+ zero = 0.;
+ np = n + 1;
+ nptm = npt - np;
+ nh = n * np / 2;
+
+ /* The call of PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ, */
+ /* BMAT and ZMAT for the first iteration, with the corresponding values of */
+ /* of NF and KOPT, which are the number of calls of CALFUN so far and the */
+ /* index of the interpolation point at the trust region centre. Then the */
+ /* initial XOPT is set too. The branch to label 720 occurs if MAXFUN is */
+ /* less than NPT. GOPT will be updated if KOPT is different from KBASE. */
+
+ prelim_(calfun, n, npt, &x[1], &xl[1], &xu[1], rhobeg, maxfun, &xbase[1],
+ &xpt[xpt_offset], &fval[1], &gopt[1], &hq[1], &pq[1], &bmat[bmat_offset],
+ &zmat[zmat_offset], ndim, &sl[1], &su[1], nf, kopt);
+ xoptsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ xopt[i__] = xpt[kopt + i__ * xpt_dim1];
+ /* L10: */
+ /* Computing 2nd power */
+ d__1 = xopt[i__];
+ xoptsq += d__1 * d__1;
+ }
+ fsave = fval[1];
+ if (nf < npt) {
+ throw bobyqa_failure("Return from BOBYQA because the objective function has been called max_f_evals times.");
+ //goto L720;
+ }
+ kbase = 1;
+
+ /* Complete the settings that are required for the iterative procedure. */
+
+ rho = rhobeg;
+ delta = rho;
+ nresc = nf;
+ ntrits = 0;
+ diffa = zero;
+ diffb = zero;
+ itest = 0;
+ nfsav = nf;
+
+ /* Update GOPT if necessary before the first iteration and after each */
+ /* call of RESCUE that makes a call of CALFUN. */
+
+L20:
+ if (kopt != kbase) {
+ ih = 0;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ i__2 = j;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ ++ih;
+ if (i__ < j) {
+ gopt[j] += hq[ih] * xopt[i__];
+ }
+ /* L30: */
+ gopt[i__] += hq[ih] * xopt[j];
+ }
+ }
+ if (nf > npt) {
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ temp = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L40: */
+ temp += xpt[k + j * xpt_dim1] * xopt[j];
+ }
+ temp = pq[k] * temp;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L50: */
+ gopt[i__] += temp * xpt[k + i__ * xpt_dim1];
+ }
+ }
+ }
+ }
+
+ /* Generate the next point in the trust region that provides a small value */
+ /* of the quadratic model subject to the constraints on the variables. */
+ /* The integer NTRITS is set to the number "trust region" iterations that */
+ /* have occurred since the last "alternative" iteration. If the length */
+ /* of XNEW-XOPT is less than HALF*RHO, however, then there is a branch to */
+ /* label 650 or 680 with NTRITS=-1, instead of calculating F at XNEW. */
+
+L60:
+ trsbox_(n, npt, &xpt[xpt_offset], &xopt[1], &gopt[1], &hq[1], &pq[1], &sl[1],
+ &su[1], delta, &xnew[1], &d__[1], &w[1], &w[np], &w[np + n],
+ &w[np + (n << 1)], &w[np + n * 3], &dsq, &crvmin);
+ /* Computing MIN */
+ d__1 = delta, d__2 = std::sqrt(dsq);
+ dnorm = std::min(d__1,d__2);
+ if (dnorm < half * rho) {
+ ntrits = -1;
+ /* Computing 2nd power */
+ d__1 = ten * rho;
+ distsq = d__1 * d__1;
+ if (nf <= nfsav + 2) {
+ goto L650;
+ }
+
+ /* The following choice between labels 650 and 680 depends on whether or */
+ /* not our work with the current RHO seems to be complete. Either RHO is */
+ /* decreased or termination occurs if the errors in the quadratic model at */
+ /* the last three interpolation points compare favourably with predictions */
+ /* of likely improvements to the model within distance HALF*RHO of XOPT. */
+
+ /* Computing MAX */
+ d__1 = std::max(diffa,diffb);
+ errbig = std::max(d__1,diffc);
+ frhosq = rho * .125 * rho;
+ if (crvmin > zero && errbig > frhosq * crvmin) {
+ goto L650;
+ }
+ bdtol = errbig / rho;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ bdtest = bdtol;
+ if (xnew[j] == sl[j]) {
+ bdtest = w[j];
+ }
+ if (xnew[j] == su[j]) {
+ bdtest = -w[j];
+ }
+ if (bdtest < bdtol) {
+ curv = hq[(j + j * j) / 2];
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L70: */
+ /* Computing 2nd power */
+ d__1 = xpt[k + j * xpt_dim1];
+ curv += pq[k] * (d__1 * d__1);
+ }
+ bdtest += half * curv * rho;
+ if (bdtest < bdtol) {
+ goto L650;
+ }
+ }
+ /* L80: */
+ }
+ goto L680;
+ }
+ ++ntrits;
+
+ /* Severe cancellation is likely to occur if XOPT is too far from XBASE. */
+ /* If the following test holds, then XBASE is shifted so that XOPT becomes */
+ /* zero. The appropriate changes are made to BMAT and to the second */
+ /* derivatives of the current model, beginning with the changes to BMAT */
+ /* that do not depend on ZMAT. VLAG is used temporarily for working space. */
+
+L90:
+ if (dsq <= xoptsq * .001) {
+ fracsq = xoptsq * .25;
+ sumpq = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ sumpq += pq[k];
+ sum = -half * xoptsq;
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L100: */
+ sum += xpt[k + i__ * xpt_dim1] * xopt[i__];
+ }
+ w[npt + k] = sum;
+ temp = fracsq - half * sum;
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ w[i__] = bmat[k + i__ * bmat_dim1];
+ vlag[i__] = sum * xpt[k + i__ * xpt_dim1] + temp * xopt[i__];
+ ip = npt + i__;
+ i__3 = i__;
+ for (j = 1; j <= i__3; ++j) {
+ /* L110: */
+ bmat[ip + j * bmat_dim1] = bmat[ip + j * bmat_dim1] + w[
+ i__] * vlag[j] + vlag[i__] * w[j];
+ }
+ }
+ }
+
+ /* Then the revisions of BMAT that depend on ZMAT are calculated. */
+
+ i__3 = nptm;
+ for (jj = 1; jj <= i__3; ++jj) {
+ sumz = zero;
+ sumw = zero;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ sumz += zmat[k + jj * zmat_dim1];
+ vlag[k] = w[npt + k] * zmat[k + jj * zmat_dim1];
+ /* L120: */
+ sumw += vlag[k];
+ }
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ sum = (fracsq * sumz - half * sumw) * xopt[j];
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L130: */
+ sum += vlag[k] * xpt[k + j * xpt_dim1];
+ }
+ w[j] = sum;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L140: */
+ bmat[k + j * bmat_dim1] += sum * zmat[k + jj * zmat_dim1];
+ }
+ }
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ ip = i__ + npt;
+ temp = w[i__];
+ i__2 = i__;
+ for (j = 1; j <= i__2; ++j) {
+ /* L150: */
+ bmat[ip + j * bmat_dim1] += temp * w[j];
+ }
+ }
+ }
+
+ /* The following instructions complete the shift, including the changes */
+ /* to the second derivative parameters of the quadratic model. */
+
+ ih = 0;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ w[j] = -half * sumpq * xopt[j];
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ w[j] += pq[k] * xpt[k + j * xpt_dim1];
+ /* L160: */
+ xpt[k + j * xpt_dim1] -= xopt[j];
+ }
+ i__1 = j;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ ++ih;
+ hq[ih] = hq[ih] + w[i__] * xopt[j] + xopt[i__] * w[j];
+ /* L170: */
+ bmat[npt + i__ + j * bmat_dim1] = bmat[npt + j + i__ *
+ bmat_dim1];
+ }
+ }
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ xbase[i__] += xopt[i__];
+ xnew[i__] -= xopt[i__];
+ sl[i__] -= xopt[i__];
+ su[i__] -= xopt[i__];
+ /* L180: */
+ xopt[i__] = zero;
+ }
+ xoptsq = zero;
+ }
+ if (ntrits == 0) {
+ goto L210;
+ }
+ goto L230;
+
+ /* XBASE is also moved to XOPT by a call of RESCUE. This calculation is */
+ /* more expensive than the previous shift, because new matrices BMAT and */
+ /* ZMAT are generated from scratch, which may include the replacement of */
+ /* interpolation points whose positions seem to be causing near linear */
+ /* dependence in the interpolation conditions. Therefore RESCUE is called */
+ /* only if rounding errors have reduced by at least a factor of two the */
+ /* denominator of the formula for updating the H matrix. It provides a */
+ /* useful safeguard, but is not invoked in most applications of BOBYQA. */
+
+L190:
+ nfsav = nf;
+ kbase = kopt;
+ rescue_(calfun, n, npt, &xl[1], &xu[1], maxfun, &xbase[1], &xpt[
+ xpt_offset], &fval[1], &xopt[1], &gopt[1], &hq[1], &pq[1], &bmat[
+ bmat_offset], &zmat[zmat_offset], ndim, &sl[1], &su[1], nf, delta,
+ kopt, &vlag[1], &w[1], &w[n + np], &w[ndim + np]);
+
+ /* XOPT is updated now in case the branch below to label 720 is taken. */
+ /* Any updating of GOPT occurs after the branch below to label 20, which */
+ /* leads to a trust region iteration as does the branch to label 60. */
+
+ xoptsq = zero;
+ if (kopt != kbase) {
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ xopt[i__] = xpt[kopt + i__ * xpt_dim1];
+ /* L200: */
+ /* Computing 2nd power */
+ d__1 = xopt[i__];
+ xoptsq += d__1 * d__1;
+ }
+ }
+ if (nf < 0) {
+ nf = maxfun;
+ throw bobyqa_failure("Return from BOBYQA because the objective function has been called max_f_evals times.");
+ //goto L720;
+ }
+ nresc = nf;
+ if (nfsav < nf) {
+ nfsav = nf;
+ goto L20;
+ }
+ if (ntrits > 0) {
+ goto L60;
+ }
+
+ /* Pick two alternative vectors of variables, relative to XBASE, that */
+ /* are suitable as new positions of the KNEW-th interpolation point. */
+ /* Firstly, XNEW is set to the point on a line through XOPT and another */
+ /* interpolation point that minimizes the predicted value of the next */
+ /* denominator, subject to ||XNEW - XOPT|| .LEQ. ADELT and to the SL */
+ /* and SU bounds. Secondly, XALT is set to the best feasible point on */
+ /* a constrained version of the Cauchy step of the KNEW-th Lagrange */
+ /* function, the corresponding value of the square of this function */
+ /* being returned in CAUCHY. The choice between these alternatives is */
+ /* going to be made when the denominator is calculated. */
+
+L210:
+ altmov_(n, npt, &xpt[xpt_offset], &xopt[1], &bmat[bmat_offset], &zmat[zmat_offset],
+ ndim, &sl[1], &su[1], kopt, knew, adelt, &xnew[1],
+ &xalt[1], alpha, cauchy, &w[1], &w[np], &w[ndim + 1]);
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L220: */
+ d__[i__] = xnew[i__] - xopt[i__];
+ }
+
+ /* Calculate VLAG and BETA for the current choice of D. The scalar */
+ /* product of D with XPT(K,.) is going to be held in W(NPT+K) for */
+ /* use when VQUAD is calculated. */
+
+L230:
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ suma = zero;
+ sumb = zero;
+ sum = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ suma += xpt[k + j * xpt_dim1] * d__[j];
+ sumb += xpt[k + j * xpt_dim1] * xopt[j];
+ /* L240: */
+ sum += bmat[k + j * bmat_dim1] * d__[j];
+ }
+ w[k] = suma * (half * suma + sumb);
+ vlag[k] = sum;
+ /* L250: */
+ w[npt + k] = suma;
+ }
+ beta = zero;
+ i__1 = nptm;
+ for (jj = 1; jj <= i__1; ++jj) {
+ sum = zero;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L260: */
+ sum += zmat[k + jj * zmat_dim1] * w[k];
+ }
+ beta -= sum * sum;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L270: */
+ vlag[k] += sum * zmat[k + jj * zmat_dim1];
+ }
+ }
+ dsq = zero;
+ bsum = zero;
+ dx = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* Computing 2nd power */
+ d__1 = d__[j];
+ dsq += d__1 * d__1;
+ sum = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L280: */
+ sum += w[k] * bmat[k + j * bmat_dim1];
+ }
+ bsum += sum * d__[j];
+ jp = npt + j;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L290: */
+ sum += bmat[jp + i__ * bmat_dim1] * d__[i__];
+ }
+ vlag[jp] = sum;
+ bsum += sum * d__[j];
+ /* L300: */
+ dx += d__[j] * xopt[j];
+ }
+ beta = dx * dx + dsq * (xoptsq + dx + dx + half * dsq) + beta - bsum;
+ vlag[kopt] += one;
+
+ /* If NTRITS is zero, the denominator may be increased by replacing */
+ /* the step D of ALTMOV by a Cauchy step. Then RESCUE may be called if */
+ /* rounding errors have damaged the chosen denominator. */
+
+ if (ntrits == 0) {
+ /* Computing 2nd power */
+ d__1 = vlag[knew];
+ denom = d__1 * d__1 + alpha * beta;
+ if (denom < cauchy && cauchy > zero) {
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ xnew[i__] = xalt[i__];
+ /* L310: */
+ d__[i__] = xnew[i__] - xopt[i__];
+ }
+ cauchy = zero;
+ goto L230;
+ }
+ /* Computing 2nd power */
+ d__1 = vlag[knew];
+ if (denom <= half * (d__1 * d__1)) {
+ if (nf > nresc) {
+ goto L190;
+ }
+ throw bobyqa_failure("Return from BOBYQA because of much cancellation in a denominator.");
+ //goto L720;
+ }
+
+ /* Alternatively, if NTRITS is positive, then set KNEW to the index of */
+ /* the next interpolation point to be deleted to make room for a trust */
+ /* region step. Again RESCUE may be called if rounding errors have damaged */
+ /* the chosen denominator, which is the reason for attempting to select */
+ /* KNEW before calculating the next value of the objective function. */
+
+ } else {
+ delsq = delta * delta;
+ scaden = zero;
+ biglsq = zero;
+ knew = 0;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ if (k == kopt) {
+ goto L350;
+ }
+ hdiag = zero;
+ i__1 = nptm;
+ for (jj = 1; jj <= i__1; ++jj) {
+ /* L330: */
+ /* Computing 2nd power */
+ d__1 = zmat[k + jj * zmat_dim1];
+ hdiag += d__1 * d__1;
+ }
+ /* Computing 2nd power */
+ d__1 = vlag[k];
+ den = beta * hdiag + d__1 * d__1;
+ distsq = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L340: */
+ /* Computing 2nd power */
+ d__1 = xpt[k + j * xpt_dim1] - xopt[j];
+ distsq += d__1 * d__1;
+ }
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = distsq / delsq;
+ d__1 = one, d__2 = d__3 * d__3;
+ temp = std::max(d__1,d__2);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = vlag[k];
+ d__1 = biglsq, d__2 = temp * (d__3 * d__3);
+ biglsq = std::max(d__1,d__2);
+L350:
+ ;
+ }
+ if (scaden <= half * biglsq) {
+ if (nf > nresc) {
+ goto L190;
+ }
+ throw bobyqa_failure("Return from BOBYQA because of much cancellation in a denominator.");
+ //goto L720;
+ }
+ }
+
+ /* Put the variables for the next calculation of the objective function */
+ /* in XNEW, with any adjustments for the bounds. */
+
+
+ /* Calculate the value of the objective function at XBASE+XNEW, unless */
+ /* the limit on the number of calculations of F has been reached. */
+
+L360:
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* Computing MIN */
+ /* Computing MAX */
+ d__3 = xl[i__], d__4 = xbase[i__] + xnew[i__];
+ d__1 = std::max(d__3,d__4), d__2 = xu[i__];
+ x[i__] = std::min(d__1,d__2);
+ if (xnew[i__] == sl[i__]) {
+ x[i__] = xl[i__];
+ }
+ if (xnew[i__] == su[i__]) {
+ x[i__] = xu[i__];
+ }
+ /* L380: */
+ }
+ if (nf >= maxfun) {
+ throw bobyqa_failure("Return from BOBYQA because the objective function has been called max_f_evals times.");
+ //goto L720;
+ }
+ ++nf;
+ f = calfun(mat(&x[1], n));
+ if (ntrits == -1) {
+ fsave = f;
+ goto L720;
+ }
+
+ /* Use the quadratic model to predict the change in F due to the step D, */
+ /* and set DIFF to the error of this prediction. */
+
+ fopt = fval[kopt];
+ vquad = zero;
+ ih = 0;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ vquad += d__[j] * gopt[j];
+ i__1 = j;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ ++ih;
+ temp = d__[i__] * d__[j];
+ if (i__ == j) {
+ temp = half * temp;
+ }
+ /* L410: */
+ vquad += hq[ih] * temp;
+ }
+ }
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L420: */
+ /* Computing 2nd power */
+ d__1 = w[npt + k];
+ vquad += half * pq[k] * (d__1 * d__1);
+ }
+ diff = f - fopt - vquad;
+ diffc = diffb;
+ diffb = diffa;
+ diffa = std::abs(diff);
+ if (dnorm > rho) {
+ nfsav = nf;
+ }
+
+ /* Pick the next value of DELTA after a trust region step. */
+
+ if (ntrits > 0) {
+ if (vquad >= zero) {
+ throw bobyqa_failure("Return from BOBYQA because a trust region step has failed to reduce Q.");
+ //goto L720;
+ }
+ ratio = (f - fopt) / vquad;
+ if (ratio <= tenth) {
+ /* Computing MIN */
+ d__1 = half * delta;
+ delta = std::min(d__1,dnorm);
+ } else if (ratio <= .7) {
+ /* Computing MAX */
+ d__1 = half * delta;
+ delta = std::max(d__1,dnorm);
+ } else {
+ /* Computing MAX */
+ d__1 = half * delta, d__2 = dnorm + dnorm;
+ delta = std::max(d__1,d__2);
+ }
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+
+ /* Recalculate KNEW and DENOM if the new F is less than FOPT. */
+
+ if (f < fopt) {
+ ksav = knew;
+ densav = denom;
+ delsq = delta * delta;
+ scaden = zero;
+ biglsq = zero;
+ knew = 0;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ hdiag = zero;
+ i__2 = nptm;
+ for (jj = 1; jj <= i__2; ++jj) {
+ /* L440: */
+ /* Computing 2nd power */
+ d__1 = zmat[k + jj * zmat_dim1];
+ hdiag += d__1 * d__1;
+ }
+ /* Computing 2nd power */
+ d__1 = vlag[k];
+ den = beta * hdiag + d__1 * d__1;
+ distsq = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L450: */
+ /* Computing 2nd power */
+ d__1 = xpt[k + j * xpt_dim1] - xnew[j];
+ distsq += d__1 * d__1;
+ }
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = distsq / delsq;
+ d__1 = one, d__2 = d__3 * d__3;
+ temp = std::max(d__1,d__2);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ /* L460: */
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = vlag[k];
+ d__1 = biglsq, d__2 = temp * (d__3 * d__3);
+ biglsq = std::max(d__1,d__2);
+ }
+ if (scaden <= half * biglsq) {
+ knew = ksav;
+ denom = densav;
+ }
+ }
+ }
+
+ /* Update BMAT and ZMAT, so that the KNEW-th interpolation point can be */
+ /* moved. Also update the second derivative terms of the model. */
+
+ update_(n, npt, &bmat[bmat_offset], &zmat[zmat_offset], ndim, &vlag[1],
+ beta, denom, knew, &w[1]);
+ ih = 0;
+ pqold = pq[knew];
+ pq[knew] = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ temp = pqold * xpt[knew + i__ * xpt_dim1];
+ i__2 = i__;
+ for (j = 1; j <= i__2; ++j) {
+ ++ih;
+ /* L470: */
+ hq[ih] += temp * xpt[knew + j * xpt_dim1];
+ }
+ }
+ i__2 = nptm;
+ for (jj = 1; jj <= i__2; ++jj) {
+ temp = diff * zmat[knew + jj * zmat_dim1];
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L480: */
+ pq[k] += temp * zmat[k + jj * zmat_dim1];
+ }
+ }
+
+ /* Include the new interpolation point, and make the changes to GOPT at */
+ /* the old XOPT that are caused by the updating of the quadratic model. */
+
+ fval[knew] = f;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ xpt[knew + i__ * xpt_dim1] = xnew[i__];
+ /* L490: */
+ w[i__] = bmat[knew + i__ * bmat_dim1];
+ }
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ suma = zero;
+ i__2 = nptm;
+ for (jj = 1; jj <= i__2; ++jj) {
+ /* L500: */
+ suma += zmat[knew + jj * zmat_dim1] * zmat[k + jj * zmat_dim1];
+ }
+ sumb = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L510: */
+ sumb += xpt[k + j * xpt_dim1] * xopt[j];
+ }
+ temp = suma * sumb;
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L520: */
+ w[i__] += temp * xpt[k + i__ * xpt_dim1];
+ }
+ }
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L530: */
+ gopt[i__] += diff * w[i__];
+ }
+
+ /* Update XOPT, GOPT and KOPT if the new calculated F is less than FOPT. */
+
+ if (f < fopt) {
+ kopt = knew;
+ xoptsq = zero;
+ ih = 0;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ xopt[j] = xnew[j];
+ /* Computing 2nd power */
+ d__1 = xopt[j];
+ xoptsq += d__1 * d__1;
+ i__1 = j;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ ++ih;
+ if (i__ < j) {
+ gopt[j] += hq[ih] * d__[i__];
+ }
+ /* L540: */
+ gopt[i__] += hq[ih] * d__[j];
+ }
+ }
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ temp = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L550: */
+ temp += xpt[k + j * xpt_dim1] * d__[j];
+ }
+ temp = pq[k] * temp;
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L560: */
+ gopt[i__] += temp * xpt[k + i__ * xpt_dim1];
+ }
+ }
+ }
+
+ /* Calculate the parameters of the least Frobenius norm interpolant to */
+ /* the current data, the gradient of this interpolant at XOPT being put */
+ /* into VLAG(NPT+I), I=1,2,...,N. */
+
+ if (ntrits > 0) {
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ vlag[k] = fval[k] - fval[kopt];
+ /* L570: */
+ w[k] = zero;
+ }
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ sum = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L580: */
+ sum += zmat[k + j * zmat_dim1] * vlag[k];
+ }
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L590: */
+ w[k] += sum * zmat[k + j * zmat_dim1];
+ }
+ }
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ sum = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L600: */
+ sum += xpt[k + j * xpt_dim1] * xopt[j];
+ }
+ w[k + npt] = w[k];
+ /* L610: */
+ w[k] = sum * w[k];
+ }
+ gqsq = zero;
+ gisq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ sum = zero;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L620: */
+ sum = sum + bmat[k + i__ * bmat_dim1] * vlag[k] + xpt[k + i__
+ * xpt_dim1] * w[k];
+ }
+ if (xopt[i__] == sl[i__]) {
+ /* Computing MIN */
+ d__2 = zero, d__3 = gopt[i__];
+ /* Computing 2nd power */
+ d__1 = std::min(d__2,d__3);
+ gqsq += d__1 * d__1;
+ /* Computing 2nd power */
+ d__1 = std::min(zero,sum);
+ gisq += d__1 * d__1;
+ } else if (xopt[i__] == su[i__]) {
+ /* Computing MAX */
+ d__2 = zero, d__3 = gopt[i__];
+ /* Computing 2nd power */
+ d__1 = std::max(d__2,d__3);
+ gqsq += d__1 * d__1;
+ /* Computing 2nd power */
+ d__1 = std::max(zero,sum);
+ gisq += d__1 * d__1;
+ } else {
+ /* Computing 2nd power */
+ d__1 = gopt[i__];
+ gqsq += d__1 * d__1;
+ gisq += sum * sum;
+ }
+ /* L630: */
+ vlag[npt + i__] = sum;
+ }
+
+ /* Test whether to replace the new quadratic model by the least Frobenius */
+ /* norm interpolant, making the replacement if the test is satisfied. */
+
+ ++itest;
+ if (gqsq < ten * gisq) {
+ itest = 0;
+ }
+ if (itest >= 3) {
+ i__1 = std::max(npt,nh);
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (i__ <= n) {
+ gopt[i__] = vlag[npt + i__];
+ }
+ if (i__ <= npt) {
+ pq[i__] = w[npt + i__];
+ }
+ if (i__ <= nh) {
+ hq[i__] = zero;
+ }
+ itest = 0;
+ /* L640: */
+ }
+ }
+ }
+
+ /* If a trust region step has provided a sufficient decrease in F, then */
+ /* branch for another trust region calculation. The case NTRITS=0 occurs */
+ /* when the new interpolation point was reached by an alternative step. */
+
+ if (ntrits == 0) {
+ goto L60;
+ }
+ if (f <= fopt + tenth * vquad) {
+ goto L60;
+ }
+
+ /* Alternatively, find out if the interpolation points are close enough */
+ /* to the best point so far. */
+
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = two * delta;
+ /* Computing 2nd power */
+ d__4 = ten * rho;
+ d__1 = d__3 * d__3, d__2 = d__4 * d__4;
+ distsq = std::max(d__1,d__2);
+L650:
+ knew = 0;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ sum = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L660: */
+ /* Computing 2nd power */
+ d__1 = xpt[k + j * xpt_dim1] - xopt[j];
+ sum += d__1 * d__1;
+ }
+ if (sum > distsq) {
+ knew = k;
+ distsq = sum;
+ }
+ /* L670: */
+ }
+
+ /* If KNEW is positive, then ALTMOV finds alternative new positions for */
+ /* the KNEW-th interpolation point within distance ADELT of XOPT. It is */
+ /* reached via label 90. Otherwise, there is a branch to label 60 for */
+ /* another trust region iteration, unless the calculations with the */
+ /* current RHO are complete. */
+
+ if (knew > 0) {
+ dist = std::sqrt(distsq);
+ if (ntrits == -1) {
+ /* Computing MIN */
+ d__1 = tenth * delta, d__2 = half * dist;
+ delta = std::min(d__1,d__2);
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+ }
+ ntrits = 0;
+ /* Computing MAX */
+ /* Computing MIN */
+ d__2 = tenth * dist;
+ d__1 = std::min(d__2,delta);
+ adelt = std::max(d__1,rho);
+ dsq = adelt * adelt;
+ goto L90;
+ }
+ if (ntrits == -1) {
+ goto L680;
+ }
+ if (ratio > zero) {
+ goto L60;
+ }
+ if (std::max(delta,dnorm) > rho) {
+ goto L60;
+ }
+
+ /* The calculations with the current value of RHO are complete. Pick the */
+ /* next values of RHO and DELTA. */
+
+L680:
+ if (rho > rhoend) {
+ delta = half * rho;
+ ratio = rho / rhoend;
+ if (ratio <= 16.) {
+ rho = rhoend;
+ } else if (ratio <= 250.) {
+ rho = std::sqrt(ratio) * rhoend;
+ } else {
+ rho = tenth * rho;
+ }
+ delta = std::max(delta,rho);
+ ntrits = 0;
+ nfsav = nf;
+ goto L60;
+ }
+
+ /* Return from the calculation, after another Newton-Raphson step, if */
+ /* it is too short to have been tried before. */
+
+ if (ntrits == -1) {
+ goto L360;
+ }
+L720:
+ if (fval[kopt] <= fsave) {
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* Computing MIN */
+ /* Computing MAX */
+ d__3 = xl[i__], d__4 = xbase[i__] + xopt[i__];
+ d__1 = std::max(d__3,d__4), d__2 = xu[i__];
+ x[i__] = std::min(d__1,d__2);
+ if (xopt[i__] == sl[i__]) {
+ x[i__] = xl[i__];
+ }
+ if (xopt[i__] == su[i__]) {
+ x[i__] = xu[i__];
+ }
+ /* L730: */
+ }
+ f = fval[kopt];
+ }
+
+ return f;
+ } /* bobyqb_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ void altmov_(
+ const integer n,
+ const integer npt,
+ const doublereal *xpt,
+ const doublereal *xopt,
+ const doublereal *bmat,
+ const doublereal *zmat,
+ const integer ndim,
+ const doublereal *sl,
+ const doublereal *su,
+ const integer kopt,
+ const integer knew,
+ const doublereal adelt,
+ doublereal *xnew,
+ doublereal *xalt,
+ doublereal& alpha,
+ doublereal& cauchy,
+ doublereal *glag,
+ doublereal *hcol,
+ doublereal *w
+ ) const
+ {
+ /* System generated locals */
+ integer xpt_dim1, xpt_offset, bmat_dim1, bmat_offset, zmat_dim1,
+ zmat_offset, i__1, i__2;
+ doublereal d__1, d__2, d__3, d__4;
+
+
+ /* Local variables */
+ integer i__, j, k;
+ doublereal ha, gw, one, diff, half;
+ integer ilbd, isbd;
+ doublereal slbd;
+ integer iubd;
+ doublereal vlag, subd, temp;
+ integer ksav = 0;
+ doublereal step = 0, zero = 0, curv = 0;
+ integer iflag;
+ doublereal scale = 0, csave = 0, tempa = 0, tempb = 0, tempd = 0, const__ = 0, sumin = 0,
+ ggfree = 0;
+ integer ibdsav = 0;
+ doublereal dderiv = 0, bigstp = 0, predsq = 0, presav = 0, distsq = 0, stpsav = 0, wfixsq = 0, wsqsav = 0;
+
+
+ /* The arguments N, NPT, XPT, XOPT, BMAT, ZMAT, NDIM, SL and SU all have */
+ /* the same meanings as the corresponding arguments of BOBYQB. */
+ /* KOPT is the index of the optimal interpolation point. */
+ /* KNEW is the index of the interpolation point that is going to be moved. */
+ /* ADELT is the current trust region bound. */
+ /* XNEW will be set to a suitable new position for the interpolation point */
+ /* XPT(KNEW,.). Specifically, it satisfies the SL, SU and trust region */
+ /* bounds and it should provide a large denominator in the next call of */
+ /* UPDATE. The step XNEW-XOPT from XOPT is restricted to moves along the */
+ /* straight lines through XOPT and another interpolation point. */
+ /* XALT also provides a large value of the modulus of the KNEW-th Lagrange */
+ /* function subject to the constraints that have been mentioned, its main */
+ /* difference from XNEW being that XALT-XOPT is a constrained version of */
+ /* the Cauchy step within the trust region. An exception is that XALT is */
+ /* not calculated if all components of GLAG (see below) are zero. */
+ /* ALPHA will be set to the KNEW-th diagonal element of the H matrix. */
+ /* CAUCHY will be set to the square of the KNEW-th Lagrange function at */
+ /* the step XALT-XOPT from XOPT for the vector XALT that is returned, */
+ /* except that CAUCHY is set to zero if XALT is not calculated. */
+ /* GLAG is a working space vector of length N for the gradient of the */
+ /* KNEW-th Lagrange function at XOPT. */
+ /* HCOL is a working space vector of length NPT for the second derivative */
+ /* coefficients of the KNEW-th Lagrange function. */
+ /* W is a working space vector of length 2N that is going to hold the */
+ /* constrained Cauchy step from XOPT of the Lagrange function, followed */
+ /* by the downhill version of XALT when the uphill step is calculated. */
+
+ /* Set the first NPT components of W to the leading elements of the */
+ /* KNEW-th column of the H matrix. */
+
+ /* Parameter adjustments */
+ zmat_dim1 = npt;
+ zmat_offset = 1 + zmat_dim1;
+ zmat -= zmat_offset;
+ xpt_dim1 = npt;
+ xpt_offset = 1 + xpt_dim1;
+ xpt -= xpt_offset;
+ --xopt;
+ bmat_dim1 = ndim;
+ bmat_offset = 1 + bmat_dim1;
+ bmat -= bmat_offset;
+ --sl;
+ --su;
+ --xnew;
+ --xalt;
+ --glag;
+ --hcol;
+ --w;
+
+ /* Function Body */
+ half = .5;
+ one = 1.;
+ zero = 0.;
+ const__ = one + std::sqrt(2.);
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L10: */
+ hcol[k] = zero;
+ }
+ i__1 = npt - n - 1;
+ for (j = 1; j <= i__1; ++j) {
+ temp = zmat[knew + j * zmat_dim1];
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L20: */
+ hcol[k] += temp * zmat[k + j * zmat_dim1];
+ }
+ }
+ alpha = hcol[knew];
+ ha = half * alpha;
+
+ /* Calculate the gradient of the KNEW-th Lagrange function at XOPT. */
+
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L30: */
+ glag[i__] = bmat[knew + i__ * bmat_dim1];
+ }
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ temp = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L40: */
+ temp += xpt[k + j * xpt_dim1] * xopt[j];
+ }
+ temp = hcol[k] * temp;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L50: */
+ glag[i__] += temp * xpt[k + i__ * xpt_dim1];
+ }
+ }
+
+ /* Search for a large denominator along the straight lines through XOPT */
+ /* and another interpolation point. SLBD and SUBD will be lower and upper */
+ /* bounds on the step along each of these lines in turn. PREDSQ will be */
+ /* set to the square of the predicted denominator for each line. PRESAV */
+ /* will be set to the largest admissible value of PREDSQ that occurs. */
+
+ presav = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ if (k == kopt) {
+ goto L80;
+ }
+ dderiv = zero;
+ distsq = zero;
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ temp = xpt[k + i__ * xpt_dim1] - xopt[i__];
+ dderiv += glag[i__] * temp;
+ /* L60: */
+ distsq += temp * temp;
+ }
+ subd = adelt / std::sqrt(distsq);
+ slbd = -subd;
+ ilbd = 0;
+ iubd = 0;
+ sumin = std::min(one,subd);
+
+ /* Revise SLBD and SUBD if necessary because of the bounds in SL and SU. */
+
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ temp = xpt[k + i__ * xpt_dim1] - xopt[i__];
+ if (temp > zero) {
+ if (slbd * temp < sl[i__] - xopt[i__]) {
+ slbd = (sl[i__] - xopt[i__]) / temp;
+ ilbd = -i__;
+ }
+ if (subd * temp > su[i__] - xopt[i__]) {
+ /* Computing MAX */
+ d__1 = sumin, d__2 = (su[i__] - xopt[i__]) / temp;
+ subd = std::max(d__1,d__2);
+ iubd = i__;
+ }
+ } else if (temp < zero) {
+ if (slbd * temp > su[i__] - xopt[i__]) {
+ slbd = (su[i__] - xopt[i__]) / temp;
+ ilbd = i__;
+ }
+ if (subd * temp < sl[i__] - xopt[i__]) {
+ /* Computing MAX */
+ d__1 = sumin, d__2 = (sl[i__] - xopt[i__]) / temp;
+ subd = std::max(d__1,d__2);
+ iubd = -i__;
+ }
+ }
+ /* L70: */
+ }
+
+ /* Seek a large modulus of the KNEW-th Lagrange function when the index */
+ /* of the other interpolation point on the line through XOPT is KNEW. */
+
+ if (k == knew) {
+ diff = dderiv - one;
+ step = slbd;
+ vlag = slbd * (dderiv - slbd * diff);
+ isbd = ilbd;
+ temp = subd * (dderiv - subd * diff);
+ if (std::abs(temp) > std::abs(vlag)) {
+ step = subd;
+ vlag = temp;
+ isbd = iubd;
+ }
+ tempd = half * dderiv;
+ tempa = tempd - diff * slbd;
+ tempb = tempd - diff * subd;
+ if (tempa * tempb < zero) {
+ temp = tempd * tempd / diff;
+ if (std::abs(temp) > std::abs(vlag)) {
+ step = tempd / diff;
+ vlag = temp;
+ isbd = 0;
+ }
+ }
+
+ /* Search along each of the other lines through XOPT and another point. */
+
+ } else {
+ step = slbd;
+ vlag = slbd * (one - slbd);
+ isbd = ilbd;
+ temp = subd * (one - subd);
+ if (std::abs(temp) > std::abs(vlag)) {
+ step = subd;
+ vlag = temp;
+ isbd = iubd;
+ }
+ if (subd > half) {
+ if (std::abs(vlag) < .25) {
+ step = half;
+ vlag = .25;
+ isbd = 0;
+ }
+ }
+ vlag *= dderiv;
+ }
+
+ /* Calculate PREDSQ for the current line search and maintain PRESAV. */
+
+ temp = step * (one - step) * distsq;
+ predsq = vlag * vlag * (vlag * vlag + ha * temp * temp);
+ if (predsq > presav) {
+ presav = predsq;
+ ksav = k;
+ stpsav = step;
+ ibdsav = isbd;
+ }
+L80:
+ ;
+ }
+
+ /* Construct XNEW in a way that satisfies the bound constraints exactly. */
+
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ temp = xopt[i__] + stpsav * (xpt[ksav + i__ * xpt_dim1] - xopt[i__]);
+ /* L90: */
+ /* Computing MAX */
+ /* Computing MIN */
+ d__3 = su[i__];
+ d__1 = sl[i__], d__2 = std::min(d__3,temp);
+ xnew[i__] = std::max(d__1,d__2);
+ }
+ if (ibdsav < 0) {
+ xnew[-ibdsav] = sl[-ibdsav];
+ }
+ if (ibdsav > 0) {
+ xnew[ibdsav] = su[ibdsav];
+ }
+
+ /* Prepare for the iterative method that assembles the constrained Cauchy */
+ /* step in W. The sum of squares of the fixed components of W is formed in */
+ /* WFIXSQ, and the free components of W are set to BIGSTP. */
+
+ bigstp = adelt + adelt;
+ iflag = 0;
+L100:
+ wfixsq = zero;
+ ggfree = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ w[i__] = zero;
+ /* Computing MIN */
+ d__1 = xopt[i__] - sl[i__], d__2 = glag[i__];
+ tempa = std::min(d__1,d__2);
+ /* Computing MAX */
+ d__1 = xopt[i__] - su[i__], d__2 = glag[i__];
+ tempb = std::max(d__1,d__2);
+ if (tempa > zero || tempb < zero) {
+ w[i__] = bigstp;
+ /* Computing 2nd power */
+ d__1 = glag[i__];
+ ggfree += d__1 * d__1;
+ }
+ /* L110: */
+ }
+ if (ggfree == zero) {
+ cauchy = zero;
+ goto L200;
+ }
+
+ /* Investigate whether more components of W can be fixed. */
+
+L120:
+ temp = adelt * adelt - wfixsq;
+ if (temp > zero) {
+ wsqsav = wfixsq;
+ step = std::sqrt(temp / ggfree);
+ ggfree = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (w[i__] == bigstp) {
+ temp = xopt[i__] - step * glag[i__];
+ if (temp <= sl[i__]) {
+ w[i__] = sl[i__] - xopt[i__];
+ /* Computing 2nd power */
+ d__1 = w[i__];
+ wfixsq += d__1 * d__1;
+ } else if (temp >= su[i__]) {
+ w[i__] = su[i__] - xopt[i__];
+ /* Computing 2nd power */
+ d__1 = w[i__];
+ wfixsq += d__1 * d__1;
+ } else {
+ /* Computing 2nd power */
+ d__1 = glag[i__];
+ ggfree += d__1 * d__1;
+ }
+ }
+ /* L130: */
+ }
+ if (wfixsq > wsqsav && ggfree > zero) {
+ goto L120;
+ }
+ }
+
+ /* Set the remaining free components of W and all components of XALT, */
+ /* except that W may be scaled later. */
+
+ gw = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (w[i__] == bigstp) {
+ w[i__] = -step * glag[i__];
+ /* Computing MAX */
+ /* Computing MIN */
+ d__3 = su[i__], d__4 = xopt[i__] + w[i__];
+ d__1 = sl[i__], d__2 = std::min(d__3,d__4);
+ xalt[i__] = std::max(d__1,d__2);
+ } else if (w[i__] == zero) {
+ xalt[i__] = xopt[i__];
+ } else if (glag[i__] > zero) {
+ xalt[i__] = sl[i__];
+ } else {
+ xalt[i__] = su[i__];
+ }
+ /* L140: */
+ gw += glag[i__] * w[i__];
+ }
+
+ /* Set CURV to the curvature of the KNEW-th Lagrange function along W. */
+ /* Scale W by a factor less than one if that can reduce the modulus of */
+ /* the Lagrange function at XOPT+W. Set CAUCHY to the final value of */
+ /* the square of this function. */
+
+ curv = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ temp = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L150: */
+ temp += xpt[k + j * xpt_dim1] * w[j];
+ }
+ /* L160: */
+ curv += hcol[k] * temp * temp;
+ }
+ if (iflag == 1) {
+ curv = -curv;
+ }
+ if (curv > -gw && curv < -const__ * gw) {
+ scale = -gw / curv;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ temp = xopt[i__] + scale * w[i__];
+ /* L170: */
+ /* Computing MAX */
+ /* Computing MIN */
+ d__3 = su[i__];
+ d__1 = sl[i__], d__2 = std::min(d__3,temp);
+ xalt[i__] = std::max(d__1,d__2);
+ }
+ /* Computing 2nd power */
+ d__1 = half * gw * scale;
+ cauchy = d__1 * d__1;
+ } else {
+ /* Computing 2nd power */
+ d__1 = gw + half * curv;
+ cauchy = d__1 * d__1;
+ }
+
+ /* If IFLAG is zero, then XALT is calculated as before after reversing */
+ /* the sign of GLAG. Thus two XALT vectors become available. The one that */
+ /* is chosen is the one that gives the larger value of CAUCHY. */
+
+ if (iflag == 0) {
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ glag[i__] = -glag[i__];
+ /* L180: */
+ w[n + i__] = xalt[i__];
+ }
+ csave = cauchy;
+ iflag = 1;
+ goto L100;
+ }
+ if (csave > cauchy) {
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L190: */
+ xalt[i__] = w[n + i__];
+ }
+ cauchy = csave;
+ }
+L200:
+ ;
+ } /* altmov_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ template <typename funct>
+ void prelim_(
+ const funct& calfun,
+ const integer n,
+ const integer npt,
+ doublereal *x,
+ const doublereal *xl,
+ const doublereal *xu,
+ const doublereal rhobeg,
+ const integer maxfun,
+ doublereal *xbase,
+ doublereal *xpt,
+ doublereal *fval,
+ doublereal *gopt,
+ doublereal *hq,
+ doublereal *pq,
+ doublereal *bmat,
+ doublereal *zmat,
+ const integer ndim,
+ const doublereal *sl,
+ const doublereal *su,
+ integer& nf,
+ integer& kopt
+ ) const
+ {
+ /* System generated locals */
+ integer xpt_dim1, xpt_offset, bmat_dim1, bmat_offset, zmat_dim1,
+ zmat_offset, i__1, i__2;
+ doublereal d__1, d__2, d__3, d__4;
+
+
+ /* Local variables */
+ doublereal f;
+ integer i__, j, k, ih, np, nfm;
+ doublereal one;
+ integer nfx = 0, ipt = 0, jpt = 0;
+ doublereal two = 0, fbeg = 0, diff = 0, half = 0, temp = 0, zero = 0, recip = 0, stepa = 0, stepb = 0;
+ integer itemp;
+ doublereal rhosq;
+
+
+
+ /* The arguments N, NPT, X, XL, XU, RHOBEG, IPRINT and MAXFUN are the */
+ /* same as the corresponding arguments in SUBROUTINE BOBYQA. */
+ /* The arguments XBASE, XPT, FVAL, HQ, PQ, BMAT, ZMAT, NDIM, SL and SU */
+ /* are the same as the corresponding arguments in BOBYQB, the elements */
+ /* of SL and SU being set in BOBYQA. */
+ /* GOPT is usually the gradient of the quadratic model at XOPT+XBASE, but */
+ /* it is set by PRELIM to the gradient of the quadratic model at XBASE. */
+ /* If XOPT is nonzero, BOBYQB will change it to its usual value later. */
+ /* NF is maintaned as the number of calls of CALFUN so far. */
+ /* KOPT will be such that the least calculated value of F so far is at */
+ /* the point XPT(KOPT,.)+XBASE in the space of the variables. */
+
+ /* SUBROUTINE PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ, */
+ /* BMAT and ZMAT for the first iteration, and it maintains the values of */
+ /* NF and KOPT. The vector X is also changed by PRELIM. */
+
+ /* Set some constants. */
+
+ /* Parameter adjustments */
+ zmat_dim1 = npt;
+ zmat_offset = 1 + zmat_dim1;
+ zmat -= zmat_offset;
+ xpt_dim1 = npt;
+ xpt_offset = 1 + xpt_dim1;
+ xpt -= xpt_offset;
+ --x;
+ --xl;
+ --xu;
+ --xbase;
+ --fval;
+ --gopt;
+ --hq;
+ --pq;
+ bmat_dim1 = ndim;
+ bmat_offset = 1 + bmat_dim1;
+ bmat -= bmat_offset;
+ --sl;
+ --su;
+
+ /* Function Body */
+ half = .5;
+ one = 1.;
+ two = 2.;
+ zero = 0.;
+ rhosq = rhobeg * rhobeg;
+ recip = one / rhosq;
+ np = n + 1;
+
+ /* Set XBASE to the initial vector of variables, and set the initial */
+ /* elements of XPT, BMAT, HQ, PQ and ZMAT to zero. */
+
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ xbase[j] = x[j];
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L10: */
+ xpt[k + j * xpt_dim1] = zero;
+ }
+ i__2 = ndim;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L20: */
+ bmat[i__ + j * bmat_dim1] = zero;
+ }
+ }
+ i__2 = n * np / 2;
+ for (ih = 1; ih <= i__2; ++ih) {
+ /* L30: */
+ hq[ih] = zero;
+ }
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ pq[k] = zero;
+ i__1 = npt - np;
+ for (j = 1; j <= i__1; ++j) {
+ /* L40: */
+ zmat[k + j * zmat_dim1] = zero;
+ }
+ }
+
+ /* Begin the initialization procedure. NF becomes one more than the number */
+ /* of function values so far. The coordinates of the displacement of the */
+ /* next initial interpolation point from XBASE are set in XPT(NF+1,.). */
+
+ nf = 0;
+L50:
+ nfm = nf;
+ nfx = nf - n;
+ ++(nf);
+ if (nfm <= n << 1) {
+ if (nfm >= 1 && nfm <= n) {
+ stepa = rhobeg;
+ if (su[nfm] == zero) {
+ stepa = -stepa;
+ }
+ xpt[nf + nfm * xpt_dim1] = stepa;
+ } else if (nfm > n) {
+ stepa = xpt[nf - n + nfx * xpt_dim1];
+ stepb = -(rhobeg);
+ if (sl[nfx] == zero) {
+ /* Computing MIN */
+ d__1 = two * rhobeg, d__2 = su[nfx];
+ stepb = std::min(d__1,d__2);
+ }
+ if (su[nfx] == zero) {
+ /* Computing MAX */
+ d__1 = -two * rhobeg, d__2 = sl[nfx];
+ stepb = std::max(d__1,d__2);
+ }
+ xpt[nf + nfx * xpt_dim1] = stepb;
+ }
+ } else {
+ itemp = (nfm - np) / n;
+ jpt = nfm - itemp * n - n;
+ ipt = jpt + itemp;
+ if (ipt > n) {
+ itemp = jpt;
+ jpt = ipt - n;
+ ipt = itemp;
+ }
+ xpt[nf + ipt * xpt_dim1] = xpt[ipt + 1 + ipt * xpt_dim1];
+ xpt[nf + jpt * xpt_dim1] = xpt[jpt + 1 + jpt * xpt_dim1];
+ }
+
+ /* Calculate the next value of F. The least function value so far and */
+ /* its index are required. */
+
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* Computing MIN */
+ /* Computing MAX */
+ d__3 = xl[j], d__4 = xbase[j] + xpt[nf + j * xpt_dim1];
+ d__1 = std::max(d__3,d__4), d__2 = xu[j];
+ x[j] = std::min(d__1,d__2);
+ if (xpt[nf + j * xpt_dim1] == sl[j]) {
+ x[j] = xl[j];
+ }
+ if (xpt[nf + j * xpt_dim1] == su[j]) {
+ x[j] = xu[j];
+ }
+ /* L60: */
+ }
+ f = calfun(mat(&x[1],n));
+ fval[nf] = f;
+ if (nf == 1) {
+ fbeg = f;
+ kopt = 1;
+ } else if (f < fval[kopt]) {
+ kopt = nf;
+ }
+
+ /* Set the nonzero initial elements of BMAT and the quadratic model in the */
+ /* cases when NF is at most 2*N+1. If NF exceeds N+1, then the positions */
+ /* of the NF-th and (NF-N)-th interpolation points may be switched, in */
+ /* order that the function value at the first of them contributes to the */
+ /* off-diagonal second derivative terms of the initial quadratic model. */
+
+ if (nf <= (n << 1) + 1) {
+ if (nf >= 2 && nf <= n + 1) {
+ gopt[nfm] = (f - fbeg) / stepa;
+ if (npt < nf + n) {
+ bmat[nfm * bmat_dim1 + 1] = -one / stepa;
+ bmat[nf + nfm * bmat_dim1] = one / stepa;
+ bmat[npt + nfm + nfm * bmat_dim1] = -half * rhosq;
+ }
+ } else if (nf >= n + 2) {
+ ih = nfx * (nfx + 1) / 2;
+ temp = (f - fbeg) / stepb;
+ diff = stepb - stepa;
+ hq[ih] = two * (temp - gopt[nfx]) / diff;
+ gopt[nfx] = (gopt[nfx] * stepb - temp * stepa) / diff;
+ if (stepa * stepb < zero) {
+ if (f < fval[nf - n]) {
+ fval[nf] = fval[nf - n];
+ fval[nf - n] = f;
+ if (kopt == nf) {
+ kopt = nf - n;
+ }
+ xpt[nf - n + nfx * xpt_dim1] = stepb;
+ xpt[nf + nfx * xpt_dim1] = stepa;
+ }
+ }
+ bmat[nfx * bmat_dim1 + 1] = -(stepa + stepb) / (stepa * stepb);
+ bmat[nf + nfx * bmat_dim1] = -half / xpt[nf - n + nfx *
+ xpt_dim1];
+ bmat[nf - n + nfx * bmat_dim1] = -bmat[nfx * bmat_dim1 + 1] -
+ bmat[nf + nfx * bmat_dim1];
+ zmat[nfx * zmat_dim1 + 1] = std::sqrt(two) / (stepa * stepb);
+ zmat[nf + nfx * zmat_dim1] = std::sqrt(half) / rhosq;
+ zmat[nf - n + nfx * zmat_dim1] = -zmat[nfx * zmat_dim1 + 1] -
+ zmat[nf + nfx * zmat_dim1];
+ }
+
+ /* Set the off-diagonal second derivatives of the Lagrange functions and */
+ /* the initial quadratic model. */
+
+ } else {
+ ih = ipt * (ipt - 1) / 2 + jpt;
+ zmat[nfx * zmat_dim1 + 1] = recip;
+ zmat[nf + nfx * zmat_dim1] = recip;
+ zmat[ipt + 1 + nfx * zmat_dim1] = -recip;
+ zmat[jpt + 1 + nfx * zmat_dim1] = -recip;
+ temp = xpt[nf + ipt * xpt_dim1] * xpt[nf + jpt * xpt_dim1];
+ hq[ih] = (fbeg - fval[ipt + 1] - fval[jpt + 1] + f) / temp;
+ }
+ if (nf < npt && nf < maxfun) {
+ goto L50;
+ }
+
+ } /* prelim_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ template <typename funct>
+ void rescue_ (
+ const funct& calfun,
+ const integer n,
+ const integer npt,
+ const doublereal *xl,
+ const doublereal *xu,
+ const integer maxfun,
+ doublereal *xbase,
+ doublereal *xpt,
+ doublereal *fval,
+ doublereal *xopt,
+ doublereal *gopt,
+ doublereal *hq,
+ doublereal *pq,
+ doublereal *bmat,
+ doublereal *zmat,
+ const integer ndim,
+ doublereal *sl,
+ doublereal *su,
+ integer& nf,
+ const doublereal delta,
+ integer& kopt,
+ doublereal *vlag,
+ doublereal * ptsaux,
+ doublereal *ptsid,
+ doublereal *w
+ ) const
+ {
+ /* System generated locals */
+ integer xpt_dim1, xpt_offset, bmat_dim1, bmat_offset, zmat_dim1,
+ zmat_offset, i__1, i__2, i__3;
+ doublereal d__1, d__2, d__3, d__4;
+
+
+ /* Local variables */
+ doublereal f;
+ integer i__, j, k, ih, jp, ip, iq, np, iw;
+ doublereal xp = 0, xq = 0, den = 0;
+ integer ihp = 0;
+ doublereal one;
+ integer ihq, jpn, kpt;
+ doublereal sum = 0, diff = 0, half = 0, beta = 0;
+ integer kold;
+ doublereal winc;
+ integer nrem, knew;
+ doublereal temp, bsum;
+ integer nptm;
+ doublereal zero = 0, hdiag = 0, fbase = 0, sfrac = 0, denom = 0, vquad = 0, sumpq = 0;
+ doublereal dsqmin, distsq, vlmxsq;
+
+
+
+ /* The arguments N, NPT, XL, XU, IPRINT, MAXFUN, XBASE, XPT, FVAL, XOPT, */
+ /* GOPT, HQ, PQ, BMAT, ZMAT, NDIM, SL and SU have the same meanings as */
+ /* the corresponding arguments of BOBYQB on the entry to RESCUE. */
+ /* NF is maintained as the number of calls of CALFUN so far, except that */
+ /* NF is set to -1 if the value of MAXFUN prevents further progress. */
+ /* KOPT is maintained so that FVAL(KOPT) is the least calculated function */
+ /* value. Its correct value must be given on entry. It is updated if a */
+ /* new least function value is found, but the corresponding changes to */
+ /* XOPT and GOPT have to be made later by the calling program. */
+ /* DELTA is the current trust region radius. */
+ /* VLAG is a working space vector that will be used for the values of the */
+ /* provisional Lagrange functions at each of the interpolation points. */
+ /* They are part of a product that requires VLAG to be of length NDIM. */
+ /* PTSAUX is also a working space array. For J=1,2,...,N, PTSAUX(1,J) and */
+ /* PTSAUX(2,J) specify the two positions of provisional interpolation */
+ /* points when a nonzero step is taken along e_J (the J-th coordinate */
+ /* direction) through XBASE+XOPT, as specified below. Usually these */
+ /* steps have length DELTA, but other lengths are chosen if necessary */
+ /* in order to satisfy the given bounds on the variables. */
+ /* PTSID is also a working space array. It has NPT components that denote */
+ /* provisional new positions of the original interpolation points, in */
+ /* case changes are needed to restore the linear independence of the */
+ /* interpolation conditions. The K-th point is a candidate for change */
+ /* if and only if PTSID(K) is nonzero. In this case let p and q be the */
+ /* integer parts of PTSID(K) and (PTSID(K)-p) multiplied by N+1. If p */
+ /* and q are both positive, the step from XBASE+XOPT to the new K-th */
+ /* interpolation point is PTSAUX(1,p)*e_p + PTSAUX(1,q)*e_q. Otherwise */
+ /* the step is PTSAUX(1,p)*e_p or PTSAUX(2,q)*e_q in the cases q=0 or */
+ /* p=0, respectively. */
+ /* The first NDIM+NPT elements of the array W are used for working space. */
+ /* The final elements of BMAT and ZMAT are set in a well-conditioned way */
+ /* to the values that are appropriate for the new interpolation points. */
+ /* The elements of GOPT, HQ and PQ are also revised to the values that are */
+ /* appropriate to the final quadratic model. */
+
+ /* Set some constants. */
+
+ /* Parameter adjustments */
+ zmat_dim1 = npt;
+ zmat_offset = 1 + zmat_dim1;
+ zmat -= zmat_offset;
+ xpt_dim1 = npt;
+ xpt_offset = 1 + xpt_dim1;
+ xpt -= xpt_offset;
+ --xl;
+ --xu;
+ --xbase;
+ --fval;
+ --xopt;
+ --gopt;
+ --hq;
+ --pq;
+ bmat_dim1 = ndim;
+ bmat_offset = 1 + bmat_dim1;
+ bmat -= bmat_offset;
+ --sl;
+ --su;
+ --vlag;
+ ptsaux -= 3;
+ --ptsid;
+ --w;
+
+ /* Function Body */
+ half = .5;
+ one = 1.;
+ zero = 0.;
+ np = n + 1;
+ sfrac = half / (doublereal) np;
+ nptm = npt - np;
+
+ /* Shift the interpolation points so that XOPT becomes the origin, and set */
+ /* the elements of ZMAT to zero. The value of SUMPQ is required in the */
+ /* updating of HQ below. The squares of the distances from XOPT to the */
+ /* other interpolation points are set at the end of W. Increments of WINC */
+ /* may be added later to these squares to balance the consideration of */
+ /* the choice of point that is going to become current. */
+
+ sumpq = zero;
+ winc = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ distsq = zero;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ xpt[k + j * xpt_dim1] -= xopt[j];
+ /* L10: */
+ /* Computing 2nd power */
+ d__1 = xpt[k + j * xpt_dim1];
+ distsq += d__1 * d__1;
+ }
+ sumpq += pq[k];
+ w[ndim + k] = distsq;
+ winc = std::max(winc,distsq);
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ /* L20: */
+ zmat[k + j * zmat_dim1] = zero;
+ }
+ }
+
+ /* Update HQ so that HQ and PQ define the second derivatives of the model */
+ /* after XBASE has been shifted to the trust region centre. */
+
+ ih = 0;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ w[j] = half * sumpq * xopt[j];
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L30: */
+ w[j] += pq[k] * xpt[k + j * xpt_dim1];
+ }
+ i__1 = j;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ ++ih;
+ /* L40: */
+ hq[ih] = hq[ih] + w[i__] * xopt[j] + w[j] * xopt[i__];
+ }
+ }
+
+ /* Shift XBASE, SL, SU and XOPT. Set the elements of BMAT to zero, and */
+ /* also set the elements of PTSAUX. */
+
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ xbase[j] += xopt[j];
+ sl[j] -= xopt[j];
+ su[j] -= xopt[j];
+ xopt[j] = zero;
+ /* Computing MIN */
+ d__1 = delta, d__2 = su[j];
+ ptsaux[(j << 1) + 1] = std::min(d__1,d__2);
+ /* Computing MAX */
+ d__1 = -(delta), d__2 = sl[j];
+ ptsaux[(j << 1) + 2] = std::max(d__1,d__2);
+ if (ptsaux[(j << 1) + 1] + ptsaux[(j << 1) + 2] < zero) {
+ temp = ptsaux[(j << 1) + 1];
+ ptsaux[(j << 1) + 1] = ptsaux[(j << 1) + 2];
+ ptsaux[(j << 1) + 2] = temp;
+ }
+ if ((d__2 = ptsaux[(j << 1) + 2], std::abs(d__2)) < half * (d__1 = ptsaux[(
+ j << 1) + 1], std::abs(d__1))) {
+ ptsaux[(j << 1) + 2] = half * ptsaux[(j << 1) + 1];
+ }
+ i__2 = ndim;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L50: */
+ bmat[i__ + j * bmat_dim1] = zero;
+ }
+ }
+ fbase = fval[kopt];
+
+ /* Set the identifiers of the artificial interpolation points that are */
+ /* along a coordinate direction from XOPT, and set the corresponding */
+ /* nonzero elements of BMAT and ZMAT. */
+
+ ptsid[1] = sfrac;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ jp = j + 1;
+ jpn = jp + n;
+ ptsid[jp] = (doublereal) j + sfrac;
+ if (jpn <= npt) {
+ ptsid[jpn] = (doublereal) j / (doublereal) np + sfrac;
+ temp = one / (ptsaux[(j << 1) + 1] - ptsaux[(j << 1) + 2]);
+ bmat[jp + j * bmat_dim1] = -temp + one / ptsaux[(j << 1) + 1];
+ bmat[jpn + j * bmat_dim1] = temp + one / ptsaux[(j << 1) + 2];
+ bmat[j * bmat_dim1 + 1] = -bmat[jp + j * bmat_dim1] - bmat[jpn +
+ j * bmat_dim1];
+ zmat[j * zmat_dim1 + 1] = std::sqrt(2.) / (d__1 = ptsaux[(j << 1) + 1]
+ * ptsaux[(j << 1) + 2], std::abs(d__1));
+ zmat[jp + j * zmat_dim1] = zmat[j * zmat_dim1 + 1] * ptsaux[(j <<
+ 1) + 2] * temp;
+ zmat[jpn + j * zmat_dim1] = -zmat[j * zmat_dim1 + 1] * ptsaux[(j
+ << 1) + 1] * temp;
+ } else {
+ bmat[j * bmat_dim1 + 1] = -one / ptsaux[(j << 1) + 1];
+ bmat[jp + j * bmat_dim1] = one / ptsaux[(j << 1) + 1];
+ /* Computing 2nd power */
+ d__1 = ptsaux[(j << 1) + 1];
+ bmat[j + npt + j * bmat_dim1] = -half * (d__1 * d__1);
+ }
+ /* L60: */
+ }
+
+ /* Set any remaining identifiers with their nonzero elements of ZMAT. */
+
+ if (npt >= n + np) {
+ i__2 = npt;
+ for (k = np << 1; k <= i__2; ++k) {
+ iw = (integer) (((doublereal) (k - np) - half) / (doublereal) (n)
+ );
+ ip = k - np - iw * n;
+ iq = ip + iw;
+ if (iq > n) {
+ iq -= n;
+ }
+ ptsid[k] = (doublereal) ip + (doublereal) iq / (doublereal) np +
+ sfrac;
+ temp = one / (ptsaux[(ip << 1) + 1] * ptsaux[(iq << 1) + 1]);
+ zmat[(k - np) * zmat_dim1 + 1] = temp;
+ zmat[ip + 1 + (k - np) * zmat_dim1] = -temp;
+ zmat[iq + 1 + (k - np) * zmat_dim1] = -temp;
+ /* L70: */
+ zmat[k + (k - np) * zmat_dim1] = temp;
+ }
+ }
+ nrem = npt;
+ kold = 1;
+ knew = kopt;
+
+ /* Reorder the provisional points in the way that exchanges PTSID(KOLD) */
+ /* with PTSID(KNEW). */
+
+L80:
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ temp = bmat[kold + j * bmat_dim1];
+ bmat[kold + j * bmat_dim1] = bmat[knew + j * bmat_dim1];
+ /* L90: */
+ bmat[knew + j * bmat_dim1] = temp;
+ }
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ temp = zmat[kold + j * zmat_dim1];
+ zmat[kold + j * zmat_dim1] = zmat[knew + j * zmat_dim1];
+ /* L100: */
+ zmat[knew + j * zmat_dim1] = temp;
+ }
+ ptsid[kold] = ptsid[knew];
+ ptsid[knew] = zero;
+ w[ndim + knew] = zero;
+ --nrem;
+ if (knew != kopt) {
+ temp = vlag[kold];
+ vlag[kold] = vlag[knew];
+ vlag[knew] = temp;
+
+ /* Update the BMAT and ZMAT matrices so that the status of the KNEW-th */
+ /* interpolation point can be changed from provisional to original. The */
+ /* branch to label 350 occurs if all the original points are reinstated. */
+ /* The nonnegative values of W(NDIM+K) are required in the search below. */
+
+ update_(n, npt, &bmat[bmat_offset], &zmat[zmat_offset], ndim, &vlag[1],
+ beta, denom, knew, &w[1]);
+ if (nrem == 0) {
+ goto L350;
+ }
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L110: */
+ w[ndim + k] = (d__1 = w[ndim + k], std::abs(d__1));
+ }
+ }
+
+ /* Pick the index KNEW of an original interpolation point that has not */
+ /* yet replaced one of the provisional interpolation points, giving */
+ /* attention to the closeness to XOPT and to previous tries with KNEW. */
+
+L120:
+ dsqmin = zero;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ if (w[ndim + k] > zero) {
+ if (dsqmin == zero || w[ndim + k] < dsqmin) {
+ knew = k;
+ dsqmin = w[ndim + k];
+ }
+ }
+ /* L130: */
+ }
+ if (dsqmin == zero) {
+ goto L260;
+ }
+
+ /* Form the W-vector of the chosen original interpolation point. */
+
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ /* L140: */
+ w[npt + j] = xpt[knew + j * xpt_dim1];
+ }
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ sum = zero;
+ if (k == kopt) {
+ } else if (ptsid[k] == zero) {
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L150: */
+ sum += w[npt + j] * xpt[k + j * xpt_dim1];
+ }
+ } else {
+ ip = (integer) ptsid[k];
+ if (ip > 0) {
+ sum = w[npt + ip] * ptsaux[(ip << 1) + 1];
+ }
+ iq = (integer) ((doublereal) np * ptsid[k] - (doublereal) (ip *
+ np));
+ if (iq > 0) {
+ iw = 1;
+ if (ip == 0) {
+ iw = 2;
+ }
+ sum += w[npt + iq] * ptsaux[iw + (iq << 1)];
+ }
+ }
+ /* L160: */
+ w[k] = half * sum * sum;
+ }
+
+ /* Calculate VLAG and BETA for the required updating of the H matrix if */
+ /* XPT(KNEW,.) is reinstated in the set of interpolation points. */
+
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ sum = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L170: */
+ sum += bmat[k + j * bmat_dim1] * w[npt + j];
+ }
+ /* L180: */
+ vlag[k] = sum;
+ }
+ beta = zero;
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ sum = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L190: */
+ sum += zmat[k + j * zmat_dim1] * w[k];
+ }
+ beta -= sum * sum;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ /* L200: */
+ vlag[k] += sum * zmat[k + j * zmat_dim1];
+ }
+ }
+ bsum = zero;
+ distsq = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ sum = zero;
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ /* L210: */
+ sum += bmat[k + j * bmat_dim1] * w[k];
+ }
+ jp = j + npt;
+ bsum += sum * w[jp];
+ i__2 = ndim;
+ for (ip = npt + 1; ip <= i__2; ++ip) {
+ /* L220: */
+ sum += bmat[ip + j * bmat_dim1] * w[ip];
+ }
+ bsum += sum * w[jp];
+ vlag[jp] = sum;
+ /* L230: */
+ /* Computing 2nd power */
+ d__1 = xpt[knew + j * xpt_dim1];
+ distsq += d__1 * d__1;
+ }
+ beta = half * distsq * distsq + beta - bsum;
+ vlag[kopt] += one;
+
+ /* KOLD is set to the index of the provisional interpolation point that is */
+ /* going to be deleted to make way for the KNEW-th original interpolation */
+ /* point. The choice of KOLD is governed by the avoidance of a small value */
+ /* of the denominator in the updating calculation of UPDATE. */
+
+ denom = zero;
+ vlmxsq = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ if (ptsid[k] != zero) {
+ hdiag = zero;
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ /* L240: */
+ /* Computing 2nd power */
+ d__1 = zmat[k + j * zmat_dim1];
+ hdiag += d__1 * d__1;
+ }
+ /* Computing 2nd power */
+ d__1 = vlag[k];
+ den = beta * hdiag + d__1 * d__1;
+ if (den > denom) {
+ kold = k;
+ denom = den;
+ }
+ }
+ /* L250: */
+ /* Computing MAX */
+ /* Computing 2nd power */
+ d__3 = vlag[k];
+ d__1 = vlmxsq, d__2 = d__3 * d__3;
+ vlmxsq = std::max(d__1,d__2);
+ }
+ if (denom <= vlmxsq * .01) {
+ w[ndim + knew] = -w[ndim + knew] - winc;
+ goto L120;
+ }
+ goto L80;
+
+ /* When label 260 is reached, all the final positions of the interpolation */
+ /* points have been chosen although any changes have not been included yet */
+ /* in XPT. Also the final BMAT and ZMAT matrices are complete, but, apart */
+ /* from the shift of XBASE, the updating of the quadratic model remains to */
+ /* be done. The following cycle through the new interpolation points begins */
+ /* by putting the new point in XPT(KPT,.) and by setting PQ(KPT) to zero, */
+ /* except that a RETURN occurs if MAXFUN prohibits another value of F. */
+
+L260:
+ i__1 = npt;
+ for (kpt = 1; kpt <= i__1; ++kpt) {
+ if (ptsid[kpt] == zero) {
+ goto L340;
+ }
+ if (nf >= maxfun) {
+ nf = -1;
+ goto L350;
+ }
+ ih = 0;
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ w[j] = xpt[kpt + j * xpt_dim1];
+ xpt[kpt + j * xpt_dim1] = zero;
+ temp = pq[kpt] * w[j];
+ i__3 = j;
+ for (i__ = 1; i__ <= i__3; ++i__) {
+ ++ih;
+ /* L270: */
+ hq[ih] += temp * w[i__];
+ }
+ }
+ pq[kpt] = zero;
+ ip = (integer) ptsid[kpt];
+ iq = (integer) ((doublereal) np * ptsid[kpt] - (doublereal) (ip * np))
+ ;
+ if (ip > 0) {
+ xp = ptsaux[(ip << 1) + 1];
+ xpt[kpt + ip * xpt_dim1] = xp;
+ }
+ if (iq > 0) {
+ xq = ptsaux[(iq << 1) + 1];
+ if (ip == 0) {
+ xq = ptsaux[(iq << 1) + 2];
+ }
+ xpt[kpt + iq * xpt_dim1] = xq;
+ }
+
+ /* Set VQUAD to the value of the current model at the new point. */
+
+ vquad = fbase;
+ if (ip > 0) {
+ ihp = (ip + ip * ip) / 2;
+ vquad += xp * (gopt[ip] + half * xp * hq[ihp]);
+ }
+ if (iq > 0) {
+ ihq = (iq + iq * iq) / 2;
+ vquad += xq * (gopt[iq] + half * xq * hq[ihq]);
+ if (ip > 0) {
+ iw = std::max(ihp,ihq) - (i__3 = ip - iq, std::abs(i__3));
+ vquad += xp * xq * hq[iw];
+ }
+ }
+ i__3 = npt;
+ for (k = 1; k <= i__3; ++k) {
+ temp = zero;
+ if (ip > 0) {
+ temp += xp * xpt[k + ip * xpt_dim1];
+ }
+ if (iq > 0) {
+ temp += xq * xpt[k + iq * xpt_dim1];
+ }
+ /* L280: */
+ vquad += half * pq[k] * temp * temp;
+ }
+
+ /* Calculate F at the new interpolation point, and set DIFF to the factor */
+ /* that is going to multiply the KPT-th Lagrange function when the model */
+ /* is updated to provide interpolation to the new function value. */
+
+ i__3 = n;
+ for (i__ = 1; i__ <= i__3; ++i__) {
+ /* Computing MIN */
+ /* Computing MAX */
+ d__3 = xl[i__], d__4 = xbase[i__] + xpt[kpt + i__ * xpt_dim1];
+ d__1 = std::max(d__3,d__4), d__2 = xu[i__];
+ w[i__] = std::min(d__1,d__2);
+ if (xpt[kpt + i__ * xpt_dim1] == sl[i__]) {
+ w[i__] = xl[i__];
+ }
+ if (xpt[kpt + i__ * xpt_dim1] == su[i__]) {
+ w[i__] = xu[i__];
+ }
+ /* L290: */
+ }
+ ++(nf);
+ f = calfun(mat(&w[1],n));
+ fval[kpt] = f;
+ if (f < fval[kopt]) {
+ kopt = kpt;
+ }
+ diff = f - vquad;
+
+ /* Update the quadratic model. The RETURN from the subroutine occurs when */
+ /* all the new interpolation points are included in the model. */
+
+ i__3 = n;
+ for (i__ = 1; i__ <= i__3; ++i__) {
+ /* L310: */
+ gopt[i__] += diff * bmat[kpt + i__ * bmat_dim1];
+ }
+ i__3 = npt;
+ for (k = 1; k <= i__3; ++k) {
+ sum = zero;
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ /* L320: */
+ sum += zmat[k + j * zmat_dim1] * zmat[kpt + j * zmat_dim1];
+ }
+ temp = diff * sum;
+ if (ptsid[k] == zero) {
+ pq[k] += temp;
+ } else {
+ ip = (integer) ptsid[k];
+ iq = (integer) ((doublereal) np * ptsid[k] - (doublereal) (ip
+ * np));
+ ihq = (iq * iq + iq) / 2;
+ if (ip == 0) {
+ /* Computing 2nd power */
+ d__1 = ptsaux[(iq << 1) + 2];
+ hq[ihq] += temp * (d__1 * d__1);
+ } else {
+ ihp = (ip * ip + ip) / 2;
+ /* Computing 2nd power */
+ d__1 = ptsaux[(ip << 1) + 1];
+ hq[ihp] += temp * (d__1 * d__1);
+ if (iq > 0) {
+ /* Computing 2nd power */
+ d__1 = ptsaux[(iq << 1) + 1];
+ hq[ihq] += temp * (d__1 * d__1);
+ iw = std::max(ihp,ihq) - (i__2 = iq - ip, std::abs(i__2));
+ hq[iw] += temp * ptsaux[(ip << 1) + 1] * ptsaux[(iq <<
+ 1) + 1];
+ }
+ }
+ }
+ /* L330: */
+ }
+ ptsid[kpt] = zero;
+L340:
+ ;
+ }
+L350:
+ ;
+ } /* rescue_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ void trsbox_(
+ const integer n,
+ const integer npt,
+ const doublereal *xpt,
+ const doublereal *xopt,
+ const doublereal *gopt,
+ const doublereal *hq,
+ const doublereal *pq,
+ const doublereal *sl,
+ const doublereal *su,
+ const doublereal delta,
+ doublereal *xnew,
+ doublereal *d__,
+ doublereal *gnew,
+ doublereal *xbdi,
+ doublereal *s,
+ doublereal *hs,
+ doublereal *hred,
+ doublereal *dsq,
+ doublereal *crvmin
+ ) const
+ {
+ /* System generated locals */
+ integer xpt_dim1, xpt_offset, i__1, i__2;
+ doublereal d__1, d__2, d__3, d__4;
+
+ /* Local variables */
+ integer i__, j, k, ih;
+ doublereal ds;
+ integer iu;
+ doublereal dhd, dhs, cth, one, shs, sth, ssq, half, beta, sdec, blen;
+ integer iact = 0, nact = 0;
+ doublereal angt, qred;
+ integer isav;
+ doublereal temp = 0, zero = 0, xsav = 0, xsum = 0, angbd = 0, dredg = 0, sredg = 0;
+ integer iterc;
+ doublereal resid = 0, delsq = 0, ggsav = 0, tempa = 0, tempb = 0,
+ redmax = 0, dredsq = 0, redsav = 0, onemin = 0, gredsq = 0, rednew = 0;
+ integer itcsav = 0;
+ doublereal rdprev = 0, rdnext = 0, stplen = 0, stepsq = 0;
+ integer itermax = 0;
+
+
+ /* The arguments N, NPT, XPT, XOPT, GOPT, HQ, PQ, SL and SU have the same */
+ /* meanings as the corresponding arguments of BOBYQB. */
+ /* DELTA is the trust region radius for the present calculation, which */
+ /* seeks a small value of the quadratic model within distance DELTA of */
+ /* XOPT subject to the bounds on the variables. */
+ /* XNEW will be set to a new vector of variables that is approximately */
+ /* the one that minimizes the quadratic model within the trust region */
+ /* subject to the SL and SU constraints on the variables. It satisfies */
+ /* as equations the bounds that become active during the calculation. */
+ /* D is the calculated trial step from XOPT, generated iteratively from an */
+ /* initial value of zero. Thus XNEW is XOPT+D after the final iteration. */
+ /* GNEW holds the gradient of the quadratic model at XOPT+D. It is updated */
+ /* when D is updated. */
+ /* XBDI is a working space vector. For I=1,2,...,N, the element XBDI(I) is */
+ /* set to -1.0, 0.0, or 1.0, the value being nonzero if and only if the */
+ /* I-th variable has become fixed at a bound, the bound being SL(I) or */
+ /* SU(I) in the case XBDI(I)=-1.0 or XBDI(I)=1.0, respectively. This */
+ /* information is accumulated during the construction of XNEW. */
+ /* The arrays S, HS and HRED are also used for working space. They hold the */
+ /* current search direction, and the changes in the gradient of Q along S */
+ /* and the reduced D, respectively, where the reduced D is the same as D, */
+ /* except that the components of the fixed variables are zero. */
+ /* DSQ will be set to the square of the length of XNEW-XOPT. */
+ /* CRVMIN is set to zero if D reaches the trust region boundary. Otherwise */
+ /* it is set to the least curvature of H that occurs in the conjugate */
+ /* gradient searches that are not restricted by any constraints. The */
+ /* value CRVMIN=-1.0D0 is set, however, if all of these searches are */
+ /* constrained. */
+
+ /* A version of the truncated conjugate gradient is applied. If a line */
+ /* search is restricted by a constraint, then the procedure is restarted, */
+ /* the values of the variables that are at their bounds being fixed. If */
+ /* the trust region boundary is reached, then further changes may be made */
+ /* to D, each one being in the two dimensional space that is spanned */
+ /* by the current D and the gradient of Q at XOPT+D, staying on the trust */
+ /* region boundary. Termination occurs when the reduction in Q seems to */
+ /* be close to the greatest reduction that can be achieved. */
+
+ /* Set some constants. */
+
+ /* Parameter adjustments */
+ xpt_dim1 = npt;
+ xpt_offset = 1 + xpt_dim1;
+ xpt -= xpt_offset;
+ --xopt;
+ --gopt;
+ --hq;
+ --pq;
+ --sl;
+ --su;
+ --xnew;
+ --d__;
+ --gnew;
+ --xbdi;
+ --s;
+ --hs;
+ --hred;
+
+ /* Function Body */
+ half = .5;
+ one = 1.;
+ onemin = -1.;
+ zero = 0.;
+
+ /* The sign of GOPT(I) gives the sign of the change to the I-th variable */
+ /* that will reduce Q from its value at XOPT. Thus XBDI(I) shows whether */
+ /* or not to fix the I-th variable at one of its bounds initially, with */
+ /* NACT being set to the number of fixed variables. D and GNEW are also */
+ /* set for the first iteration. DELSQ is the upper bound on the sum of */
+ /* squares of the free variables. QRED is the reduction in Q so far. */
+
+ iterc = 0;
+ nact = 0;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ xbdi[i__] = zero;
+ if (xopt[i__] <= sl[i__]) {
+ if (gopt[i__] >= zero) {
+ xbdi[i__] = onemin;
+ }
+ } else if (xopt[i__] >= su[i__]) {
+ if (gopt[i__] <= zero) {
+ xbdi[i__] = one;
+ }
+ }
+ if (xbdi[i__] != zero) {
+ ++nact;
+ }
+ d__[i__] = zero;
+ /* L10: */
+ gnew[i__] = gopt[i__];
+ }
+ delsq = delta * delta;
+ qred = zero;
+ *crvmin = onemin;
+
+ /* Set the next search direction of the conjugate gradient method. It is */
+ /* the steepest descent direction initially and when the iterations are */
+ /* restarted because a variable has just been fixed by a bound, and of */
+ /* course the components of the fixed variables are zero. ITERMAX is an */
+ /* upper bound on the indices of the conjugate gradient iterations. */
+
+L20:
+ beta = zero;
+L30:
+ stepsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] != zero) {
+ s[i__] = zero;
+ } else if (beta == zero) {
+ s[i__] = -gnew[i__];
+ } else {
+ s[i__] = beta * s[i__] - gnew[i__];
+ }
+ /* L40: */
+ /* Computing 2nd power */
+ d__1 = s[i__];
+ stepsq += d__1 * d__1;
+ }
+ if (stepsq == zero) {
+ goto L190;
+ }
+ if (beta == zero) {
+ gredsq = stepsq;
+ itermax = iterc + n - nact;
+ }
+ if (gredsq * delsq <= qred * 1e-4 * qred) {
+ goto L190;
+ }
+
+ /* Multiply the search direction by the second derivative matrix of Q and */
+ /* calculate some scalars for the choice of steplength. Then set BLEN to */
+ /* the length of the the step to the trust region boundary and STPLEN to */
+ /* the steplength, ignoring the simple bounds. */
+
+ goto L210;
+L50:
+ resid = delsq;
+ ds = zero;
+ shs = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] == zero) {
+ /* Computing 2nd power */
+ d__1 = d__[i__];
+ resid -= d__1 * d__1;
+ ds += s[i__] * d__[i__];
+ shs += s[i__] * hs[i__];
+ }
+ /* L60: */
+ }
+ if (resid <= zero) {
+ goto L90;
+ }
+ temp = std::sqrt(stepsq * resid + ds * ds);
+ if (ds < zero) {
+ blen = (temp - ds) / stepsq;
+ } else {
+ blen = resid / (temp + ds);
+ }
+ stplen = blen;
+ if (shs > zero) {
+ /* Computing MIN */
+ d__1 = blen, d__2 = gredsq / shs;
+ stplen = std::min(d__1,d__2);
+ }
+
+ /* Reduce STPLEN if necessary in order to preserve the simple bounds, */
+ /* letting IACT be the index of the new constrained variable. */
+
+ iact = 0;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (s[i__] != zero) {
+ xsum = xopt[i__] + d__[i__];
+ if (s[i__] > zero) {
+ temp = (su[i__] - xsum) / s[i__];
+ } else {
+ temp = (sl[i__] - xsum) / s[i__];
+ }
+ if (temp < stplen) {
+ stplen = temp;
+ iact = i__;
+ }
+ }
+ /* L70: */
+ }
+
+ /* Update CRVMIN, GNEW and D. Set SDEC to the decrease that occurs in Q. */
+
+ sdec = zero;
+ if (stplen > zero) {
+ ++iterc;
+ temp = shs / stepsq;
+ if (iact == 0 && temp > zero) {
+ *crvmin = std::min(*crvmin,temp);
+ if (*crvmin == onemin) {
+ *crvmin = temp;
+ }
+ }
+ ggsav = gredsq;
+ gredsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ gnew[i__] += stplen * hs[i__];
+ if (xbdi[i__] == zero) {
+ /* Computing 2nd power */
+ d__1 = gnew[i__];
+ gredsq += d__1 * d__1;
+ }
+ /* L80: */
+ d__[i__] += stplen * s[i__];
+ }
+ /* Computing MAX */
+ d__1 = stplen * (ggsav - half * stplen * shs);
+ sdec = std::max(d__1,zero);
+ qred += sdec;
+ }
+
+ /* Restart the conjugate gradient method if it has hit a new bound. */
+
+ if (iact > 0) {
+ ++nact;
+ xbdi[iact] = one;
+ if (s[iact] < zero) {
+ xbdi[iact] = onemin;
+ }
+ /* Computing 2nd power */
+ d__1 = d__[iact];
+ delsq -= d__1 * d__1;
+ if (delsq <= zero) {
+ goto L90;
+ }
+ goto L20;
+ }
+
+ /* If STPLEN is less than BLEN, then either apply another conjugate */
+ /* gradient iteration or RETURN. */
+
+ if (stplen < blen) {
+ if (iterc == itermax) {
+ goto L190;
+ }
+ if (sdec <= qred * .01) {
+ goto L190;
+ }
+ beta = gredsq / ggsav;
+ goto L30;
+ }
+L90:
+ *crvmin = zero;
+
+ /* Prepare for the alternative iteration by calculating some scalars */
+ /* and by multiplying the reduced D by the second derivative matrix of */
+ /* Q, where S holds the reduced D in the call of GGMULT. */
+
+L100:
+ if (nact >= n - 1) {
+ goto L190;
+ }
+ dredsq = zero;
+ dredg = zero;
+ gredsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] == zero) {
+ /* Computing 2nd power */
+ d__1 = d__[i__];
+ dredsq += d__1 * d__1;
+ dredg += d__[i__] * gnew[i__];
+ /* Computing 2nd power */
+ d__1 = gnew[i__];
+ gredsq += d__1 * d__1;
+ s[i__] = d__[i__];
+ } else {
+ s[i__] = zero;
+ }
+ /* L110: */
+ }
+ itcsav = iterc;
+ goto L210;
+
+ /* Let the search direction S be a linear combination of the reduced D */
+ /* and the reduced G that is orthogonal to the reduced D. */
+
+L120:
+ ++iterc;
+ temp = gredsq * dredsq - dredg * dredg;
+ if (temp <= qred * 1e-4 * qred) {
+ goto L190;
+ }
+ temp = std::sqrt(temp);
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] == zero) {
+ s[i__] = (dredg * d__[i__] - dredsq * gnew[i__]) / temp;
+ } else {
+ s[i__] = zero;
+ }
+ /* L130: */
+ }
+ sredg = -temp;
+
+ /* By considering the simple bounds on the variables, calculate an upper */
+ /* bound on the tangent of half the angle of the alternative iteration, */
+ /* namely ANGBD, except that, if already a free variable has reached a */
+ /* bound, there is a branch back to label 100 after fixing that variable. */
+
+ angbd = one;
+ iact = 0;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] == zero) {
+ tempa = xopt[i__] + d__[i__] - sl[i__];
+ tempb = su[i__] - xopt[i__] - d__[i__];
+ if (tempa <= zero) {
+ ++nact;
+ xbdi[i__] = onemin;
+ goto L100;
+ } else if (tempb <= zero) {
+ ++nact;
+ xbdi[i__] = one;
+ goto L100;
+ }
+ /* Computing 2nd power */
+ d__1 = d__[i__];
+ /* Computing 2nd power */
+ d__2 = s[i__];
+ ssq = d__1 * d__1 + d__2 * d__2;
+ /* Computing 2nd power */
+ d__1 = xopt[i__] - sl[i__];
+ temp = ssq - d__1 * d__1;
+ if (temp > zero) {
+ temp = std::sqrt(temp) - s[i__];
+ if (angbd * temp > tempa) {
+ angbd = tempa / temp;
+ iact = i__;
+ xsav = onemin;
+ }
+ }
+ /* Computing 2nd power */
+ d__1 = su[i__] - xopt[i__];
+ temp = ssq - d__1 * d__1;
+ if (temp > zero) {
+ temp = std::sqrt(temp) + s[i__];
+ if (angbd * temp > tempb) {
+ angbd = tempb / temp;
+ iact = i__;
+ xsav = one;
+ }
+ }
+ }
+ /* L140: */
+ }
+
+ /* Calculate HHD and some curvatures for the alternative iteration. */
+
+ goto L210;
+L150:
+ shs = zero;
+ dhs = zero;
+ dhd = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ if (xbdi[i__] == zero) {
+ shs += s[i__] * hs[i__];
+ dhs += d__[i__] * hs[i__];
+ dhd += d__[i__] * hred[i__];
+ }
+ /* L160: */
+ }
+
+ /* Seek the greatest reduction in Q for a range of equally spaced values */
+ /* of ANGT in [0,ANGBD], where ANGT is the tangent of half the angle of */
+ /* the alternative iteration. */
+
+ redmax = zero;
+ isav = 0;
+ redsav = zero;
+ iu = (integer) (angbd * 17. + 3.1);
+ i__1 = iu;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ angt = angbd * (doublereal) i__ / (doublereal) iu;
+ sth = (angt + angt) / (one + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ rednew = sth * (angt * dredg - sredg - half * sth * temp);
+ if (rednew > redmax) {
+ redmax = rednew;
+ isav = i__;
+ rdprev = redsav;
+ } else if (i__ == isav + 1) {
+ rdnext = rednew;
+ }
+ /* L170: */
+ redsav = rednew;
+ }
+
+ /* Return if the reduction is zero. Otherwise, set the sine and cosine */
+ /* of the angle of the alternative iteration, and calculate SDEC. */
+
+ if (isav == 0) {
+ goto L190;
+ }
+ if (isav < iu) {
+ temp = (rdnext - rdprev) / (redmax + redmax - rdprev - rdnext);
+ angt = angbd * ((doublereal) isav + half * temp) / (doublereal) iu;
+ }
+ cth = (one - angt * angt) / (one + angt * angt);
+ sth = (angt + angt) / (one + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ sdec = sth * (angt * dredg - sredg - half * sth * temp);
+ if (sdec <= zero) {
+ goto L190;
+ }
+
+ /* Update GNEW, D and HRED. If the angle of the alternative iteration */
+ /* is restricted by a bound on a free variable, that variable is fixed */
+ /* at the bound. */
+
+ dredg = zero;
+ gredsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ gnew[i__] = gnew[i__] + (cth - one) * hred[i__] + sth * hs[i__];
+ if (xbdi[i__] == zero) {
+ d__[i__] = cth * d__[i__] + sth * s[i__];
+ dredg += d__[i__] * gnew[i__];
+ /* Computing 2nd power */
+ d__1 = gnew[i__];
+ gredsq += d__1 * d__1;
+ }
+ /* L180: */
+ hred[i__] = cth * hred[i__] + sth * hs[i__];
+ }
+ qred += sdec;
+ if (iact > 0 && isav == iu) {
+ ++nact;
+ xbdi[iact] = xsav;
+ goto L100;
+ }
+
+ /* If SDEC is sufficiently small, then RETURN after setting XNEW to */
+ /* XOPT+D, giving careful attention to the bounds. */
+
+ if (sdec > qred * .01) {
+ goto L120;
+ }
+L190:
+ *dsq = zero;
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* Computing MAX */
+ /* Computing MIN */
+ d__3 = xopt[i__] + d__[i__], d__4 = su[i__];
+ d__1 = std::min(d__3,d__4), d__2 = sl[i__];
+ xnew[i__] = std::max(d__1,d__2);
+ if (xbdi[i__] == onemin) {
+ xnew[i__] = sl[i__];
+ }
+ if (xbdi[i__] == one) {
+ xnew[i__] = su[i__];
+ }
+ d__[i__] = xnew[i__] - xopt[i__];
+ /* L200: */
+ /* Computing 2nd power */
+ d__1 = d__[i__];
+ *dsq += d__1 * d__1;
+ }
+ return;
+ /* The following instructions multiply the current S-vector by the second */
+ /* derivative matrix of the quadratic model, putting the product in HS. */
+ /* They are reached from three different parts of the software above and */
+ /* they can be regarded as an external subroutine. */
+
+L210:
+ ih = 0;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ hs[j] = zero;
+ i__2 = j;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ ++ih;
+ if (i__ < j) {
+ hs[j] += hq[ih] * s[i__];
+ }
+ /* L220: */
+ hs[i__] += hq[ih] * s[j];
+ }
+ }
+ i__2 = npt;
+ for (k = 1; k <= i__2; ++k) {
+ if (pq[k] != zero) {
+ temp = zero;
+ i__1 = n;
+ for (j = 1; j <= i__1; ++j) {
+ /* L230: */
+ temp += xpt[k + j * xpt_dim1] * s[j];
+ }
+ temp *= pq[k];
+ i__1 = n;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ /* L240: */
+ hs[i__] += temp * xpt[k + i__ * xpt_dim1];
+ }
+ }
+ /* L250: */
+ }
+ if (*crvmin != zero) {
+ goto L50;
+ }
+ if (iterc > itcsav) {
+ goto L150;
+ }
+ i__2 = n;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L260: */
+ hred[i__] = hs[i__];
+ }
+ goto L120;
+ } /* trsbox_ */
+
+ // ----------------------------------------------------------------------------------------
+
+ void update_(
+ const integer n,
+ const integer npt,
+ doublereal *bmat,
+ doublereal *zmat,
+ const integer ndim,
+ doublereal *vlag,
+ const doublereal beta,
+ const doublereal denom,
+ const integer knew,
+ doublereal *w
+ ) const
+ {
+ /* System generated locals */
+ integer bmat_dim1, bmat_offset, zmat_dim1, zmat_offset, i__1, i__2;
+ doublereal d__1, d__2, d__3;
+
+ /* Local variables */
+ integer i__, j, k, jp;
+ doublereal one, tau, temp;
+ integer nptm;
+ doublereal zero, alpha, tempa, tempb, ztest;
+
+
+ /* The arrays BMAT and ZMAT are updated, as required by the new position */
+ /* of the interpolation point that has the index KNEW. The vector VLAG has */
+ /* N+NPT components, set on entry to the first NPT and last N components */
+ /* of the product Hw in equation (4.11) of the Powell (2006) paper on */
+ /* NEWUOA. Further, BETA is set on entry to the value of the parameter */
+ /* with that name, and DENOM is set to the denominator of the updating */
+ /* formula. Elements of ZMAT may be treated as zero if their moduli are */
+ /* at most ZTEST. The first NDIM elements of W are used for working space. */
+
+ /* Set some constants. */
+
+ /* Parameter adjustments */
+ zmat_dim1 = npt;
+ zmat_offset = 1 + zmat_dim1;
+ zmat -= zmat_offset;
+ bmat_dim1 = ndim;
+ bmat_offset = 1 + bmat_dim1;
+ bmat -= bmat_offset;
+ --vlag;
+ --w;
+
+ /* Function Body */
+ one = 1.;
+ zero = 0.;
+ nptm = npt - n - 1;
+ ztest = zero;
+ i__1 = npt;
+ for (k = 1; k <= i__1; ++k) {
+ i__2 = nptm;
+ for (j = 1; j <= i__2; ++j) {
+ /* L10: */
+ /* Computing MAX */
+ d__2 = ztest, d__3 = (d__1 = zmat[k + j * zmat_dim1], std::abs(d__1));
+ ztest = std::max(d__2,d__3);
+ }
+ }
+ ztest *= 1e-20;
+
+ /* Apply the rotations that put zeros in the KNEW-th row of ZMAT. */
+
+ i__2 = nptm;
+ for (j = 2; j <= i__2; ++j) {
+ if ((d__1 = zmat[knew + j * zmat_dim1], std::abs(d__1)) > ztest) {
+ /* Computing 2nd power */
+ d__1 = zmat[knew + zmat_dim1];
+ /* Computing 2nd power */
+ d__2 = zmat[knew + j * zmat_dim1];
+ temp = std::sqrt(d__1 * d__1 + d__2 * d__2);
+ tempa = zmat[knew + zmat_dim1] / temp;
+ tempb = zmat[knew + j * zmat_dim1] / temp;
+ i__1 = npt;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ temp = tempa * zmat[i__ + zmat_dim1] + tempb * zmat[i__ + j *
+ zmat_dim1];
+ zmat[i__ + j * zmat_dim1] = tempa * zmat[i__ + j * zmat_dim1]
+ - tempb * zmat[i__ + zmat_dim1];
+ /* L20: */
+ zmat[i__ + zmat_dim1] = temp;
+ }
+ }
+ zmat[knew + j * zmat_dim1] = zero;
+ /* L30: */
+ }
+
+ /* Put the first NPT components of the KNEW-th column of HLAG into W, */
+ /* and calculate the parameters of the updating formula. */
+
+ i__2 = npt;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ w[i__] = zmat[knew + zmat_dim1] * zmat[i__ + zmat_dim1];
+ /* L40: */
+ }
+ alpha = w[knew];
+ tau = vlag[knew];
+ vlag[knew] -= one;
+
+ /* Complete the updating of ZMAT. */
+
+ temp = std::sqrt(denom);
+ tempb = zmat[knew + zmat_dim1] / temp;
+ tempa = tau / temp;
+ i__2 = npt;
+ for (i__ = 1; i__ <= i__2; ++i__) {
+ /* L50: */
+ zmat[i__ + zmat_dim1] = tempa * zmat[i__ + zmat_dim1] - tempb * vlag[
+ i__];
+ }
+
+ /* Finally, update the matrix BMAT. */
+
+ i__2 = n;
+ for (j = 1; j <= i__2; ++j) {
+ jp = npt + j;
+ w[jp] = bmat[knew + j * bmat_dim1];
+ tempa = (alpha * vlag[jp] - tau * w[jp]) / denom;
+ tempb = (-(beta) * w[jp] - tau * vlag[jp]) / denom;
+ i__1 = jp;
+ for (i__ = 1; i__ <= i__1; ++i__) {
+ bmat[i__ + j * bmat_dim1] = bmat[i__ + j * bmat_dim1] + tempa *
+ vlag[i__] + tempb * w[i__];
+ if (i__ > npt) {
+ bmat[jp + (i__ - npt) * bmat_dim1] = bmat[i__ + j *
+ bmat_dim1];
+ }
+ /* L60: */
+ }
+ }
+ } /* update_ */
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename funct,
+ typename T,
+ typename U
+ >
+ double find_min_bobyqa (
+ const funct& f,
+ T& x,
+ long npt,
+ const U& x_lower,
+ const U& x_upper,
+ const double rho_begin,
+ const double rho_end,
+ const long max_f_evals
+ )
+ {
+ // The starting point (i.e. x) must be a column vector.
+ COMPILE_TIME_ASSERT(T::NC <= 1);
+
+ // check the requirements. Also split the assert up so that the error message isn't huge.
+ DLIB_CASSERT(is_col_vector(x) && is_col_vector(x_lower) && is_col_vector(x_upper) &&
+ x.size() == x_lower.size() && x_lower.size() == x_upper.size() &&
+ x.size() > 1 && max_f_evals > 1,
+ "\tdouble find_min_bobyqa()"
+ << "\n\t Invalid arguments have been given to this function"
+ << "\n\t is_col_vector(x): " << is_col_vector(x)
+ << "\n\t is_col_vector(x_lower): " << is_col_vector(x_lower)
+ << "\n\t is_col_vector(x_upper): " << is_col_vector(x_upper)
+ << "\n\t x.size(): " << x.size()
+ << "\n\t x_lower.size(): " << x_lower.size()
+ << "\n\t x_upper.size(): " << x_upper.size()
+ << "\n\t max_f_evals: " << max_f_evals
+ );
+
+ DLIB_CASSERT(x.size() + 2 <= npt && npt <= (x.size()+1)*(x.size()+2)/2 &&
+ 0 < rho_end && rho_end < rho_begin &&
+ min(x_upper - x_lower) > 2*rho_begin &&
+ min(x - x_lower) >= 0 && min(x_upper - x) >= 0,
+ "\tdouble find_min_bobyqa()"
+ << "\n\t Invalid arguments have been given to this function"
+ << "\n\t ntp in valid range: " << (x.size() + 2 <= npt && npt <= (x.size()+1)*(x.size()+2)/2)
+ << "\n\t npt: " << npt
+ << "\n\t rho_begin: " << rho_begin
+ << "\n\t rho_end: " << rho_end
+ << "\n\t min(x_upper - x_lower) > 2*rho_begin: " << (min(x_upper - x_lower) > 2*rho_begin)
+ << "\n\t min(x - x_lower) >= 0 && min(x_upper - x) >= 0: " << (min(x - x_lower) >= 0 && min(x_upper - x) >= 0)
+ );
+
+
+ bobyqa_implementation impl;
+ return impl.find_min(f, x, npt, x_lower, x_upper, rho_begin, rho_end, max_f_evals);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename funct,
+ typename T,
+ typename U
+ >
+ double find_max_bobyqa (
+ const funct& f,
+ T& x,
+ long npt,
+ const U& x_lower,
+ const U& x_upper,
+ const double rho_begin,
+ const double rho_end,
+ const long max_f_evals
+ )
+ {
+ // The starting point (i.e. x) must be a column vector.
+ COMPILE_TIME_ASSERT(T::NC <= 1);
+
+ return -find_min_bobyqa(negate_function(f), x, npt, x_lower, x_upper, rho_begin, rho_end, max_f_evals);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_OPTIMIZATIOn_BOBYQA_Hh_
+