public static void LevenbergMarquardtFit (LMFunction fcn, double[] xvec, double[] fvec, double ftol, double xtol, double gtol, int maxfev, double epsfcn, double[] diag, int mode, double factor, int nprint, ref int info, ref int nfev, double[] fjac, int ldfjac, int[] ipvt, double[] qtf, double[] wa1, double[] wa2, double[] wa3, double[] wa4) // // The purpose of LevenbergMarquardtFit is to minimize the sum of the // squares of m nonlinear functions in n variables by a modification of // the Levenberg-Marquardt algorithm. The user must provide a // subroutine which calculates the functions. The Jacobian is // then calculated by a forward-difference approximation. // // This is the most general interface to the Levenberg-Marquardt algorithm // which gives you full control over the minimization process and auxilliary // storage allocation. Use one of the simpler interfaces above, if you don't // all arguments. // // Arguments: // // fcn is the name of the user-supplied subroutine which // calculates the functions. fcn should be written as follows: // // void fcn (int m, int n, double* xvec, double* fvec, int& iflag) // { // ... // calculate the functions at xvec[0..n-1] and // return this vector in fvec[0..m-1]. // ... // } // // The value of iflag should not be changed by fcn unless // the user wants to terminate execution of LevenbergMarquardtFit. // In this case set iflag to a negative integer. // // xvec is an array of length n. On input x must contain // an initial estimate of the solution vector. On output x // contains the final estimate of the solution vector. // // fvec is an output array of length m which contains // the functions evaluated at the output x. // // ftol is a nonnegative input variable. Termination // occurs when both the actual and predicted relative // reductions in the sum of squares are at most ftol. // therefore, ftol measures the relative error desired // in the sum of squares. // // xtol is a nonnegative input variable. Termination // occurs when the relative error between two consecutive // iterates is at most xtol. Therefore, xtol measures the // relative error desired in the approximate solution. // // gtol is a nonnegative input variable. Termination // occurs when the cosine of the angle between fvec and // any column of the Jacobian is at most gtol in absolute // value. therefore, gtol measures the orthogonality // desired between the function vector and the columns // of the Jacobian. // // maxfev is a positive integer input variable. Termination // occurs when the number of calls to fcn is at least // maxfev by the end of an iteration. // // epsfcn is an input variable used in determining a suitable // step length for the forward-difference approximation. This // approximation assumes that the relative errors in the // functions are of the order of epsfcn. If epsfcn is less // than the machine precision, it is assumed that the relative // errors in the functions are of the order of the machine // precision. // // diag is an array of length n. If mode = 1 (see // below), diag is internally set. If mode = 2, diag // must contain positive entries that serve as // multiplicative scale factors for the variables. // // mode is an integer input variable. If mode = 1, the // variables will be scaled internally. If mode = 2, // the scaling is specified by the input diag. Other // values of mode are equivalent to mode = 1. // // factor is a positive input variable used in determining the // initial step bound. This bound is set to the product of // factor and the euclidean norm of diag*x if nonzero, or else // to factor itself. In most cases factor should lie in the // interval (0.1,100.0). 100.0 is a generally recommended value. // // nprint is an integer input variable that enables controlled // printing of iterates if it is positive. In this case, // fcn is called with iflag = 0 at the beginning of the first // iteration and every nprint iterations thereafter and // immediately prior to return, with x and fvec available // for printing. if nprint is not positive, no special calls // of fcn with iflag = 0 are made. // // info is an integer output variable. If the user has // terminated execution, info is set to the (negative) // value of iflag. see description of fcn. Otherwise, // info is set as follows: // // info = 0 improper input parameters. // // info = 1 both actual and predicted relative reductions // in the sum of squares are at most ftol. // // info = 2 relative error between two consecutive iterates // is at most xtol. // // info = 3 conditions for info = 1 and info = 2 both hold. // // info = 4 the cosine of the angle between fvec and any // column of the Jacobian is at most gtol in // absolute value. // // info = 5 number of calls to fcn has reached or // exceeded maxfev. // // info = 6 ftol is too small. No further reduction in // the sum of squares is possible. // // info = 7 xtol is too small. No further improvement in // the approximate solution x is possible. // // info = 8 gtol is too small. fvec is orthogonal to the // columns of the Jacobian to machine precision. // // nfev is an integer output variable set to the number of // calls to fcn. // // fjac is an output m by n array. the upper n by n submatrix // of fjac contains an upper triangular matrix r with // diagonal elements of nonincreasing magnitude such that // // t t t // p *(jac *jac)*p = r *r, // // where p is a permutation matrix and jac is the final // calculated Jacobian. column j of p is column ipvt(j) // (see below) of the identity matrix. the lower trapezoidal // part of fjac contains information generated during // the computation of r. // // ldfjac is a positive integer input variable not less than m // which specifies the leading dimension of the array fjac. // // ipvt is an integer output array of length n. ipvt // defines a permutation matrix p such that jac*p = q*r, // where jac is the final calculated jacobian, q is // orthogonal (not stored), and r is upper triangular // with diagonal elements of nonincreasing magnitude. // column j of p is column ipvt(j) of the identity matrix. // // qtf is an output array of length n which contains // the first n elements of the vector (q transpose)*fvec. // // wa1, wa2, and wa3 are work arrays of length n. // // wa4 is a work array of length m. // { const double p1 = 0.1; const double p5 = 0.5; const double p25 = 0.25; const double p75 = 0.75; const double p0001 = 1e-4; int fjac_dim1, fjac_offset; int iter; double temp=0.0, temp1, temp2; int i, j, l, iflag; double delta=0; // LELLID!! TODO there was no initialization of delta in the original code double ratio; double fnorm, gnorm; double pnorm, xnorm=0.0, fnorm1, actred, dirder, prered; double par, sum; // Parameter adjustments int m,n; m = fvec.Length; // number of functions n = xvec.Length; // number of variables double[] x = xvec; double[] f = fvec; // x = &xvec[ xvec.Lo()-1 ]; // 1-offset solution variables vector // f = &fvec[ fvec.Lo()-1 ]; // 1-offset functions vector //--wa4; //--wa3; //--wa2; //--wa1; //--qtf; //--ipvt; fjac_dim1 = ldfjac; fjac_offset = fjac_dim1 + 1; //fjac -= fjac_offset; //--diag; info = iflag = nfev = 0; // check the input parameters for errors if (n <= 0 || m < n || ldfjac < m || ftol < 0.0 || xtol < 0.0 || gtol < 0.0 || maxfev <= 0 || factor <= 0.0) goto L300; if (mode != 2) goto L20; for (j = 0; j < n; j++) // LELLID!! if (diag[j] <= 0.0) goto L300; L20: // evaluate the function at the starting point and calculate its norm iflag = 1; fcn(m, n, x, f, ref iflag); nfev = 1; if (iflag < 0) goto L300; fnorm = enorm(m, f); // initialize levenberg-marquardt parameter and iteration counter par = 0.0; iter = 1; // beginning of the outer loop. L30: // calculate the Jacobian matrix. iflag = 2; fdjac2(fcn, m, n, x, f, fjac, ldfjac, ref iflag, epsfcn, wa4); nfev += n; if (iflag < 0) goto L300; // if requested, call fcn to enable printing of iterates if (nprint <= 0) goto L40; iflag = 0; if ((iter - 1) % nprint == 0) fcn(m, n, x, f, ref iflag); if (iflag < 0) goto L300; L40: // compute the qr factorization of the Jacobian. qrfac(m, n, fjac, ldfjac, true, ipvt, n, wa1, wa2, wa3); // on the first iteration and if mode is 1, scale according // to the norms of the columns of the initial Jacobian if (iter != 1) goto L80; if (mode == 2) goto L60; for (j = 0; j < n; ++j) { // LELLID!! diag[j] = wa2[j]; if (wa2[j] == 0.0) diag[j] = 1.0; } L60: // on the first iteration, calculate the norm of the scaled x // and initialize the step bound delta for (j = 0; j < n; ++j) // LELLID wa3[j] = diag[j] * x[j]; xnorm = enorm(n, wa3); delta = factor * xnorm; if (delta == 0.0) delta = factor; L80: // form (q transpose)*f and store the first n components in qtf for (i = 0; i < m; ++i) wa4[i] = f[i]; // LELLID!! for (j = 0; j < n; ++j) { // LELLID!! if (fjac[j + j * fjac_dim1] != 0.0) { sum = 0.0; for (i = j; i < m; ++i) // LELLID!! sum += fjac[i + j * fjac_dim1] * wa4[i]; temp = -sum / fjac[j + j * fjac_dim1]; for (i = j; i < m; ++i) // LELLID!! wa4[i] += fjac[i + j * fjac_dim1] * temp; } fjac[j + j * fjac_dim1] = wa1[j]; qtf[j] = wa4[j]; } // compute the norm of the scaled gradient gnorm = 0.0; if (fnorm != 0.0) for (j = 0; j < n; ++j) { // LELLID!! l = ipvt[j]; if (wa2[l] != 0.0) { sum = 0.0; for (i = 0; i <= j; ++i) // LELLID!! sum += fjac[i + j * fjac_dim1] * (qtf[i] / fnorm); gnorm = Math.Max( gnorm, Math.Abs(sum/wa2[l]) ); } } // test for convergence of the gradient norm if (gnorm <= gtol) info = 4; if (info != 0) goto L300; // rescale if necessary if (mode != 2) for (j = 0; j < n; ++j) // LELLID!! diag[j] = Math.Max( diag[j], wa2[j] ); // beginning of the inner loop L200: // determine the levenberg-marquardt parameter lmpar(n, fjac, ldfjac, ipvt, diag, qtf, delta, ref par, wa1, wa2, wa3, wa4); // store the direction p and x + p. calculate the norm of p for (j = 0; j < n; ++j) { // LELLID!! wa1[j] = -wa1[j]; wa2[j] = x[j] + wa1[j]; wa3[j] = diag[j] * wa1[j]; } pnorm = enorm(n, wa3); // on the first iteration, adjust the initial step bound if (iter == 1) delta = Math.Min(delta, pnorm); // evaluate the function at x + p and calculate its norm iflag = 1; fcn(m, n, wa2, wa4, ref iflag); ++nfev; if (iflag < 0) goto L300; fnorm1 = enorm(m, wa4); // compute the scaled actual reduction actred = -1.0; if (p1 * fnorm1 < fnorm) actred = 1.0 - sqr(fnorm1 / fnorm); // compute the scaled predicted reduction and // the scaled directional derivative for (j = 0; j < n; ++j) { // Lellid!! wa3[j] = 0.0; l = ipvt[j]; temp = wa1[l]; for (i = 0; i <= j; ++i) { // LELLID!! wa3[i] += fjac[i + j * fjac_dim1] * temp; } } temp1 = enorm(n, wa3) / fnorm; temp2 = Math.Sqrt(par) * pnorm / fnorm; prered = sqr(temp1) + sqr(temp2) / p5; dirder = -(sqr(temp1) + sqr(temp2)); // compute the ratio of the actual to the predicted reduction ratio = 0.0; if (prered != 0.0) ratio = actred / prered; // update the step bound if (ratio > p25) goto L240; if (actred >= 0.0) temp = p5; if (actred < 0.0) temp = p5 * dirder / (dirder + p5 * actred); if (p1 * fnorm1 >= fnorm || temp < p1) temp = p1; delta = temp * Math.Min(delta, pnorm / p1); par /= temp; goto L260; L240: if (par != 0.0 && ratio < p75) goto L260; delta = pnorm / p5; par = p5 * par; L260: // test for successful iteration if (ratio < p0001) goto L290; // successful iteration. update x, f, and their norms for (j = 0; j < n; ++j) { // LELLID x[j] = wa2[j]; wa2[j] = diag[j] * x[j]; } for (i = 0; i < m; ++i) { // LELLID!! f[i] = wa4[i]; } xnorm = enorm(n, wa2); fnorm = fnorm1; ++iter; L290: // tests for convergence if (Math.Abs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1.0) info = 1; if (delta <= xtol * xnorm) info = 2; if (Math.Abs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1.0 && info == 2) info = 3; if (info != 0) goto L300; // tests for termination and stringent tolerances if (nfev >= maxfev) info = 5; if (Math.Abs(actred) <= DBL_EPSILON && prered <= DBL_EPSILON && p5 * ratio <= 1.0) info = 6; if (delta <= DBL_EPSILON * xnorm) info = 7; if (gnorm <= DBL_EPSILON) info = 8; if (info != 0) goto L300; // end of the inner loop. repeat if iteration unsuccessful if (ratio < p0001) goto L200; // end of the outer loop goto L30; L300: // termination, either normal or user imposed if (iflag < 0) info = iflag; iflag = 0; if (nprint > 0) fcn(m, n, x, f, ref iflag); }
/// <summary> /// The purpose of LevenbergMarquardtFit is to minimize the sum of the /// squares of m nonlinear functions in n variables by a modification of the /// Levenberg-Marquardt algorithm. This is done by using the more /// general least-squares solver below. The user must provide a /// subroutine which calculates the functions. The Jacobian is /// then calculated by a forward-difference approximation. /// </summary> /// <param name="fcn">The user supplied function which provides the values to minimize.</param> /// <param name="xvec"> /// Array of length n containing the parameter vector. On input x must contain /// an initial estimate of the solution vector. On output x /// contains the final estimate of the solution vector. /// </param> /// <param name="fvec">Output array of length m which contains the functions evaluated at the output x. </param> /// <param name="tol"> /// Nonnegative input variable. Termination occurs /// when the algorithm estimates either that the relative /// error in the sum of squares is at most tol or that /// the relative error between x and the solution is at /// most tol. /// </param> /// <param name="info"> /// Info is an integer output variable. If the user has /// terminated execution, info is set to the (negative) /// value of iflag. See description of fcn. Otherwise, /// info is set as follows: /// /// info = 0 improper input parameters. /// /// info = 1 algorithm estimates that the relative error /// in the sum of squares is at most tol. /// /// info = 2 algorithm estimates that the relative error /// between x and the solution is at most tol. /// /// info = 3 conditions for info = 1 and info = 2 both hold. /// /// info = 4 fvec is orthogonal to the columns of the /// Jacobian to machine precision. /// /// info = 5 number of calls to fcn has reached or /// exceeded 200*(n+1). /// /// info = 6 tol is too small. No further reduction in /// the sum of squares is possible. /// /// info = 7 tol is too small. No further improvement in /// the approximate solution x is possible. /// </param> /// <param name="iwa">Integer working array of length n.</param> /// <param name="diag"></param> /// <param name="fjac"></param> /// <param name="ipvt"></param> /// <param name="qtf"></param> /// <param name="wa1"></param> /// <param name="wa2"></param> /// <param name="wa3"></param> /// <param name="wa4"></param> /// <remarks> /// This is the most easy-to-use interface with the smallest number of /// arguments. If you need more control over the minimization process and /// auxilliary storage allocation you should use one of the interfaces /// described below. /// </remarks> public static void LevenbergMarquardtFit( LMFunction fcn, double[] xvec, double[] fvec, double tol, ref int info, int[] iwa, double[] diag, double[] fjac, int[] ipvt, double[] qtf, double[] wa1, double[] wa2, double[] wa3, double[] wa4) // // iwa is an integer work array of length n. // // wa is a work array of length lwa. // // lwa is a positive integer input variable not less than // m*n+5*n+m. // // { int mode, nfev, maxfev, nprint; double factor, ftol, gtol, xtol, epsfcn; // Parameter adjustments int m,n; m = fvec.Length; // number of functions n = xvec.Length; // number of variables // --wa; // --iwa; info = 0; nfev = 0; // check the input parameters for errors if (n <= 0 || m < n || tol < 0 ) return; factor = 100; maxfev = (n + 1) * 200; ftol = tol; xtol = tol; gtol = 0; epsfcn = 0; mode = 1; nprint = 0; LevenbergMarquardtFit(fcn, xvec, fvec, ftol, xtol, gtol, maxfev, epsfcn, diag, mode, factor, nprint, ref info, ref nfev, fjac, m, ipvt, qtf, wa1, wa2, wa3, wa4); if (info == 8) info = 4; }
/// <summary> /// This will compute the covariances at a given parameter set xvec. /// </summary> /// <param name="func"></param> /// <param name="xvec"></param> /// <param name="covar"></param> public static int ComputeCovariances(LMFunction fcn, double[] x, int n, int m, double[]covar, out double sumchisq) { int info=0; double[] f = new double[n]; double[] fjac = new double[n*m]; double [] jactjac = new double[m*m]; fcn(n,m,x,f, ref info); sumchisq=0; for(int i=0;i<n;++i) sumchisq += f[i]*f[i]; // calculate the Jacobian matrix. int iflag = 2; int ldfjac = n; double epsfcn=0; double[] wa4 = new double[n]; fdjac2(fcn, n, m, x, f, fjac, ldfjac, ref iflag, epsfcn, wa4); // compute jacT*jac for(int i=0;i<m;++i) { for(int j=0;j<m;++j) { double sum = 0; for(int k=0;k<n;++k) { sum += fjac[k+n*i]*fjac[k+n*j]; } jactjac[j+i*m] = sum; } } return LEVMAR_COVAR(jactjac, covar, sumchisq, m, n); }
/// <summary> /// The purpose of LevenbergMarquardtFit is to minimize the sum of the /// squares of m nonlinear functions in n variables by a modification of the /// Levenberg-Marquardt algorithm. This is done by using the more /// general least-squares solver below. The user must provide a /// subroutine which calculates the functions. The Jacobian is /// then calculated by a forward-difference approximation. /// </summary> /// <param name="fcn">The user supplied function which provides the values to minimize.</param> /// <param name="xvec"> /// Array of length n containing the parameter vector. On input x must contain /// an initial estimate of the solution vector. On output x /// contains the final estimate of the solution vector. /// </param> /// <param name="fvec">Output array of length m which contains the functions evaluated at the output x. </param> /// <param name="tol"> /// Nonnegative input variable. Termination occurs /// when the algorithm estimates either that the relative /// error in the sum of squares is at most tol or that /// the relative error between x and the solution is at /// most tol. /// </param> /// <param name="info"> /// Info is an integer output variable. If the user has /// terminated execution, info is set to the (negative) /// value of iflag. See description of fcn. Otherwise, /// info is set as follows: /// /// info = 0 improper input parameters. /// /// info = 1 algorithm estimates that the relative error /// in the sum of squares is at most tol. /// /// info = 2 algorithm estimates that the relative error /// between x and the solution is at most tol. /// /// info = 3 conditions for info = 1 and info = 2 both hold. /// /// info = 4 fvec is orthogonal to the columns of the /// Jacobian to machine precision. /// /// info = 5 number of calls to fcn has reached or /// exceeded 200*(n+1). /// /// info = 6 tol is too small. No further reduction in /// the sum of squares is possible. /// /// info = 7 tol is too small. No further improvement in /// the approximate solution x is possible. /// </param> /// <remarks> /// This is the most easy-to-use interface with the smallest number of /// arguments. If you need more control over the minimization process and /// auxilliary storage allocation you should use one of the interfaces /// described below. /// </remarks> public static void LevenbergMarquardtFit( LMFunction fcn, double[] xvec, double[] fvec, double tol, ref int info) { int m,n; int[] iwa; m = fvec.Length; // number of functions n = xvec.Length; // number of variables // allocate working arrays // lwa = m*n+5*n+m; iwa = new int[n]; // wa = new double[lwa]; double[] diag = new double[n]; double[] fjac = new double[n*m]; int[] ipvt = new int[n]; double[] qtf = new double[n]; double[] wa1 = new double[n]; double[] wa2 = new double[n]; double[] wa3 = new double[n]; double[] wa4 = new double[m]; LevenbergMarquardtFit (fcn,xvec,fvec,tol,ref info,iwa, diag, fjac, ipvt, qtf, wa1, wa2, wa3,wa4); }
/// <summary> /// This subroutine computes a forward-difference approximation /// to the m by n Jacobian matrix associated with a specified /// problem of m functions in n variables. /// </summary> /// <param name="fcn">User-supplied subroutine which calculates the functions</param> /// <param name="m">m is a positive integer input variable set to the number of functions.</param> /// <param name="n">n is a positive integer input variable set to the number of variables. n must not exceed m.</param> /// <param name="x">x is an input array of length n containing the parameters.</param> /// <param name="fvec">fvec is an input array of length m which must contain the functions evaluated at x. </param> /// <param name="fjac">fjac is an output m by n array which contains the approximation to the Jacobian matrix evaluated at x.</param> /// <param name="ldfjac">ldfjac is a positive integer input variable not less than m which specifies the leading dimension of the array fjac. </param> /// <param name="iflag">iflag is an integer variable which can be used to terminate the execution of fdjac2. see description of fcn.</param> /// <param name="epsfcn"> /// epsfcn is an input variable used in determining a suitable /// step length for the forward-difference approximation. this /// approximation assumes that the relative errors in the /// functions are of the order of epsfcn. if epsfcn is less /// than the machine precision, it is assumed that the relative /// errors in the functions are of the order of the machine /// precision. /// </param> /// <param name="wa">wa is a work array of length m.</param> public static void fdjac2( LMFunction fcn, int m, int n, double[] x, double[] fvec, double[] fjac, int ldfjac, ref int iflag, double epsfcn, double[] wa) { int fjac_dim1; double temp, h; int i, j; double eps; // Parameter adjustments // --wa; LELLID!! fjac_dim1 = ldfjac; // fjac_offset = fjac_dim1 + 1; // LELLID!! // fjac -= fjac_offset; // LELLID!! // --fvec; LELLID!! // --x; // LELLID!! eps = Math.Sqrt((Math.Max(epsfcn, DBL_EPSILON))); for (j = 0; j < n; ++j) { // LELLID!! temp = x[j]; h = eps * Math.Abs(temp); if (h == 0.0) h = eps; x[j] = temp + h; fcn(m, n, x, wa, ref iflag); if (iflag < 0) return; x[j] = temp; for (i = 0; i < m; ++i) // LELLID!! fjac[i + j * fjac_dim1] = (wa[i] - fvec[i]) / h; } }
/// <summary> /// This will compute the covariances at a given parameter set xvec. /// </summary> /// <param name="fcn">The function that was to minimize.</param> /// <param name="x">Parameter vector.</param> /// <param name="n">First dimension of the parameter vector.</param> /// <param name="m">Second dimension of the parameter vector.</param> /// <param name="covar">Array to hold the covariance matrix. Must be of dimension m*n.</param> /// <param name="sumchisq">Outputs the sum of chi squared.</param> public static int ComputeCovariances(LMFunction fcn, double[] x, int n, int m, double[]covar, out double sumchisq) { int info=0; double[] f = new double[n]; double[] fjac = new double[n*m]; double [] jactjac = new double[m*m]; fcn(n,m,x,f, ref info); sumchisq=0; for(int i=0;i<n;++i) sumchisq += f[i]*f[i]; // calculate the Jacobian matrix. int iflag = 2; int ldfjac = n; double epsfcn=0; double[] wa4 = new double[n]; fdjac2(fcn, n, m, x, f, fjac, ldfjac, ref iflag, epsfcn, wa4); // compute jacT*jac for(int i=0;i<m;++i) { for(int j=0;j<m;++j) { double sum = 0; for(int k=0;k<n;++k) { sum += fjac[k+n*i]*fjac[k+n*j]; } jactjac[j + i * m] = sum; } } // changed 20060519: scale jactjac so that the diagonal is 1 (except for those elements which are zero) double[] scale = new double[m]; for(int i=0;i<m;i++) { double jj = jactjac[i+i*m]; scale[i] = jj == 0 ? 1 : 1 / Math.Sqrt(Math.Abs(jj)); } for (int i = 0; i < m; ++i) for (int j = 0; j < m; ++j) jactjac[j + i * m] *= scale[i] * scale[j]; int result = LEVMAR_COVAR(jactjac, covar, sumchisq, m, n); // changed 20060519: now scale the covar back again so that Diag[x]*covar*Diag[x] for (int i = 0; i < m; ++i) for (int j = 0; j < m; ++j) covar[j + i * m] *= scale[i] * scale[j]; return result; }