/*
 * Roby Joehanes
 *  
 * Copyright 2012 Roby Joehanes
 * This file is distributed under the GNU General Public License version 3.0.
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package jdistlib.math.opt;

import static java.lang.Math.*;
import jdistlib.math.MultivariableFunction;

/**
 * Translation of the infamous Bobyqa algorithm by Michael J. D. Powell. Original Fortran code
 * can be found here.
 * Paper is here
 * 
 * @author Roby Joehanes
 *
 */
public class Bobyqa extends MultivariateOptimization {

	public OptimizationResult optimize(OptimizationConfig cfg)
	{
		BobyqaConfig bcfg = null;
		if (cfg instanceof BobyqaConfig) {
			bcfg = (BobyqaConfig) cfg;
		} else {
			bcfg = new BobyqaConfig(cfg);
		}
		return bobyqa(bcfg.initialGuess, bcfg.lowerBound, bcfg.upperBound,
			bcfg.objectiveFunction, bcfg.maxNumFunctionCall, bcfg.initialTrustRegionRadius,
			bcfg.stoppingTrustRegionRadius, bcfg.numInterpolationPoints, bcfg.isMinimize);
	}

	/*
	private static String toString(double[] value) {
		if (value == null) return "-";
		int n = value.length;
		StringBuilder buf = new StringBuilder();
		buf.append(value[0]);
		for (int i = 1; i < n; i++)
			buf.append(", " + value[i]);
		return buf.toString();
	}
	private static String toString(double[][] value) {
		if (value == null) return "-";
		StringBuilder buffer = new StringBuilder();
		int numValues = value.length;
		for (int i = 0; i < numValues; i++)
			buffer.append(toString(value[i]) + "\n");
		return buffer.toString();
	}

	static java.io.PrintWriter writer = null;
	static {
		try {
			writer = new java.io.PrintWriter("C:\\Users\\robbyjo\\Desktop\\mybobyqa.txt");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	static final void printState(String msg, int nf, double[] xbase, double[] xopt, double[] fval, double[] vlag, double[] d, double[][] bmat, double[][] zmat, double[][] xpt)
	{
		writer.println(msg);
		writer.println("nf: " + nf);
		writer.println("xbase: " + toString(xbase));
		writer.println("xopt: " + toString(xopt));
		writer.println("fval: " + toString(fval));
		writer.println("vlag: " + toString(vlag));
		writer.println("d: " + toString(d));
		writer.println("bmat: " + toString(bmat));
		writer.println("zmat: " + toString(zmat));
		writer.println("xpt: " + toString(xpt));
		writer.flush();
	}
	//*/

	/**
	 * 
	 * 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.
	 * The value of IPRINT should be set to 0, 1, 2 or 3, which controls the
	 *   amount of printing. Specifically, there is no output if IPRINT=0 and
	 *   there is output only at the return if IPRINT=1. Otherwise, each new
	 *   value of RHO is printed, with the best vector of variables so far and
	 *   the corresponding value of the objective function. Further, each new
	 *   value of F with its variables are output if IPRINT=3.
	 * 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.
	 *   
	 * DOUBLE PRECISION FUNCTION CALFUN (N,X,IP) has to be provided by
	 * the user. It returns the value of the objective function for
	 * the current values of the variables X(1),X(2),...,X(N), which are
	 * generated automatically in a way that satisfies the bounds given
	 * in XL and XU.
	 * 
	 * Return if the value of NPT is unacceptable.
	 * 
*

RJ's note: Storage bound [3.5*n^2 + 23.5*n + 14, (n^4 + 35*n^2)/4 + 2*n^3 + 24*n + 6] * @param x Initial estimate * @param xl Lower estimate * @param xu Upper estimate * @param calfun Function to optimize * @param npt Number of interpolation points * @param rhobeg Initial trust region radius * @param rhoend Final trust region radius * @param maxfun Maximum number of calls to calfun * @param isMinimize true if goal is to find the global minimum */ public static final OptimizationResult bobyqa(double[] x, double[] xl, double[] xu, MultivariableFunction calfun, int npt, double rhobeg, double rhoend, int maxfun, boolean isMinimize) { int n = x.length, np = n + 1, ndim = npt + n; final int toRescue = 190; if (xl.length != n || xu.length != n) // sanity check throw new RuntimeException(); if (npt < n + 2 || npt > ((n + 2) * np) / 2) throw new RuntimeException("Return from BOBYQA because NPT is not in the required interval"); double origx[] = x, // Store the original object since the object will be used for the storage of the result xbase[] = new double[n], xpt[][] = new double[npt][n], fval[] = new double[npt], xopt[] = new double[n], gopt[] = new double[n], hq[] = new double[n * np/2], pq[] = new double[npt], bmat[][] = new double[ndim][n], zmat[][] = new double[npt][npt-np], sl[] = new double[n], su[] = new double[n], xnew[] = new double[n], xalt[] = new double[n], d[] = new double[n], vlag[] = new double[ndim], work1[] = new double[n], work2[] = new double[npt], work3[] = new double[npt]; x = new double[n]; // Make a new array and copy it System.arraycopy(origx, 0, x, 0, n); double two_rhobeg = 2 * rhobeg; // int isl = 0 + n + n * npt + npt + n + n + (n*np) / 2 + npt + ndim * n + npt * (npt - np); //int iw = (0 + n + n * npt + npt + n + n + (n*np) / 2 + npt + ndim * n + npt * (npt - np)) + 5*n + ndim; for (int j = 0; j < n; j++) { double temp = xu[j] - xl[j]; if (temp < two_rhobeg) throw new RuntimeException("Return from BOBYQA because one of the differences xu[i]-xl[i] is less than 2*RHOBEG"); sl[j] = xl[j] - x[j]; su[j] = xu[j] - x[j]; if (sl[j] >= -rhobeg) { if (sl[j] >= 0) { x[j] = xl[j]; sl[j] = 0; su[j] = temp; } else { x[j] = xl[j] + rhobeg; sl[j] = -rhobeg; su[j] = max(xu[j] - x[j], rhobeg); } } else if (su[j] <= rhobeg) { if (su[j] <= 0) { x[j] = xu[j]; sl[j] = -temp; su[j] = 0; } else { x[j] = xu[j] - rhobeg; sl[j] = min(xl[j] - x[j], -rhobeg); su[j] = rhobeg; } } } //bobyqb(x, xl, xu, calfun, rhobeg, rhoend, iprint, maxfun, xbase, xpt, fval, // xopt, gopt, hq, pq, bmat, zmat, sl, su, xnew, xalt, d, vlag); /* * 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. */ //int n = x.length, npt = xpt.length, np = n + 1; //double[] w = new double[3 * ndim]; double[] gnew = new double[n]; int nf = 0, kopt = 0, nptm = npt - np, nh = n * np / 2; { int[] temp = prelim(x, xl, xu, calfun, rhobeg, maxfun, xbase, xpt, fval, gopt, hq, pq, bmat, zmat, sl, su, isMinimize); nf = temp[0]; kopt = temp[1]; } double xoptsq = 0, fsave = fval[0], alpha = 0, cauchy = 0, adelt = 0, beta = 0, f = 0, denom = 0, den = 0, ratio = 0; boolean loop = true; for (int i = 0; i < n; i++) { double val = xopt[i] = xpt[kopt][i]; xoptsq += val * val; } if (nf < npt) loop = false; int kbase = 0, knew = -1; // Complete the settings that are required for the iterative procedure. double rho = rhobeg, delta = rho, diffa = 0, diffb = 0, diffc = 0; int nresc = nf, ntrits = 0, nfsav = nf, itest = 0; int label = 20; double dsq = 0, crvmin = 0, dnorm = 0, distsq = 0; while(loop) switch(label) { case 20: /* * Update GOPT if necessary before the first iteration and after each * call of RESCUE that makes a call of CALFUN. */ //printState("bobyqb-20-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); if (kopt != kbase) { int ih = 0; for (int j = 0; j < n; j++) for (int i = 0; i <= j; i++) { if (i < j) gopt[j] = gopt[j] + hq[ih] * xopt[i]; gopt[i] = gopt[i] + hq[ih] * xopt[j]; ih++; } if (nf > npt) { for (int k = 0; k < npt; k++) { double temp = 0; for (int j = 0; j < n; j++) temp += xpt[k][j] * xopt[j]; temp = pq[k] * temp; for (int i = 0; i < n; i++) gopt[i] = gopt[i] + temp * xpt[k][i]; } } } label = 60; break; case 60: //printState("bobyqb-60-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); //printState("bobyqb-60-start-checkstate\n"+ // "delta="+delta+", dnorm="+dnorm+ // "distsq="+distsq, 0, null, null, null, null, null, null, null, null); { // trsbox(double[][] xpt, double[] xopt, double[] gopt, double[] hq, double[] pq, // double[] sl, double[] su, double delta, double[] xnew, double[] d, double[] gnew, // double dsq, double crvmin) double[] temp = trsbox(xpt, xopt, gopt, hq, pq, sl, su, delta, xnew, d, gnew, dsq, crvmin); dsq = temp[0]; crvmin = temp[1]; } //printState("bobyqb-60-aftertrsbox-checkstate\n"+ // "delta="+delta+", dnorm="+dnorm+ // "distsq="+distsq, 0, null, null, null, null, null, null, null, null); dnorm = min(delta, sqrt(dsq)); if (dnorm < 0.5 * rho) { ntrits = -1; distsq = 100*rho*rho; if (nf <= nfsav + 2) { label = 650; break; } /* * 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. */ double errbig = max(max(diffa, diffb), diffc); if (crvmin > 0 && errbig > (0.125 * rho * rho) * crvmin) { label = 650; break; // goto 650 } /* * Due to the precision problem below (see my comments), I cannot verify * this particular branch numerically with that of the FORTRAN code. * However, test runs looks okay. -- RJ */ double bdtol = errbig / rho; for (int j = 0; j < n; j++) { double bdtest = bdtol; //if (xnew[j] == sl[j]) bdtest = gnew[j]; //if (xnew[j] == su[j]) bdtest = -gnew[j]; if (xnew[j] == sl[j]) bdtest = work1[j]; if (xnew[j] == su[j]) bdtest = -work1[j]; if (bdtest < bdtol) { int jp1 = j+1; double curv = hq[(jp1 + jp1*jp1) / 2 - 1]; for (int k = 0; k < npt; k++) { double val = xpt[k][j]; curv += pq[k] * val * val; } bdtest += 0.5 * curv * rho; if (bdtest < bdtol) { label = 650; break; // goto 650 } } } label = 680; break; // goto 680 } // end if (dnorm < 0.5 * rho) ntrits++; label = 90; break; case 90: /* * 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. */ //printState("bobyqb-90-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); if (dsq <= 1e-3 * xoptsq) { double fracsq = 0.25 * xoptsq, sumpq = 0; for (int k = 0; k < npt; k++) { sumpq += pq[k]; double sum = -0.5 * xoptsq; for (int i = 0; i < n; i++) sum += xpt[k][i] * xopt[i]; //w[npt+k] = sum; work2[k] = sum; double temp = fracsq - 0.5 * sum; for (int i = 0; i < n; i++) { //w[i] = bmat[k][i]; work1[i] = bmat[k][i]; vlag[i] = sum * xpt[k][i] + temp * xopt[i]; int ip = npt + i; for (int j = 0; j <= i; j++) //bmat[ip][j] += w[i] * vlag[j] + vlag[i] * w[j]; bmat[ip][j] = bmat[ip][j] + work1[i] * vlag[j] + vlag[i] * work1[j]; } } // Then the revisions of BMAT that depend on ZMAT are calculated. for (int jj = 0; jj < nptm; jj++) // L110 { double sumz = 0, sumw = 0; for (int k = 0; k < npt; k++) { sumz = sumz + zmat[k][jj]; //vlag[k] = w[npt+k] * zmat[k][jj]; vlag[k] = work2[k] * zmat[k][jj]; sumw = sumw + vlag[k]; } /* * TODO This is the place where minor precision difference may start to take toll. * Whether this makes any real difference in the long run or not is unknown. * However, it looks like it's okay. -- RJ's note * Fortran run: * JJ=1, SUMW= -5.10633202388532936E-014, SUMZ= -5.41788836017076392E-014 * JJ=2, SUMW= -3.49720252756924310E-015, SUMZ= -5.40123501480138657E-014 * JJ=3, SUMW= -1.11022302462515654E-016, SUMZ= 1.79856129989275360E-014 * JJ=4, SUMW= -1.92068583260152081E-014, SUMZ= -4.70734562441066373E-014 * JJ=5, SUMW= -2.10942374678779743E-015, SUMZ= 4.44089209850062616E-016 * * Java run: * jj=0, sumw= -4.445749324233361E-14, sumz= -3.1086244689504383E-15 * jj=1, sumw=-9.547918011776346E-15, sumz=5.773159728050814E-15 * jj=2, sumw=-7.105427357601002E-15, sumz=-5.773159728050814E-15 * jj=3, sumw=8.215650382226158E-15, sumz=3.352873534367973E-14 * jj=4, sumw=8.881784197001252E-15, sumz=8.215650382226158E-15 */ //System.out.println("jj="+jj+", sumw="+sumw+", sumz="+sumz); for (int j = 0; j < n; j++) { double sum = (fracsq * sumz - 0.5 * sumw) * xopt[j]; for (int k = 0; k < npt; k++) sum += vlag[k] * xpt[k][j]; //w[j] = sum; work1[j] = sum; for (int k = 0; k < npt; k++) bmat[k][j] = bmat[k][j] + sum * zmat[k][jj]; } for (int i = 0; i < n; i++) { int ip = i + npt; //double temp = w[i]; double temp = work1[i]; for (int j = 0; j <= i; j++) bmat[ip][j] = bmat[ip][j] + temp * work1[j]; //bmat[ip][j] += temp * w[j]; } } /* * The following instructions complete the shift, including the changes * to the second derivative parameters of the quadratic model. */ int ih = 0; // L150 for (int j = 0; j < n; j++) { //w[j] = -0.5 * sumpq * xopt[j]; work1[j] = -0.5 * sumpq * xopt[j]; for (int k = 0; k < npt; k++) { //w[j] += pq[k] * xpt[k][j]; work1[j] = work1[j] + pq[k] * xpt[k][j]; xpt[k][j] = xpt[k][j] - xopt[j]; } for (int i = 0; i <= j; i++) { //hq[ih] += w[i] * xopt[j] + xopt[i] * w[j]; hq[ih] = hq[ih] + work1[i] * xopt[j] + xopt[i] * work1[j]; bmat[npt+i][j] = bmat[npt+j][i]; ih++; } } for (int i = 0; i < n; i++) // L180 { double xopti = xopt[i]; xbase[i] = xbase[i] + xopti; xnew[i] = xnew[i] - xopti; sl[i] = sl[i] - xopti; su[i] = su[i] - xopti; xopt[i] = 0; } xoptsq = 0; } // end if (dsq <= 1e-3 * xoptsq) label = ntrits == 0 ? 210 : 230; // if (ntrits == 0) goto 210 else goto 230; break; case toRescue: /* * 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. */ // TODO Untested branch nfsav = nf; kbase = kopt; { int[] temp = rescue(xl, xu, calfun, maxfun, xbase, xpt, fval, xopt, gopt, hq, pq, bmat, zmat, sl, su, nf, delta, kopt, vlag, isMinimize); nf = temp[0]; kopt = temp[1]; } /* * 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 = 0; if (kopt != kbase) { for (int i = 0; i < n; i++) { double val = xopt[i] = xpt[kopt][i]; xoptsq += val * val; } } if (nf < 0) { nf = maxfun; // Already called maxfun times loop = false; label = 720; break; // goto 720 } nresc = nf; if (nfsav < nf) { nfsav = nf; label = 20; break; // goto 20 } if (ntrits > 0) { label = 60; break; // goto 60 } label = 210; // goto 210 break; case 210: /* * 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. */ //printState("bobyqb-210-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); { double[] temp = altmov(xpt, xopt, bmat, zmat, sl, su, kopt, knew, adelt, xnew, xalt); alpha = temp[0]; cauchy = temp[1]; } for (int i = 0; i < n; i++) d[i] = xnew[i] - xopt[i]; label = 230; break; case 230: /* * 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. */ //printState("bobyqb-230-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); for (int k = 0; k < npt; k++) { double suma = 0, sumb = 0, sum = 0; for (int j = 0; j < n; j++) { suma += xpt[k][j] * d[j]; sumb += xpt[k][j] * xopt[j]; sum += bmat[k][j] * d[j]; } //w[k] = suma * (0.5 * suma + sumb); work3[k] = suma * (0.5 * suma + sumb); vlag[k] = sum; //w[npt+k] = suma; work2[k] = suma; } beta = 0; for (int jj = 0; jj < nptm; jj++) { double sum = 0; for (int k = 0; k < npt; k++) sum += zmat[k][jj] * work3[k]; //sum += zmat[k][jj] * w[k]; beta -= sum * sum; for (int k = 0; k < npt; k++) vlag[k] = vlag[k] + sum * zmat[k][jj]; } dsq = 0; double bsum = 0, dx = 0; for (int j = 0; j < n; j++) { double val = d[j]; dsq += val * val; double sum = 0; for (int k = 0; k < npt; k++) sum += work3[k] * bmat[k][j]; //sum += w[k] * bmat[k][j]; bsum += sum * d[j]; int jp = npt + j; for (int i = 0; i < n; i++) sum += bmat[jp][i] * d[i]; vlag[jp] = sum; bsum += sum * d[j]; dx += d[j] * xopt[j]; } beta = dx * dx + dsq * (xoptsq + dx + dx + 0.5*dsq) + beta - bsum; vlag[kopt] = vlag[kopt] + 1; /* * 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) { double val = vlag[knew]; denom = val * val + alpha * beta; if (denom < cauchy && cauchy > 0) { for (int i = 0; i < n; i++) { xnew[i] = xalt[i]; d[i] = xnew[i] - xopt[i]; } cauchy = 0; label = 230; break; // goto 230 } if (denom <= 0.5 * val * val) { if (nf > nresc) { label = toRescue; break; // goto 190 } loop = false; label = 720; break; // goto 720 } } else { /* * 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. */ double delsq = delta * delta, scaden = 0, biglsq = 0; knew = -1; for (int k = 0; k < npt; k++) { if (k == kopt) continue; double hdiag = 0, val; for (int jj = 0; jj < nptm; jj++) { val = zmat[k][jj]; hdiag += val * val; } val = vlag[k]; den = beta * hdiag + val * val; distsq = 0; for (int j = 0; j < n; j++) { val = xpt[k][j] - xopt[j]; distsq += val * val; } val = distsq / delsq; double temp = max(1, val * val); if (temp * den > scaden) { scaden = temp * den; knew = k; denom = den; } val = vlag[k]; biglsq = max(biglsq, temp * val * val); } if (scaden <= 0.5 * biglsq) { if (nf > nresc) { label = toRescue; break; // goto 190 } loop = false; label = 720; break; // goto 720 } } // end if (ntrits == 0) label = 360; break; case 360: //printState("bobyqb-360-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); for (int i = 0; i < n; i++) { x[i] = min(max(xl[i], xbase[i]+xnew[i]), xu[i]); if (xnew[i] == sl[i]) x[i] = xl[i]; if (xnew[i] == su[i]) x[i] = xu[i]; } if (nf >= maxfun) { loop = false; label = 720; break; // goto 720 } nf++; f = isMinimize ? calfun.eval(x) : -calfun.eval(x); if (ntrits == -1) { fsave = f; loop = false; label = 720; break; // goto 720 } /* * Use the quadratic model to predict the change in F due to the step D, * and set DIFF to the error of this prediction. */ double fopt = fval[kopt], vquad = 0; int ih = 0; for (int j = 0; j < n; j++) { vquad += d[j] * gopt[j]; for (int i = 0; i <= j; i++) { double temp = d[i] * d[j]; if (i == j) temp = 0.5 * temp; vquad += hq[ih] * temp; ih++; } } for (int k = 0; k < npt; k++) { //double val = w[npt+k]; double val = work2[k]; vquad += 0.5 * pq[k] * val * val; } double diff = f - fopt - vquad; diffc = diffb; diffb = diffa; diffa = abs(diff); if (dnorm > rho) nfsav = nf; // Pick the next value of DELTA after a trust region step. if (ntrits > 0) { if (vquad >= 0) { loop = false; label = 720; break; // goto 720 } ratio = (f - fopt) / vquad; delta = ratio <= 0.1 ? min(0.5 * delta, dnorm) : max(0.5 * delta, (ratio <= 0.7 ? dnorm : 2 * dnorm)); if (delta <= 1.5 * rho) delta = rho; //printState("bobyqb-360-ifntrits>0-checkstate\n"+ // "delta="+delta+", dnorm="+dnorm+ // "distsq="+distsq, 0, null, null, null, null, null, null, null, null); // Recalculate KNEW and DENOM if the new F is less than FOPT. if (f < fopt) { int ksav = knew; double densav = denom, delsq = delta * delta, scaden = 0, biglsq = 0, val; knew = -1; for (int k = 0; k < npt; k++) { double hdiag = 0; for (int jj = 0; jj < nptm; jj++) { val = zmat[k][jj]; hdiag += val * val; } val = vlag[k]; den = beta * hdiag + val * val; distsq = 0; for (int j = 0; j < n; j++) { val = xpt[k][j] - xnew[j]; distsq += val * val; } val = distsq / delsq; double temp = max(1, val * val); if (temp * den > scaden) { scaden = temp * den; knew = k; denom = den; } val = vlag[k]; biglsq = max(biglsq, temp * val * val); } // end for (int k = 0; k < npt; k++) if (scaden <= 0.5 * biglsq) { knew = ksav; denom = densav; } } } // end if (ntrits > 0) /* * Update BMAT and ZMAT, so that the KNEW-th interpolation point can be * moved. Also update the second derivative terms of the model. */ //printState("bobyqb-360-before-update", nf, xbase, xopt, fval, vlag, bmat, zmat); update(bmat, zmat, vlag, beta, denom, knew); //printState("bobyqb-360-after-update", nf, xbase, xopt, fval, vlag, bmat, zmat); ih = 0; double pqold = pq[knew]; pq[knew] = 0; for (int i = 0; i < n; i++) { double temp = pqold * xpt[knew][i]; for (int j = 0; j <= i; j++) { hq[ih] = hq[ih] + temp * xpt[knew][j]; ih++; } } for (int jj = 0; jj < nptm; jj++) { double temp = diff * zmat[knew][jj]; for (int k = 0; k < npt; k++) pq[k] = pq[k] + temp * zmat[k][jj]; } /* * 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; for (int i = 0; i < n; i++) { xpt[knew][i] = xnew[i]; //w[i] = bmat[knew][i]; work1[i] = bmat[knew][i]; } for (int k = 0; k < npt; k++) { double suma = 0, sumb = 0; for (int jj = 0; jj < nptm; jj++) suma += zmat[knew][jj] * zmat[k][jj]; for (int j = 0; j < n; j++) sumb += xpt[k][j] * xopt[j]; double temp = suma * sumb; for (int i = 0; i < n; i++) work1[i] = work1[i] + temp * xpt[k][i]; //w[i] += temp * xpt[k][i]; } for (int i = 0; i < n; i++) gopt[i] = gopt[i] + diff * work1[i]; //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 = 0; ih = 0; for (int j = 0; j < n; j++) { xopt[j] = xnew[j]; double val = xopt[j]; xoptsq += val * val; for (int i = 0; i <= j; i++) { if (i < j) gopt[j] = gopt[j] + hq[ih] * d[i]; gopt[i] = gopt[i] + hq[ih] * d[j]; ih++; } } for (int k = 0; k < npt; k++) { double temp = 0; for (int j = 0; j < n; j++) temp += xpt[k][j] * d[j]; temp *= pq[k]; for (int i = 0; i < n; i++) gopt[i] = gopt[i] + temp * xpt[k][i]; } } // end if (f < fopt) /* * 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) { for (int k = 0; k < npt; k++) { vlag[k] = fval[k] - fval[kopt]; //w[k] = 0; work3[k] = 0; } for (int j = 0; j < nptm; j++) { double sum = 0; for (int k = 0; k < npt; k++) sum += zmat[k][j] * vlag[k]; for (int k = 0; k < npt; k++) work3[k] = work3[k] + sum * zmat[k][j]; //w[k] += sum * zmat[k][j]; } for (int k = 0; k < npt; k++) { double sum = 0; for (int j = 0; j < n; j++) sum += xpt[k][j] * xopt[j]; //w[k + npt] = w[k]; //w[k] *= sum; work2[k] = work3[k]; work3[k] = work3[k] * sum; } double gqsq = 0, gisq = 0; for (int i = 0; i < n; i++) { double sum = 0, val; for (int k = 0; k < npt; k++) sum += bmat[k][i] * vlag[k] + xpt[k][i] * work3[k]; //sum += bmat[k][i] * vlag[k] + xpt[k][i] * w[k]; if (xopt[i] == sl[i]) { val = min(0, gopt[i]); gqsq += val * val; val = min(0, sum); gisq += val * val; } else if (xopt[i] == su[i]) { val = max(0, gopt[i]); gqsq += val * val; val = max(0, sum); gisq += val * val; } else { val = gopt[i]; gqsq += val * val; gisq += sum * sum; } vlag[npt+i] = sum; } // end for (int i = 0; i < n; i++) /* * 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 < 10 * gisq) itest = 0; if (itest >= 3) { /* * Due to the precision problem below (see my comments), I cannot verify * this particular branch numerically with that of the FORTRAN code. * However, test runs looks okay. -- RJ */ int val = max(npt, nh); for (int i = 0; i < val; i++) { if (i < n) gopt[i] = vlag[npt + i]; //if (i < npt) pq[i] = w[npt + i]; if (i < npt) pq[i] = work2[i]; if (i < nh) hq[i] = 0; } itest = 0; } } // end if (ntrits > 0) /* * 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 || f <= fopt + 0.1 * vquad) { label = 60; break; // goto 60 } double v1 = 2 * delta, v2 = 10 * rho; distsq = max(v1 * v1, v2 * v2); label = 650; break; case 650: /* * Alternatively, find out if the interpolation points are close enough * to the best point so far. */ //printState("bobyqb-650-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); knew = -1; for (int k = 0; k < npt; k++) { double sum = 0; for (int j = 0; j < n; j++) { double val = xpt[k][j] - xopt[j]; sum += val * val; } if (sum > distsq) { knew = k; distsq = sum; } } /* * 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) { double dist = sqrt(distsq); if (ntrits == -1) { delta = min(0.1 * delta, 0.5 * dist); if (delta <= 1.5 * rho) delta = rho; //printState("bobyqb-650-ifknew>=0,ntrits==-1-checkstate\n"+ // "delta="+delta+", dnorm="+dnorm+ // "distsq="+distsq, 0, null, null, null, null, null, null, null, null); } ntrits = 0; adelt = max(min(0.1 * dist, delta), rho); dsq = adelt * adelt; label = 90; break; // goto 90; } if (ntrits != -1) { if (ratio > 0 || max(delta, dnorm) > 0) { label = 60; break; // goto 60 } } label = 680; break; case 680: /* * The calculations with the current value of RHO are complete. Pick the * next values of RHO and DELTA. */ //printState("bobyqb-680-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); if (rho > rhoend) { delta = 0.5 * rho; ratio = rho / rhoend; rho = ratio <= 16 ? rhoend : ratio <= 250 ? sqrt(ratio) * rhoend : 0.1 * rho; delta = max(delta, rho); // Some debug printout, if you wish /* System.out.println("Rho = " + rho + ", Number of function calls = "+nf); System.out.println("Optimum F = " + fval[kopt] + ", on X = "); for (int i = 0; i < n; i++) System.out.print((xbase[i] + xopt[i]) + " "); System.out.println(); //*/ ntrits = 0; nfsav = nf; label = 60; break; // goto 60 } if (ntrits == -1) { label = 360; break; // goto 360 } loop = false; label = 720; break; } // Label 720 //printState("bobyqb-720-start", nf, xbase, xopt, fval, vlag, d, bmat, zmat, xpt); if (fval[kopt] <= fsave) { for (int i = 0; i < n; i++) { x[i] = min(max(xl[i], xbase[i] + xopt[i]), xu[i]); if (xopt[i] == sl[i]) x[i] = xl[i]; if (xopt[i] == su[i]) x[i] = xu[i]; } f=fval[kopt]; } //return f; return new OptimizationResult(x, f, nf, isMinimize); } /** *

	 * 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).
	 * 
* @param x * @param xl * @param xu * @param calfun * @param rhobeg * @param rhoend * @param iprint * @param maxfun * @param xbase * @param xpt * @param fval * @param xopt * @param gopt * @param hq * @param pq * @param bmat * @param zmat * @param sl * @param su * @param xnew * @param xalt * @param d * @param vlag */ /* static final double bobyqb(double[] x, double[] xl, double[] xu, IMultivariableFunction calfun, double rhobeg, double rhoend, int iprint, int maxfun, double[] xbase, double[][] xpt, double[] fval, double[] xopt, double[] gopt, double[] hq, double[] pq, double[][] bmat, double[][] zmat, double[] sl, double[] su, double[] xnew, double[] xalt, double[] d, double[] vlag) { } //*/ /** *
	 * 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 maintined 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.
	 * 
* @param x * @param xl * @param xu * @param calfun * @param rhobeg * @param maxfun * @param xbase * @param xpt * @param fval * @param gopt * @param hq * @param pq * @param bmat * @param zmat * @param sl * @param su * @param isMinimize true if goal is to find the global minimum * @return integer array of two elements: nf and kopt */ private static final int[] prelim(double[] x, double[] xl, double[] xu, MultivariableFunction calfun, double rhobeg, int maxfun, double[] xbase, double[][] xpt, double[] fval, double[] gopt, double[] hq, double[] pq, double[][] bmat, double[][] zmat, double[] sl, double[] su, boolean isMinimize) { int nf, kopt = 0; int n = x.length, ndim = bmat.length, npt = zmat.length, mult = isMinimize ? 1 : -1; double rhosq = rhobeg * rhobeg, recip = 1/rhosq, fbeg = 0; int np = n + 1, nnpdiv2 = n*np/2, nptminnp = npt - np; /* * Set XBASE to the initial vector of variables, and set the initial * elements of XPT, BMAT, HQ, PQ and ZMAT to zero. */ for (int j = 0; j < n; j++) { xbase[j] = x[j]; for (int k = 0; k < npt; k++) xpt[k][j] = 0; for (int i = 0; i < ndim; i++) bmat[i][j] = 0; } for (int ih = 0; ih < nnpdiv2; ih++) hq[ih] = 0; for (int k = 0; k < npt; k++) { pq[k] = 0; for (int j = 0; j < nptminnp; j++) zmat[k][j] = 0; } /* * 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; int ipt = 0, jpt = 0; double stepa = 0, stepb = 0; do // L50 { int nfm = nf, nfx = nf - n; nf++; if (nfm <= 2 * n) { if (nfm >= 1 && nfm <= n) { stepa = su[nfm-1] == 0 ? -rhobeg : rhobeg; xpt[nf-1][nfm-1] = stepa; } else if (nfm > n) { stepa = xpt[nf-n-1][nfx-1]; stepb = -rhobeg; if (sl[nfx-1] == 0) stepb = min(2*rhobeg, su[nfx-1]); if (su[nfx-1] == 0) stepb = max(-2*rhobeg, sl[nfx-1]); xpt[nf-1][nfx-1] = stepb; } } else { int itemp = (nfm - np) / n; jpt = nfm - itemp * n - n; ipt = jpt + itemp; if (ipt > n) { itemp = jpt; jpt = ipt - n; ipt = itemp; } xpt[nf-1][ipt-1] = xpt[ipt][ipt-1]; xpt[nf-1][jpt-1] = xpt[jpt][jpt-1]; } /* * Calculate the next value of F. The least function value so far and * its index are required. */ for (int j = 0; j < n; j++) { x[j] = min(max(xl[j], xbase[j] + xpt[nf-1][j]), xu[j]); if (xpt[nf-1][j] == sl[j]) x[j] = xl[j]; if (xpt[nf-1][j] == su[j]) x[j] = xu[j]; } double f = calfun.eval(x) * mult; fval[nf-1] = f; if (nf == 1) { fbeg = f; kopt = 1; } else if (f < fval[kopt-1]) 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. */ int ih; if (nf <= 2*n+1) { if (nf >= 2 && nf <= n+1) { gopt[nfm-1] = (f - fbeg) / stepa; if (npt < nf + n) { bmat[0][nfm-1] = -1 / stepa; bmat[nf-1][nfm-1] = 1/stepa; bmat[npt+nfm-1][nfm-1] = -0.5 * rhosq; } } else if (nf >= n+2) { ih = (nfx * (nfx + 1)) / 2; double temp = (f - fbeg) / stepb; double diff = stepb - stepa; hq[ih - 1] = 2 * (temp - gopt[nfx - 1]) / diff; gopt[nfx - 1] = (gopt[nfx - 1] * stepb - temp * stepa) / diff; if (stepa * stepb < 0 && f < fval[nf - n - 1]) { fval[nf - 1] = fval[nf - n - 1]; fval[nf - n - 1] = f; if (kopt == nf) kopt = nf - n; xpt[nf-n-1][nfx - 1] = stepb; xpt[nf - 1][nfx - 1] = stepa; } bmat[0][nfx - 1] = -(stepa + stepb) / (stepa * stepb); bmat[nf - 1][nfx - 1] = -0.5/xpt[nf - n - 1][nfx - 1]; bmat[nf - n - 1][nfx - 1] = -bmat[0][nfx - 1] - bmat[nf - 1][nfx - 1]; zmat[0][nfx - 1] = sqrt(2) / (stepa * stepb); zmat[nf - 1][nfx - 1] = sqrt(0.5) / rhosq; zmat[nf - n - 1][nfx - 1] = -zmat[0][nfx - 1] - zmat[nf - 1][nfx - 1]; } // end else if (nf >= n+2) } else { /* * Set the off-diagonal second derivatives of the Lagrange functions and * the initial quadratic model. */ ih = (ipt * (ipt - 1)) / 2 + jpt; zmat[0][nfx - 1] = zmat[nf - 1][nfx - 1] = recip; zmat[ipt][nfx - 1] = zmat[jpt][nfx - 1] = -recip; double temp = xpt[nf - 1][ipt - 1] * xpt[nf - 1][jpt - 1]; hq[ih - 1] = (fbeg-fval[ipt]-fval[jpt] + f) / temp; } } while (nf < npt && nf < maxfun); // end do-while loop L50 return new int[] {nf, kopt-1}; } /** *
	 * 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.
	 * 
* @param bmat * @param zmat * @param vlag * @param beta * @param denom * @param knew */ private static final void update(double[][] bmat, double[][] zmat, double[] vlag, double beta, double denom, int knew) { int ndim = bmat.length, n = bmat[0].length, npt = zmat.length; int nptm = npt - n - 1; double ztest = 0, w[] = new double[ndim]; for (int k = 0; k < npt; k++) for (int j = 0; j < nptm; j++) ztest = max(ztest, abs(zmat[k][j])); ztest *= 1e-20; // Apply the rotations that put zeros in the KNEW-th row of ZMAT. // int jl = 1 for (int j = 1; j < nptm; j++) { if (abs(zmat[knew][j]) > ztest) { double tempa = zmat[knew][0], tempb = zmat[knew][j], temp = sqrt(tempa * tempa + tempb * tempb); tempa = tempa / temp; tempb = tempb / temp; for (int i = 0; i < npt; i++) { temp = tempa * zmat[i][0] + tempb * zmat[i][j]; zmat[i][j] = tempa * zmat[i][j] - tempb * zmat[i][0]; zmat[i][0] = temp; } } zmat[knew][j] = 0; } // Put the first NPT components of the KNEW-th column of HLAG into W, // and calculate the parameters of the updating formula. for (int i = 0; i < npt; i++) w[i] = zmat[knew][0] * zmat[i][0]; double alpha = w[knew], tau = vlag[knew], // Complete the updating of ZMAT. temp = sqrt(denom), tempb = zmat[knew][0] / temp, tempa = tau / temp; vlag[knew] = vlag[knew] - 1; for (int i = 0; i < npt; i++) zmat[i][0] = tempa * zmat[i][0] - tempb * vlag[i]; // Finally, update the matrix BMAT. //System.out.println("alpha=" + alpha + ", beta=" + beta + ", tau=" + tau + ", denom=" + denom); //System.out.println(toString(w)); for (int j = 0; j < n; j++) { int jp = npt + j; w[jp] = bmat[knew][j]; tempa = (alpha * vlag[jp] - tau * w[jp]) / denom; tempb = (-beta * w[jp] - tau * vlag[jp]) / denom; //System.out.println("tempa=" + tempa + ", tempb=" + tempb); //System.out.println("j=" + j + ": " + toString(w)); for (int i = 0; i <= jp; i++) { // NOTE: DO NOT change the expression to bmat[i][j] += ... // This will cause difference in 18th digit under zero but will be carried out in thousands iteration! bmat[i][j] = bmat[i][j] + tempa * vlag[i] + tempb * w[i]; if (i >= npt) bmat[jp][i-npt]=bmat[i][j]; } } } /** *
	 * The arguments N, NPT, XL, XU, 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
	 *   int 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.
	 * 
* @param xl * @param xu * @param calfun * @param maxfun * @param xbase * @param xpt * @param fval * @param xopt * @param gopt * @param hq * @param pq * @param bmat * @param zmat * @param sl * @param su * @param nf * @param delta * @param kopt * @param vlag * @param isMinimize true if the goal is to find the global minimum * @return integer array of two elements: nf and kopt */ private static final int[] rescue(double[] xl, double[] xu, MultivariableFunction calfun, int maxfun, double[] xbase, double[][] xpt, double[] fval, double[] xopt, double[] gopt, double[] hq, double[] pq, double[][] bmat, double[][] zmat, double[] sl, double[] su, int nf, double delta, int kopt, double[] vlag, boolean isMinimize) { // TODO This entire subroutine is untested int n = xl.length, ndim = bmat.length, npt = xpt.length, np = n + 1, mult = isMinimize ? 1 : -1, nptm = npt - np; assert(npt == zmat.length); double sfrac = 0.5 / np, beta = 0, denom = 0, ptsaux[][] = new double[2][n], ptsid[] = new double[npt], w[] = new double[ndim + npt]; /* * 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. */ double sumpq = 0, winc = 0; for (int k = 0; k < npt; k++) { double distsq = 0; for (int j = 0; j < n; j++) { xpt[k][j] = xpt[k][j] - xopt[j]; double temp = xpt[k][j]; distsq += temp * temp; } sumpq += pq[k]; w[ndim + k] = distsq; winc = max(winc, distsq); for (int j = 0; j < nptm; j++) zmat[k][j] = 0; } /* * Update HQ so that HQ and PQ define the second derivatives of the model * after XBASE has been shifted to the trust region centre. */ int ih = 0; for (int j = 0; j < n; j++) { w[j] = 0.5 * sumpq * xopt[j]; for(int k = 0; k < npt; k++) w[j] = w[j] + pq[k]*xpt[k][j]; for (int i = 0; i <= j; i++) { hq[ih] = hq[ih] + w[i] * xopt[j] + w[j] * xopt[i]; ih++; } } /* * Shift XBASE, SL, SU and XOPT. Set the elements of BMAT to zero, and * also set the elements of PTSAUX. */ for (int j = 0; j < n; j++) { xbase[j] = xbase[j] + xopt[j]; sl[j] = sl[j] - xopt[j]; su[j] = su[j] - xopt[j]; xopt[j] = 0; ptsaux[0][j] = min(delta, su[j]); ptsaux[1][j] = max(-delta, sl[j]); if (ptsaux[0][j] + ptsaux[1][j] < 0) { double temp = ptsaux[0][j]; ptsaux[0][j] = ptsaux[1][j]; ptsaux[1][j] = temp; } if (abs(ptsaux[1][j]) < 0.5 * abs(ptsaux[0][j])) ptsaux[1][j] = 0.5 * ptsaux[0][j]; for (int i = 0; i < ndim; i++) bmat[i][j] = 0; } double 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[0] = sfrac; for (int j = 0; j < n; j++) { int jp = j+1, jpn = jp+n; double ptsaux1j = ptsaux[0][j], ptsaux2j = ptsaux[1][j]; ptsid[jp] = j + sfrac; if (jpn < npt) // if (jpn + 1 <= npt) { ptsid[jpn] = j * 1.0 / np + sfrac; double temp = 1.0 / (ptsaux1j - ptsaux2j); bmat[jp][j] = -temp + 1.0 / ptsaux1j; bmat[jpn][j] = temp + 1.0 / ptsaux2j; bmat[0][j] = -bmat[jp][j] - bmat[jpn][j]; zmat[0][j] = sqrt(2.0) / abs(ptsaux1j * ptsaux2j); zmat[jp][j] = zmat[0][j] * ptsaux2j * temp; zmat[jpn][j] = -zmat[0][j] * ptsaux1j * temp; } else { bmat[0][j] = -1.0 / ptsaux1j; bmat[jp][j] = 1.0 / ptsaux1j; bmat[j+npt][j] = -0.5 * ptsaux1j * ptsaux1j; } } // Set any remaining identifiers with their nonzero elements of ZMAT. if (npt >= n + np) { for (int k = 2 * np - 1; k < npt; k++) // TODO beware of ptsid { int iw = (int) ((k - np - 0.5) / n), ip = k - np - iw * n, iq = ip + iw, knpm1 = k - np; if (iq >= n) iq -= n; ptsid[k] = ip + iq * 1.0 / np + sfrac; double temp = 1.0 / (ptsaux[0][ip] * ptsaux[1][iq]); zmat[0][knpm1] = temp; zmat[ip][knpm1] = -temp; zmat[iq][knpm1] = -temp; zmat[k][knpm1] = temp; } } int nrem = npt, kold = 0, knew = kopt; boolean gotoL120 = false; // Reorder the provisional points in the way that exchanges PTSID(KOLD) with PTSID(KNEW). for(;;) { // L80: if (!gotoL120) { for (int j = 0; j < n; j++) { double temp = bmat[kold][j]; bmat[kold][j] = bmat[knew][j]; bmat[knew][j] = temp; } for (int j = 0; j < nptm; j++) { double temp = zmat[kold][j]; zmat[kold][j] = zmat[knew][j]; zmat[knew][j] = temp; } ptsid[kold] = ptsid[knew]; ptsid[knew] = 0; w[ndim + knew] = 0; nrem--; if (knew != kopt) { double 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(double[][] bmat, double[][] zmat, double[] vlag, double beta, double denom, int knew) update(bmat, zmat, vlag, beta, denom, knew); if (nrem == 0) // goto L350 // TODO return new int[] {nf, kopt}; for (int k = 0; k < npt; k++) w[ndim+k] = abs(w[ndim+k]); } } /* * 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 gotoL120 = false; double dsqmin = 0; for (int k = 0; k < npt; k++) { double wndimk = w[ndim + k]; if ((wndimk > 0) && (dsqmin == 0 || wndimk < dsqmin)) { knew = k; dsqmin = wndimk; } } if (dsqmin == 0) break; // goto L260 // Form the W-vector of the chosen original interpolation point. for (int j = 0; j < n; j++) w[npt + j] = xpt[knew][j]; for (int k = 0; k < npt; k++) { double sum = 0; if (k != kopt) { if (ptsid[k] == 0) { for (int j = 0; j < n; j++) sum += w[npt+j] * xpt[k][j]; } else { int ip = (int) ptsid[k]; if (ip > 0) sum = w[npt + ip] * ptsaux[0][ip]; int iq = (int) (np * ptsid[k] - (ip * np)); if (iq > 0) { int iw = 0; if (ip == 0) iw = 1; sum += w[npt+iq] * ptsaux[iw][iq]; } } } w[k] = 0.5 * 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. for (int k = 0; k < npt; k++) { double sum = 0; for (int j = 0; j < n; j++) sum+= bmat[k][j] * w[npt + j]; vlag[k] = sum; } beta = 0; for (int j = 0; j < nptm; j++) { double sum = 0; for (int k = 0; k < npt; k++) sum += zmat[k][j] * w[k]; beta -= sum * sum; for (int k = 0; k < npt; k++) vlag[k] += sum * zmat[k][j]; } double bsum = 0, distsq =0; for (int j = 0; j < n; j++) { double sum = 0; for (int k = 0; k < npt; k++) sum += bmat[k][j] * w[k]; int jp = j + npt; bsum += sum * w[jp]; for (int ip = npt; ip < ndim; ip++) sum += bmat[ip][j] * w[ip]; bsum += sum * w[jp]; vlag[jp] = sum; sum = xpt[knew][j]; distsq += sum * sum; } beta = 0.5 * distsq * distsq + beta - bsum; vlag[kopt] = vlag[kopt] + 1; /* * 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 = 0; double vlmxsq = 0; for (int k = 0; k < npt; k++) { double vlagksq = vlag[k]; vlagksq *= vlagksq; if (ptsid[k] != 0) { double hdiag = 0; for (int j = 0; j < nptm; j++) hdiag += zmat[k][j] * zmat[k][j]; double den = beta * hdiag + vlagksq; if (den > denom) { kold = k; denom = den; } } vlmxsq = max(vlmxsq, vlagksq); } if (denom <= 0.01 * vlmxsq) { w[ndim + knew] = -w[ndim+knew] - winc; gotoL120 = true; // 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 for (int kpt = 0; kpt < npt; kpt++) { double xp = 0, xq = 0; if (ptsid[kpt] < 0) continue; // goto 340 if (nf >= maxfun) { nf = -1; break; // goto 350 } ih = 0; for (int j = 0; j < n; j++) { w[j] = xpt[kpt][j]; xpt[kpt][j] = 0; double temp = pq[kpt] * w[j]; for (int i = 0; i <= j; i++) { hq[ih] = hq[ih] + temp * w[i]; ih++; } } pq[kpt] = 0; int ip = (int) ptsid[kpt]; int iq = (int) (np * ptsid[kpt] - (ip * np)); if (ip >= 0) { xp = ptsaux[0][ip]; xpt[kpt][ip] = xp; } if (iq >= 0) { xq = ptsaux[ip == 0 ? 1 : 0][iq]; xpt[kpt][iq] = xq; } // Set VQUAD to the value of the current model at the new point. double vquad = fbase; int ihp = 0, ihq = 0; if (ip >= 0) { ihp = (ip + ip * ip) / 2; vquad += xp * (gopt[ip] + 0.5 * xp * hq[ihp-1]); } if (iq >= 0) { ihq = (iq + iq * iq) / 2; vquad += xq * (gopt[iq] + 0.5 * xq * hq[ihq-1]); if (ip >= 0) { int iw = max(ihp, ihq) - abs(ip - iq); vquad += xp * xq * hq[iw-1]; } } for (int k = 0; k < npt; k++) { double temp = 0; if (ip >= 0) temp += xp * xpt[k][ip]; if (iq >= 0) temp += xq * xpt[k][iq]; vquad += 0.5 * 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. */ for (int i = 0; i < n; i++) { w[i] = min(max(xl[i], xbase[i] + xpt[kpt][i]), xu[i]); if (xpt[kpt][i] == sl[i]) w[i] = xl[i]; if (xpt[kpt][i] == su[i]) w[i] = xu[i]; } nf++; double f = calfun.eval(w) * mult; fval[kpt] = f; if (f < fval[kopt]) kopt = kpt; double diff = f - vquad; /* * Update the quadratic model. The RETURN from the subroutine occurs when * all the new interpolation points are included in the model. */ for (int i = 0; i < n; i++) gopt[i] = gopt[i] + diff * bmat[kpt][i]; for (int k = 0; k < npt; k++) { double sum = 0; for (int j = 0; j < nptm; j++) sum += zmat[k][j] * zmat[kpt][j]; double temp = diff * sum; if (ptsid[k] < 0) pq[k] = pq[k] + temp; else { ip = (int) ptsid[k]; iq = (int) (np * ptsid[k] - (ip * np)); ihq = (iq * iq + iq) / 2; if (ip == 0) { double val = ptsaux[1][iq]; hq[ihq] = hq[ihq] + temp * val * val; } else { ihp = (ip * ip + ip) / 2; double val = ptsaux[0][ip]; hq[ihp] = hq[ihp] + temp * val * val; if (iq >= 0) { double val2 = ptsaux[0][iq]; hq[ihq] = hq[ihq] + temp * val2 * val2; int iw = max(ihp, ihq) - abs(iq - ip); hq[iw] = hq[iw] + temp * val * val2; } } } } ptsid[kpt] = -1; } // For L260 return new int[] {nf, kopt}; } /** *
	 * 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.
	 * 
* @param xpt * @param xopt * @param bmat * @param zmat * @param sl * @param su * @param kopt * @param knew * @param adelt * @param xnew * @param xalt * @return an array of two elements: alpha, cauchy */ private static final double[] altmov(double[][] xpt, double[] xopt, double[][] bmat, double[][] zmat, double[] sl, double[] su, int kopt, int knew, double adelt, double[] xnew, double[] xalt) { int n = xpt[0].length, // ndim = bmat.length, npt = xpt.length; final double __const = 1 + sqrt(2.0); double alpha, cauchy; double[] glag = new double[n], hcol = new double[npt], w = new double[2*n]; /* * Set the first NPT components of W to the leading elements of the * KNEW-th column of the H matrix. */ for (int k = 0; k < npt; k++) hcol[k] = 0; for (int j = 0; j < npt - n - 1; j++) { double temp = zmat[knew][j]; for (int k = 0; k < npt; k++) hcol[k] = hcol[k] + temp * zmat[k][j]; } alpha = hcol[knew]; double ha = 0.5 * alpha; // Calculate the gradient of the KNEW-th Lagrange function at XOPT. for (int i = 0; i < n; i++) glag[i] = bmat[knew][i]; for (int k = 0; k < npt; k++) { double temp = 0; for (int j = 0; j < n; j++) temp += xpt[k][j] * xopt[j]; temp *= hcol[k]; for (int i = 0; i < n; i++) glag[i] = glag[i] + temp * xpt[k][i]; } /* * 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. */ double presav = 0, stpsav = 0; int ksav = 0, ibdsav = 0; for (int k = 0; k < npt; k++) { if (k == kopt) continue; double dderiv = 0, distsq = 0; for (int i = 0; i < n; i++) { double temp = xpt[k][i] - xopt[i]; dderiv += glag[i] * temp; distsq += temp * temp; } double subd = adelt / sqrt(distsq), slbd = -subd, sumin = min(1, subd); int ilbd = 0, iubd = 0; // Revise SLBD and SUBD if necessary because of the bounds in SL and SU. for (int i = 0; i < n; i++) { double temp = xpt[k][i] - xopt[i]; if (temp > 0) { if (slbd * temp < sl[i] - xopt[i]) { slbd = (sl[i] - xopt[i]) / temp; ilbd = -i-1; } if (subd * temp > su[i] - xopt[i]) { subd = max(sumin, (su[i] - xopt[i])/temp); iubd = i+1; } } else if (temp < 0) { if (slbd * temp > su[i] - xopt[i]) { slbd = (su[i] - xopt[i])/temp; ilbd = i+1; } if (subd * temp < sl[i] - xopt[i]) { subd = max(sumin, (sl[i] - xopt[i])/temp); iubd = -i-1; } } } /* * 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. */ double diff, step = slbd, vlag; int isbd = ilbd; if (k == knew) { diff = dderiv - 1; vlag = slbd * (dderiv - slbd * diff); double temp = subd * (dderiv - subd * diff); if (abs(temp) > abs(vlag)) { step = subd; vlag = temp; isbd = iubd; } double tempd = 0.5 * dderiv, tempa = tempd - diff * slbd, tempb = tempd - diff * subd; if (tempa * tempb < 0) { temp = tempd * tempd / diff; if (abs(temp) > abs(vlag)) { step = tempd/diff; vlag = temp; isbd = 0; } } } else { // Search along each of the other lines through XOPT and another point. vlag = slbd * (1.0 - slbd); double temp = subd * (1.0 - subd); if (abs(temp) > abs(vlag)) { step = subd; vlag = temp; isbd = iubd; } if (subd > 0.5) { if (abs(vlag) < 0.25) { step = 0.5; vlag = 0.25; isbd = 0; } } vlag *= dderiv; } // Calculate PREDSQ for the current line search and maintain PRESAV. double temp = step * (1.0 - step) * distsq; double predsq = vlag * vlag * (vlag * vlag + ha * temp * temp); if (predsq > presav) { presav = predsq; ksav = k; stpsav = step; ibdsav = isbd; } } // Construct XNEW in a way that satisfies the bound constraints exactly. for (int i = 0; i < n; i++) { double temp = xopt[i] + stpsav * (xpt[ksav][i] - xopt[i]); xnew[i] = max(sl[i], min(su[i], temp)); } if (ibdsav < 0) xnew[-ibdsav-1] = sl[-ibdsav-1]; else if (ibdsav > 0) xnew[ibdsav-1] = su[ibdsav-1]; /* * 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. */ double bigstp = adelt + adelt; int iflag = 0; double csave = 0, temp; // L100 while(true) { double wfixsq = 0, ggfree = 0; for (int i = 0; i < n; i++) { w[i] = 0; double glagi = glag[i]; if (min(xopt[i] - sl[i], glagi) > 0 || max(xopt[i] - su[i], glagi) < 0) { w[i] = bigstp; ggfree += glagi * glagi; } } if (ggfree == 0) return new double[] {alpha, 0}; // goto 200; double step = 0; // Investigate whether more components of W can be fixed. while ((temp = adelt * adelt - wfixsq) > 0) // L120 { double wsqsav = wfixsq; step = sqrt(temp / ggfree); ggfree = 0; for (int i = 0; i < n; i++) { double wi; if (w[i] == bigstp) { temp = xopt[i] - step * glag[i]; if (temp <= sl[i]) { wi = w[i] = sl[i] - xopt[i]; wfixsq += wi * wi; } else if (temp >= su[i]) { wi = w[i] = su[i] - xopt[i]; wfixsq += wi * wi; } else { wi = glag[i]; ggfree += wi * wi; } } } if (!(wfixsq > wsqsav && ggfree > 0)) break; } /* * Set the remaining free components of W and all components of XALT, * except that W may be scaled later. */ double gw = 0; for (int i = 0; i < n; i++) { double glagi = glag[i]; if (w[i] == bigstp) { w[i] = -step * glagi; xalt[i] = max(sl[i], min(su[i], xopt[i] + w[i])); } else if (w[i] == 0) xalt[i] = xopt[i]; else if (glagi > 0) xalt[i] = sl[i]; else xalt[i] = su[i]; gw += glagi * 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. */ double curv = 0; for (int k = 0; k < npt; k++) { temp = 0; for (int j = 0; j < n; j++) temp += xpt[k][j] * w[j]; curv += hcol[k] * temp * temp; } if (iflag == 1) curv = -curv; if (curv > -gw && curv < -gw * __const) { double scale = -gw / curv; for (int i = 0; i < n; i++) { temp = xopt[i] + scale * w[i]; xalt[i] = max(sl[i], min(su[i], temp)); } cauchy = 0.5 * gw * scale; cauchy *= cauchy; } else { cauchy = gw + 0.5 * curv; cauchy *= cauchy; } /* * 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) { for (int i = 0; i < n; i++) { glag[i] = -glag[i]; w[n+i] = xalt[i]; } csave = cauchy; iflag = 1; // goto 100; continue; } break; } // end for (;;) L100 if (csave > cauchy) { for (int i = 0; i < n; i++) xalt[i] = w[n+i]; cauchy = csave; } return new double[] {alpha, cauchy}; } /** *
	 * 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.
	 * 
* @param xpt * @param xopt * @param gopt * @param hq * @param pq * @param sl * @param su * @param delta * @param xnew * @param d * @param gnew * @param dsq * @param crvmin * @return an array of two elements: dsq, crvmin */ private static final double[] trsbox(double[][] xpt, double[] xopt, double[] gopt, double[] hq, double[] pq, double[] sl, double[] su, double delta, double[] xnew, double[] d, double[] gnew, double dsq, double crvmin) { /* * 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. */ //printState("trsbox-00-start-hq="+toString(hq), 0, null, xopt, null, null, d, null, null, xpt); int n = xpt[0].length, npt = xpt.length, iterc = 0, nact = 0; int[] xbdi = new int[n]; double[] s = new double[n], hs = new double[n], hred = new double[n]; //double sqstp = 0; // seems unused for (int i = 0; i < n; i++) { xbdi[i] = 0; if (xopt[i] <= sl[i]) { if (gopt[i] >= 0) xbdi[i] = -1; } else if (xopt[i] >= su[i]) { if (gopt[i] <= 0) xbdi[i] = 1; } if (xbdi[i] != 0) nact++; d[i] = 0; gnew[i] = gopt[i]; } //printState("trsbox-00-after-firstloop", 0, null, xopt, null, null, d, null, null, xpt); double delsq = delta * delta, qred = 0, beta = 0, gredsq = 0, dredsq = 0, dredg = 0, ggsav = 0, stepsq = 0, sredg = 0, redsav = 0, angbd = 1; int itermax = 0, itcsav = 0, label = 30, xsav = 0, iact = 0; crvmin = -1; boolean loop = true; // L30 for(;loop;) { switch (label) { case 30: //printState("trsbox-30-start", 0, null, xopt, null, null, d, null, null, xpt); stepsq = 0; for (int i = 0; i < n; i++) { if (xbdi[i] != 0) s[i] = 0; else if (beta == 0) s[i] = -gnew[i]; else s[i] = beta * s[i] - gnew[i]; double val = s[i]; stepsq += val * val; } //printState("trsbox-30-checkstate:\n" + // "stepsq="+stepsq+", beta="+beta+ // ", gredsq="+gredsq, 0, null, null, null, null, null, null, null, null); if (stepsq == 0) { //label = 190; // goto 190; loop = false; break; } if (beta == 0) { gredsq = stepsq; itermax = iterc + n - nact; } if (gredsq * delsq <= qred * 1e-4 * qred) { //label = 190; // goto 190; loop = false; break; } /* * 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. */ label = 210; break; // goto 210; case 50: // L50 //printState("trsbox-50-start", 0, null, xopt, null, null, d, null, null, xpt); double resid = delsq, ds = 0, shs = 0; for (int i = 0; i < n; i ++) if (xbdi[i] == 0) { double di = d[i], si = s[i]; resid -= di * di; ds += si * di; shs += si * hs[i]; } if (resid <= 0) { // goto 90 crvmin = 0; label = 100; break; } double temp = sqrt(stepsq * resid + ds * ds), blen = ds < 0 ? (temp - ds) / stepsq : resid / (temp + ds), stplen = shs > 0 ? min(blen, gredsq / shs) : blen; //printState("trsbox-50-checkstate:\n" + // "stepsq="+stepsq+", resid="+resid+", " + // "ds="+ds+", gredsq="+gredsq+ ", blen="+blen, 0, null, null, null, null, null, null, null, null); /* * Reduce STPLEN if necessary in order to preserve the simple bounds, * letting IACT be the index of the new constrained variable. */ iact = -1; for (int i = 0; i < n; i++) if (s[i] != 0) { double xsum = xopt[i] + d[i]; temp = s[i] > 0 ? (su[i] - xsum) / s[i] : (sl[i] - xsum) / s[i]; if (temp < stplen) { stplen = temp; iact = i; } } // Update CRVMIN, GNEW and D. Set SDEC to the decrease that occurs in Q. double sdec = 0; if (stplen > 0) { //printState("trsbox-50-ifstplen>0", 0, null, xopt, null, null, d, null, null, xpt); iterc++; temp = shs/stepsq; if (iact == -1 && temp > 0) { crvmin = min(crvmin, temp); if (crvmin == -1) crvmin = temp; } ggsav = gredsq; gredsq = 0; //printState("trsbox-50-ifstplen>0-beforeloop-stplen="+stplen, 0, null, xopt, null, null, d, null, null, xpt); //printState("trsbox-50-ifstplen>0-beforeloop-gnew="+toString(gnew), 0, null, null, null, null, null, null, null, null); //printState("trsbox-50-ifstplen>0-beforeloop-hs="+toString(hs), 0, null, null, null, null, null, null, null, null); for (int i = 0; i < n; i++) { gnew[i] = gnew[i] + stplen * hs[i]; if (xbdi[i] == 0) { double val = gnew[i]; gredsq += val * val; } d[i] = d[i] + stplen * s[i]; } //printState("trsbox-50-ifstplen>0-afterloop-gredsq="+gredsq, 0, null, xopt, null, null, d, null, null, xpt); sdec = max(stplen*(ggsav - 0.5 * stplen * shs), 0); qred += sdec; } // Restart the conjugate gradient method if it has hit a new bound. if (iact >= 0) { nact++; xbdi[iact] = 1; if (s[iact] < 0) xbdi[iact] = -1; double val = d[iact]; delsq -= val * val; if (delsq <= 0) { crvmin = 0; label = 100; // goto 90 break; } // goto 20; beta = 0; label = 30; break; } /* * If STPLEN is less than BLEN, then either apply another conjugate * gradient iteration or RETURN. */ if (stplen < blen) { if (iterc == itermax || sdec <= 0.01 * qred) { // label = 190; // goto 190 loop = false; break; } beta = gredsq / ggsav; label = 30; // goto 30; break; } crvmin = 0; label = 100; break; case 100: // L100 //printState("trsbox-100-start", 0, null, xopt, null, null, d, null, null, xpt); /* * 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. */ if (nact >= n - 1) { // label = 190; // goto 190 loop = false; break; } dredsq = dredg = gredsq = 0; for (int i = 0; i < n; i++) { if (xbdi[i] == 0) { double di = d[i], gi = gnew[i]; dredsq += di * di; dredg += di * gi; gredsq += gi * gi; s[i] = di; } else s[i] = 0; } //printState("trsbox-100-checkstate:\n" + // "stepsq="+stepsq+", gredsq="+gredsq, 0, null, null, null, null, null, null, null, null); itcsav = iterc; label = 210; // goto 210; break; case 120: //printState("trsbox-120-start", 0, null, xopt, null, null, d, null, null, xpt); /* * Let the search direction S be a linear combination of the reduced D * and the reduced G that is orthogonal to the reduced D. */ iterc++; temp = gredsq * dredsq - dredg * dredg; if (temp <= qred * 1e-4 * qred) { // label = 190; // goto 190 loop = false; break; } temp = sqrt(temp); for (int i = 0; i < n; i++) s[i] = xbdi[i] == 0 ? (dredg * d[i] - dredsq * gnew[i])/temp : 0; 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 = 1; iact = -1; label = 0; for (int i = 0; i < n; i++) { if (xbdi[i] == 0) { double tempa = xopt[i] + d[i] - sl[i], tempb = su[i] - xopt[i] - d[i]; if (tempa <= 0) { nact++; xbdi[i] = -1; label = 100; break; } else if (tempb <= 0) { nact++; xbdi[i] = 1; label = 100; break; } double // ratio = 1, // seems unused di = d[i], si = s[i], ssq = di * di + si * si, val = xopt[i] - sl[i]; temp = ssq - val * val; if (temp > 0) { temp = sqrt(temp) - s[i]; if (angbd * temp > tempa) { angbd = tempa / temp; iact = i; xsav = -1; } } val = su[i] - xopt[i]; temp = ssq - val * val; if (temp > 0) { temp = sqrt(temp) + s[i]; if (angbd * temp > tempb) { angbd = tempb / temp; iact = i; xsav = 1; } } } // end if (xbdi[i] == 0) } // end for (int i = 0; i < n; i++) // Calculate HHD and some curvatures for the alternative iteration. if (label == 0) label = 210; break; case 150: //printState("trsbox-150-start", 0, null, xopt, null, null, d, null, null, xpt); double dhs = 0, dhd = 0; shs = 0; for (int i = 0; i < n; i++) { if (xbdi[i] == 0) { shs += s[i] * hs[i]; dhs += d[i] * hs[i]; dhd += d[i] * hred[i]; } } /* * 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. */ double redmax = redsav = 0, rdprev = 0, rdnext = 0, angt = 0, sth; int isav = 0, iu = (int) (17. * angbd + 3.1); for (int i = 1; i <= iu; i++) { angt = (angbd * i) / iu; sth = (angt + angt) / (1 + angt * angt); temp = shs + angt * (angt * dhd - dhs - dhs); double rednew = sth * (angt * dredg - sredg - 0.5 * sth * temp); if (rednew > redmax) { redmax = rednew; isav = i; rdprev = redsav; } else if (i == isav + 1) rdnext = rednew; 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) { //label = 190; break; // goto 190; loop = false; break; } if (isav < iu) { temp = (rdnext - rdprev) / (redmax + redmax - rdprev - rdnext); angt = angbd * (isav + 0.5 * temp) / iu; } double cth = (1 - angt * angt) / (1 + angt * angt); sth = (angt + angt) / (1 + angt * angt); temp = shs + angt * (angt * dhd - dhs - dhs); sdec = sth * (angt * dredg - sredg - 0.5 * sth * temp); if (sdec <= 0) { // label = 190; break; // goto 190; loop = false; break; } /* * 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 = gredsq = 0; for (int i = 0; i < n; i++) { gnew[i] = gnew[i] + (cth - 1) * hred[i] + sth * hs[i]; if (xbdi[i] == 0) { d[i] = cth * d[i] + sth * s[i]; double val = gnew[i]; dredg += d[i] * val; gredsq += val * val; } hred[i] = cth * hred[i] + sth * hs[i]; } //printState("trsbox-150-checkstate:\n" + // "stepsq="+stepsq+", gredsq="+gredsq, 0, null, null, null, null, null, null, null, null); qred += sdec; if (iact >= 0 && isav == iu) { nact++; xbdi[iact] = xsav; label = 100; break; // goto 100 } /* * If SDEC is sufficiently small, then RETURN after setting XNEW to * XOPT+D, giving careful attention to the bounds. */ if (sdec > 0.01 * qred) { label = 120; break; // goto 120 } loop = false; // goto 190 // End untested branch break; case 210: //printState("trsbox-210-start", 0, null, xopt, null, null, d, null, null, xpt); /* * 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. */ int ih = 0; for (int j = 0; j < n; j++) { hs[j] = 0; for (int i = 0; i <= j; i++) { if (i < j) hs[j] = hs[j] + hq[ih] * s[i]; hs[i] = hs[i] + hq[ih] * s[j]; ih++; } } for (int k = 0; k < npt; k++) { if (pq[k] != 0) { temp = 0; for (int j = 0; j < n; j++) temp += xpt[k][j] * s[j]; temp *= pq[k]; for (int i = 0; i < n; i++) hs[i] = hs[i] + temp * xpt[k][i]; } } if (crvmin != 0) { label = 50; break; // goto 50; } if (iterc > itcsav) { label = 150; break; // goto 150; } for (int i = 0; i < n; i++) hred[i] = hs[i]; label = 120; // goto 120; break; } // end humongous switch } // end for (;;) L30 // Label 190 //printState("trsbox-190-start", 0, null, xopt, null, null, d, null, null, xpt); dsq = 0; for (int i = 0; i < n; i++) { xnew[i] = max(min(xopt[i] + d[i], su[i]), sl[i]); if (xbdi[i] == -1) xnew[i] = sl[i]; if (xbdi[i] == 1) xnew[i] = su[i]; double val = d[i] = xnew[i] - xopt[i]; dsq += val * val; } return new double[] {dsq, crvmin}; } public static void main(String[] args) { MultivariableFunction ifun = new MultivariableFunction() { public double eval(double... x) { int n = x.length; double f = 0; for (int i = 3; i < n; i += 2) for (int j = 1; j < i - 1; j += 2) { double v1 = x[i-1] - x[j-1], v2 = x[i] - x[j]; f += 1 / sqrt(max(v1 * v1 + v2 * v2, 1e-6)); } return f; } }; double rhobeg = 1e-1, rhoend = 1e-6; int maxfun = 500000, max_n = 80, n = 10; System.out.println("rhobeg = " + rhobeg + ", rhoend = " + rhoend); /* * TODO The program is less optimal with the following input: * Java output: * n = 80, npt = 86 * Number of function calls = 19302 * Minimum value of F = 769.8202594453368 * * n = 80, npt = 161 * Number of function calls = 18331 * Minimum value of F = 769.820259440993 * * Fortran output: * n = 80, npt = 86 * Number of function values = 13732 * Least value of F = 7.692677518765548D+02 * * n = 80, npt = 161 * Number of function values = 4907 * Least value of F = 7.698202594229921D+02 */ do { int npt = n+6; double[] xl = new double[n], xu = new double[n], x = new double[n]; for (int i = 0; i < n; i++) { xl[i] = -1; xu[i] = 1; } for (int i = 0; i < n / 2; i++) { double temp = (i + 1) * 4 * PI / n; x[2 * i] = cos(temp); x[2 * i + 1] = sin(temp); } System.out.println("n = " + n + ", npt = " + npt); OptimizationResult result = bobyqa(x, xl, xu, ifun, npt, rhobeg, rhoend, maxfun, true); System.out.println(result); //writer.close(); //System.exit(0); npt = 2*n + 1; System.out.println("n = " + n + ", npt = " + npt); result = bobyqa(x, xl, xu, ifun, npt, rhobeg, rhoend, maxfun, true); System.out.println(result); n *= 2; } while (n <= max_n); } }

Ads help maintain this website.