package com.dwave.math; /** * Richardson extrapolation uses the powerful idea of extrapolating a computed * result to the value that would have been obtained if the stepsize had been very * much smaller than it actually was. In particular, extrapolation to zero stepsize is * the desired goal. The first practical ODE integrator that implemented this idea was * developed by Bulirsch and Stoer, and so extrapolation methods are often call * Bulirsch-Stoer methods. *
* Translated to Java directly from Numerical Recipes in C.
*/
public final class BulirschStoer {
private static float[] x;
private static float[][] d;
/**
* Private constructor to guard against instantiation
*/
private BulirschStoer() {}
//////////////////////////////
///// ALGORITHM ROUTINES /////
//////////////////////////////
/**
* Modified midpoint step. At xs, input the dependent variable vector
* y[1..nvar] and its derivative vector dydx[1..nvar].
* Also inputt is htot, the total step to be made, and nstep, the
* number of substeps to be used. The output is returned as yout[1..nvar], which need not
* be a distinct array from y; if it is distinct, however, then y
* and dydx are returned undamaged.
*/
public static void mmid(float[] y, float[] dydx, int nvar, float xs, float htot,
int nstep, float[] yout, ODEInterface derivs) {
int n, i;
float xmid, swap, h2, h;
float[] ym = new float[nvar],
yn = new float[nvar];
h = htot/nstep; // stepsize this trip
for(i=0; i < nvar; i++) {
ym[i] = y[i];
yn[i] = y[i] + h*dydx[i]; // first step
}
xmid = xs+h;
derivs.deriv(xmid, yn, yout); // will use yout for temporary storage of derivatives
h2 = 2f * h;
for(n=2; n <= nstep; n++) { // general step
for(i=0; i < nvar; i++) {
swap = ym[i] + h2*yout[i];
ym[i] = yn[i];
yn[i] = swap;
}
xmid += h;
derivs.deriv(xmid, yn, yout);
}
for(i=0; i < nvar; i++) // last step
yout[i] = 0.5f * (ym[i] +yn[i] + h*yout[i]);
}
/////////////////////////////////
///// EXTRAPOLATION METHODS /////
/////////////////////////////////
/**
* Use polynomial extrapolation to evaluate nv functions at x=0 by fitting
* a polynomial to a sequence of estimates with progressively smaller values x=xest,
* and corresponding function vectors yest[1..nv]. This call is number iest
* in the sequence of calls. Extrapolated function values are output as yz[1..nv],
* and their estimated eror is output as dy[1..nv].
*/
public static void pzextr(int iest, float xest, float[] yest, float[] yz, float[] dy, int nv) {
int k1, j;
float q, f2, f1, delta;
float[] c = new float[nv];
x[iest] = xest; // save current independent variable
for(j=0; j < nv; j++)
dy[j] = yz[j] = yest[j];
if(iest == 1) // Store first estimate in first column
for(j = 0; j < nv; j++)
d[j][0] = yest[j];
else {
for(j=0; j < nv; j++)
c[j] = yest[j];
for(k1=0; k1< iest;k1++) {
delta = 1.0f/(x[iest-k1] - xest);
f1 = xest * delta;
f2 = x[iest-k1] * delta;
for(j=0; jdydx[1..nv] at the starting value of the independent variable x.
* Also input are the stepsize to be attempted htry, the required accuracy
* eps, and the vector yscal[1..nv] against which the errror is
* scaled. On output, y and x are replaced by their new values,
* hdid is the stepsize that was actually accomplishhed, and hnext
* is the estimated next stepsize. derivs is the user-supplied routine that
* computes the right-hand side derivatives. Be sure to set htry on successive steps
* to the value of hnext returned from the previous step, as is the case
* if the routine is called by odeint.
*/
public static void bsstep(float[] y, float[] dydx, int nv, float[] xx, float htry,
float eps, float yscal[], float[] hdid, float[] hnext, ODEInterface derivs) {
int KMAXX = 8; // Max row number used
int IMAXX = KMAXX + 1; // in the extrapolation
float SAFE1 = 0.25F, // Safety factors
SAFE2 = 0.7F,
REDMAX = 1.0e-5F, // Maximum factor for stepsize reduction
REDMIN = 0.7F, // Minimum factor for stepsize reduction
TINY = 1.0E-30F, // Prevents division by zero
SCALMX = 0.1F; // 1/SCALMX is the max factor by which a stepsize can be increased
int i, iq, k, kk, km=0;
int kmax=0, kopt=0;
float epsold = -1.0f;
float eps1, errmax=0f, fact, h, red=0f, scale=0f, work, wrkmin, xest, xnew=0f;
float[] err, yerr, ysav, yseq;
float[] a = new float[IMAXX+1];
float[][] alf = new float[KMAXX+1][KMAXX+1];
int nseq[] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18};
boolean reduct = false;
boolean exitflag = false;
boolean first = true;
d = new float[nv][KMAXX];
err=new float[KMAXX];
x=new float[KMAXX];
yerr=new float[nv];
ysav=new float[nv];
yseq=new float[nv];
if (eps != epsold) { // A new tolerance, so reinitialize.
hnext[0] =-1.0e29f; // "Impossible" values.
xnew = -1.0e29f; // "Impossible" values.
eps1 = SAFE1*eps;
a[0] = nseq[0]+1; //Compute work coeffcients A_k
for (k = 0;k < KMAXX;k++)
a[k+1] = a[k]+nseq[k+1];
for (iq = 1; iq < KMAXX; iq++) { // Compute (k, q).
for (k = 0; k < iq; k++)
alf[k][iq] = (float) Math.pow(eps1, (a[k+1]-a[iq+1]) / ((a[iq+1]-a[0]+1.0)*(2*k+1)));
}
epsold = eps;
for (kopt = 1; kopt < KMAXX-1; kopt++) //Determine optimal row number for convergence.
if (a[kopt+1] > a[kopt]*alf[kopt-1][kopt])
break;
kmax = kopt;
}
h = htry;
for (i = 0;i < nv;i++)
ysav[i] = y[i]; // Save the starting values.
if (xx[0] != xnew || h != hnext[0]) { //A new stepsize or a new integration: re-establish the order window.
first = true;
kopt = kmax;
}
reduct = false;
for (;;) {
for (k = 0;k < kmax;k++) { // Evaluate the sequence of modified midpoint integrations.
xnew = xx[0] + h;
if (xnew == xx[0]) {
System.out.println("step size underflow in bsstep");
return;
}
mmid(ysav, dydx, nv, xx[0], h, nseq[k], yseq, derivs);
xest = (h/nseq[k])*(h/nseq[k]); // Squared, since error series is even.
pzextr(k, xest, yseq, y, yerr, nv); // Perform extrapolation.
if (k != 0) { // Compute normalized error estimate (k).
errmax = TINY;
for (i = 0;i < nv;i++)
errmax = Math.max(errmax, Math.abs(yerr[i]/yscal[i]));
errmax /= eps; // Scale error relative to tolerance.
km = k-1;
err[km] = (float)Math.pow(errmax/SAFE1, 1.0/(2*km+1));
}
if (k != 0 && (k >= kopt-1 || first)) { // In order window.
if (errmax < 1.0) { // Converged.
exitflag = true;
break;
}
if (k == kmax || k == kopt+1) { // Check for possible stepsize reduction.
red = SAFE2/err[km];
break;
}
else if (k == kopt && alf[kopt-1][kopt] < err[km]) {
red = 1.0f/err[km];
break;
}
else if (kopt == kmax && alf[km][kmax-1] < err[km]) {
red = alf[km][kmax-1]*SAFE2/err[km];
break;
}
else if (alf[km][kopt] < err[km]) {
red = alf[km][kopt-1]/err[km];
break;
}
}
}
if (exitflag)
break;
red = Math.min(red, REDMIN); // Reduce stepsize by at least REDMIN and at most REDMAX.
red = Math.max(red, REDMAX);
h *= red;
reduct = true;
} // Try again.
xx[0] = xnew; // Successful step taken.
hdid[0] = h;
first = false;
wrkmin = 1.0e35f; // Compute optimal row for convergence and corresponding stepsize.
for (kk = 0;kk < km; kk++) {
fact = Math.max(err[kk], SCALMX);
work = fact*a[kk+1];
if (work < wrkmin) {
scale = fact;
wrkmin = work;
kopt = kk+1;
}
}
hnext[0] = h/scale;
if (kopt >= k && kopt == kmax && !reduct) { //Check for possible order increase, but not if stepsize was just reduced.
fact = Math.max(scale/alf[kopt-1][kopt], SCALMX);
if (a[kopt+1]*fact <= wrkmin) {
hnext[0] = h/fact;
kopt++;
}
}
}
///////////////////
///// DRIVERS /////
///////////////////
}
/**
* Stoermer's rule for integrating y'' = f(x, y) for a system of n = nv/2
* equations. On input y[1..nv] contains y in its first n elements and
* y' in its second n elements, all evaluated at xs. d2y[1..nv]
* contains the right-hand side function f (also evaluated at xs)
* in its first n elements. Its second n elements are not referneced.
* Also input is htot, the total step to be taken, and nstep,
* the number of substeps to be used. The output is returned as yout[1..nv]
* with the same storage arrangements as y. derivs is the user-supplied
* routine that calculates f.
public static void stoerm(float[] y, float[] d2y, int nv, float xs, float htot, int nstep,
float[] yout, ODEInterface derivs) {
int i, n, neqns, nn;
float h, h2, halfh, xstoerm;
float[] ytemp = new float[nv];
h = htot/nstep; // stepsize this trip
halfh= 0.5f*h;
neqns=nv/2; // number of equations
for(i=1;i<=neqns;i++) { // first step
n=neqns+i;
ytemp[n] = h*(y[n] + halfh*d2y[i]);
ytemp[i] = y[i]+ytemp[n];
}
xstoerm = xs+h;
derivs.deriv(xstoerm, ytemp, yout); // use yout for temporary storage of derivatives
h2=h*h;
for(nn=2; nn<=nstep; nn++) { // general step
for(i=1; i <=neqns; i++) {
n=neqns+1;
ytemp[n] += h2*yout[i];
ytemp[i] += ytemp[n];
}
xstoerm += h;
derivs.deriv(xstoerm, ytemp, yout);
}
for(i=1; i <=neqns;i++) { // last step
n=neqns+1;
yout[n] = ytemp[n]/h + halfh*yout[i];
yout[i] = ytemp[i];
}
} */