protected static void lnsrch( int n, double[] xold, double fold, double[] g, double[] p, double[] x, out double f, double stpmax, out int check, BFGSFunctionEval _FunctionEval, object _Params ) { int i; double a,alam,alam2 = 0.0,alamin,b,disc,f2 = 0.0,fold2 = 0.0,rhs1,rhs2,slope,sum,temp,test,tmplam; check=0; for ( sum=0.0,i=1; i <= n; i++ ) sum += p[i]*p[i]; sum = Math.Sqrt( sum ); if ( sum > stpmax ) for ( i=1; i <= n; i++ ) p[i] *= stpmax / sum; for ( slope=0.0,i=1; i <= n; i++ ) slope += g[i] * p[i]; test = 0.0; for ( i=1; i <= n; i++ ) { temp = Math.Abs( p[i] ) / Math.Max( Math.Abs( xold[i] ), 1.0 ); if ( temp > test ) test = temp; } alamin = TOLY / test; alam = 1.0; for (;;) { for ( i=1; i <= n; i++ ) x[i] = xold[i] + alam * p[i]; f = _FunctionEval( x, _Params ); if ( alam < alamin ) { for ( i=1; i <= n; i++ ) x[i] = xold[i]; check = 1; return; } else if ( f <= fold + ALF * alam * slope ) return; else { if ( alam == 1.0 ) tmplam = -slope / (2.0 * (f - fold-slope)); else { rhs1 = f-fold-alam*slope; rhs2 = f2-fold2-alam2*slope; a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2); b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2); if (a == 0.0) tmplam = -slope/(2.0*b); else { disc = b*b - 3.0 * a * slope; if ( disc < 0.0 ) throw new Exception( "Roundoff problem in lnsrch." ); else tmplam = (-b + Math.Sqrt( disc ) ) / (3.0 * a); } if ( tmplam > 0.5 * alam ) tmplam = 0.5 * alam; } } alam2=alam; f2 = f; fold2=fold; alam = Math.Max( tmplam, 0.1*alam ); } }
/// <summary> /// Performs BFGS function minimzation on a quadratic form function evaluated by the provided delegate /// </summary> /// <param name="_Coefficients">The array of initial coefficients (indexed from 1!!) that will also contain the resulting coefficients when the routine has converged</param> /// <param name="_ConvergenceTolerance">The tolerance error to accept as the minimum of the function</param> /// <param name="_PerformedIterationsCount">The amount of iterations performed to reach the minimum</param> /// <param name="_Minimum">The found minimum</param> /// <param name="_FunctionEval">The delegate used to evaluate the function to minimize</param> /// <param name="_FunctionGradientEval">The delegate used to evaluate the gradient of the function to minimize</param> /// <param name="_Params">Some user params passed to the evaluation functions</param> protected static void dfpmin( double[] _Coefficients, double _ConvergenceTolerance, out int _PerformedIterationsCount, out double _Minimum, BFGSFunctionEval _FunctionEval, BFGSFunctionGradientEval _FunctionGradientEval, object _Params ) { int n = _Coefficients.Length - 1; int check,i,its,j; double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test; double[] dg = new double[1+n]; double[] g = new double[1+n]; double[] hdg = new double[1+n]; double[][] hessin = new double[1+n][]; for ( i=1; i <= n; i++ ) hessin[i] = new double[1+n]; double[] pnew = new double[1+n]; double[] xi = new double[1+n]; // Initialize values fp = _FunctionEval( _Coefficients, _Params ); _FunctionGradientEval( _Coefficients, g, _Params ); for ( i=1; i <= n; i++ ) { for ( j=1; j <= n; j++ ) hessin[i][j]=0.0; hessin[i][i] = 1.0; xi[i] = -g[i]; sum += _Coefficients[i]*_Coefficients[i]; } stpmax = STPMX * Math.Max( Math.Sqrt( sum ), n ); for ( its=1; its <= ITMAX; its++ ) { _PerformedIterationsCount = its; // The new function evaluation occurs in lnsrch lnsrch( n, _Coefficients, fp, g, xi, pnew, out _Minimum, stpmax, out check, _FunctionEval, _Params ); fp = _Minimum; for ( i=1; i<=n; i++ ) { xi[i] = pnew[i] - _Coefficients[i]; // Update the line direction _Coefficients[i] = pnew[i]; // as well as the current point } // Test for convergence on Delta X test = 0.0; for ( i=1; i <= n; i++ ) { temp = Math.Abs( xi[i] ) / Math.Max( Math.Abs( _Coefficients[i] ), 1.0 ); if ( temp > test ) test = temp; } if ( test < TOLX ) return; // Done! // Save the old gradient for ( i=1; i <= n; i++ ) dg[i] = g[i]; // Get the new one _FunctionGradientEval( _Coefficients, g, _Params ); // Test for convergence on zero gradient test = 0.0; den = Math.Max( _Minimum, 1.0 ); for ( i=1; i <= n; i++ ) { temp = Math.Abs( g[i] ) * Math.Max( Math.Abs( _Coefficients[i] ), 1.0 ) / den; if ( temp > test ) test = temp; } if ( test < _ConvergenceTolerance ) return; // Done! // Compute difference of gradients for ( i=1; i <= n ; i++ ) dg[i] = g[i]-dg[i]; // ...and difference times current hessian matrix for ( i=1; i <= n; i++ ) { hdg[i]=0.0; for ( j=1; j <= n; j++ ) hdg[i] += hessin[i][j] * dg[j]; } // Calculate dot products for the denominators fac = fae = sumdg = sumxi = 0.0; for ( i=1; i <= n; i++ ) { fac += dg[i] * xi[i]; fae += dg[i] * hdg[i]; sumdg += dg[i] * dg[i]; sumxi += xi[i] * xi[i]; } if ( fac * fac > EPS * sumdg * sumxi ) { fac = 1.0 / fac; fad = 1.0 / fae; // The vector that makes BFGS different from DFP for ( i=1; i <= n; i++ ) dg[i] = fac * xi[i] - fad * hdg[i]; // BFGS Hessian update formula for ( i=1; i <= n; i++ ) for ( j=1; j <= n; j++ ) hessin[i][j] += fac * xi[i] * xi[j] -fad * hdg[i] * hdg[j] + fae * dg[i] * dg[j]; } // Now, calculate the next direction to go for ( i=1; i <= n; i++ ) { xi[i] = 0.0; for ( j=1; j <= n; j++ ) xi[i] -= hessin[i][j] * g[j]; } } throw new Exception( "Too many iterations in dfpmin" ); }