/************************************************************************* Clears request fileds (to be sure that we don't forgot to clear something) *************************************************************************/ private static void clearrequestfields(nleqstate state) { state.needf = false; state.needfij = false; state.xupdated = false; }
/************************************************************************* NLEQ solver results Buffered implementation of NLEQResults(), which uses pre-allocated buffer to store X[]. If buffer size is too small, it resizes buffer. It is intended to be used in the inner cycles of performance critical algorithms where array reallocation penalty is too large to be ignored. -- ALGLIB -- Copyright 20.08.2009 by Bochkanov Sergey *************************************************************************/ public static void nleqresultsbuf(nleqstate state, ref double[] x, nleqreport rep) { int i_ = 0; if( alglib.ap.len(x)<state.n ) { x = new double[state.n]; } for(i_=0; i_<=state.n-1;i_++) { x[i_] = state.xbase[i_]; } rep.iterationscount = state.repiterationscount; rep.nfunc = state.repnfunc; rep.njac = state.repnjac; rep.terminationtype = state.repterminationtype; }
/************************************************************************* This subroutine restarts CG algorithm from new point. All optimization parameters are left unchanged. This function allows to solve multiple optimization problems (which must have same number of dimensions) without object reallocation penalty. INPUT PARAMETERS: State - structure used for reverse communication previously allocated with MinCGCreate call. X - new starting point. BndL - new lower bounds BndU - new upper bounds -- ALGLIB -- Copyright 30.07.2010 by Bochkanov Sergey *************************************************************************/ public static void nleqrestartfrom(nleqstate state, double[] x) { int i_ = 0; alglib.ap.assert(alglib.ap.len(x)>=state.n, "NLEQRestartFrom: Length(X)<N!"); alglib.ap.assert(apserv.isfinitevector(x, state.n), "NLEQRestartFrom: X contains infinite or NaN values!"); for(i_=0; i_<=state.n-1;i_++) { state.x[i_] = x[i_]; } state.rstate.ia = new int[2+1]; state.rstate.ba = new bool[0+1]; state.rstate.ra = new double[5+1]; state.rstate.stage = -1; clearrequestfields(state); }
/************************************************************************* -- ALGLIB -- Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ public static bool nleqiteration(nleqstate state) { bool result = new bool(); int n = 0; int m = 0; int i = 0; double lambdaup = 0; double lambdadown = 0; double lambdav = 0; double rho = 0; double mu = 0; double stepnorm = 0; bool b = new bool(); int i_ = 0; // // Reverse communication preparations // I know it looks ugly, but it works the same way // anywhere from C++ to Python. // // This code initializes locals by: // * random values determined during code // generation - on first subroutine call // * values from previous call - on subsequent calls // if( state.rstate.stage>=0 ) { n = state.rstate.ia[0]; m = state.rstate.ia[1]; i = state.rstate.ia[2]; b = state.rstate.ba[0]; lambdaup = state.rstate.ra[0]; lambdadown = state.rstate.ra[1]; lambdav = state.rstate.ra[2]; rho = state.rstate.ra[3]; mu = state.rstate.ra[4]; stepnorm = state.rstate.ra[5]; } else { n = -983; m = -989; i = -834; b = false; lambdaup = -287; lambdadown = 364; lambdav = 214; rho = -338; mu = -686; stepnorm = 912; } if( state.rstate.stage==0 ) { goto lbl_0; } if( state.rstate.stage==1 ) { goto lbl_1; } if( state.rstate.stage==2 ) { goto lbl_2; } if( state.rstate.stage==3 ) { goto lbl_3; } if( state.rstate.stage==4 ) { goto lbl_4; } // // Routine body // // // Prepare // n = state.n; m = state.m; state.repterminationtype = 0; state.repiterationscount = 0; state.repnfunc = 0; state.repnjac = 0; // // Calculate F/G, initialize algorithm // clearrequestfields(state); state.needf = true; state.rstate.stage = 0; goto lbl_rcomm; lbl_0: state.needf = false; state.repnfunc = state.repnfunc+1; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.x[i_]; } state.fbase = state.f; state.fprev = math.maxrealnumber; if( !state.xrep ) { goto lbl_5; } // // progress report // clearrequestfields(state); state.xupdated = true; state.rstate.stage = 1; goto lbl_rcomm; lbl_1: state.xupdated = false; lbl_5: if( (double)(state.f)<=(double)(math.sqr(state.epsf)) ) { state.repterminationtype = 1; result = false; return result; } // // Main cycle // lambdaup = 10; lambdadown = 0.3; lambdav = 0.001; rho = 1; lbl_7: if( false ) { goto lbl_8; } // // Get Jacobian; // before we get to this point we already have State.XBase filled // with current point and State.FBase filled with function value // at XBase // clearrequestfields(state); state.needfij = true; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 2; goto lbl_rcomm; lbl_2: state.needfij = false; state.repnfunc = state.repnfunc+1; state.repnjac = state.repnjac+1; ablas.rmatrixmv(n, m, state.j, 0, 0, 1, state.fi, 0, ref state.rightpart, 0); for(i_=0; i_<=n-1;i_++) { state.rightpart[i_] = -1*state.rightpart[i_]; } // // Inner cycle: find good lambda // lbl_9: if( false ) { goto lbl_10; } // // Solve (J^T*J + (Lambda+Mu)*I)*y = J^T*F // to get step d=-y where: // * Mu=||F|| - is damping parameter for nonlinear system // * Lambda - is additional Levenberg-Marquardt parameter // for better convergence when far away from minimum // for(i=0; i<=n-1; i++) { state.candstep[i] = 0; } fbls.fblssolvecgx(state.j, m, n, lambdav, state.rightpart, ref state.candstep, ref state.cgbuf); // // Normalize step (it must be no more than StpMax) // stepnorm = 0; for(i=0; i<=n-1; i++) { if( (double)(state.candstep[i])!=(double)(0) ) { stepnorm = 1; break; } } linmin.linminnormalized(ref state.candstep, ref stepnorm, n); if( (double)(state.stpmax)!=(double)(0) ) { stepnorm = Math.Min(stepnorm, state.stpmax); } // // Test new step - is it good enough? // * if not, Lambda is increased and we try again. // * if step is good, we decrease Lambda and move on. // // We can break this cycle on two occasions: // * step is so small that x+step==x (in floating point arithmetics) // * lambda is so large // for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.x[i_] + stepnorm*state.candstep[i_]; } b = true; for(i=0; i<=n-1; i++) { if( (double)(state.x[i])!=(double)(state.xbase[i]) ) { b = false; break; } } if( b ) { // // Step is too small, force zero step and break // stepnorm = 0; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; goto lbl_10; } clearrequestfields(state); state.needf = true; state.rstate.stage = 3; goto lbl_rcomm; lbl_3: state.needf = false; state.repnfunc = state.repnfunc+1; if( (double)(state.f)<(double)(state.fbase) ) { // // function value decreased, move on // decreaselambda(ref lambdav, ref rho, lambdadown); goto lbl_10; } if( !increaselambda(ref lambdav, ref rho, lambdaup) ) { // // Lambda is too large (near overflow), force zero step and break // stepnorm = 0; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.f = state.fbase; goto lbl_10; } goto lbl_9; lbl_10: // // Accept step: // * new position // * new function value // state.fbase = state.f; for(i_=0; i_<=n-1;i_++) { state.xbase[i_] = state.xbase[i_] + stepnorm*state.candstep[i_]; } state.repiterationscount = state.repiterationscount+1; // // Report new iteration // if( !state.xrep ) { goto lbl_11; } clearrequestfields(state); state.xupdated = true; state.f = state.fbase; for(i_=0; i_<=n-1;i_++) { state.x[i_] = state.xbase[i_]; } state.rstate.stage = 4; goto lbl_rcomm; lbl_4: state.xupdated = false; lbl_11: // // Test stopping conditions on F, step (zero/non-zero) and MaxIts; // If one of the conditions is met, RepTerminationType is changed. // if( (double)(Math.Sqrt(state.f))<=(double)(state.epsf) ) { state.repterminationtype = 1; } if( (double)(stepnorm)==(double)(0) && state.repterminationtype==0 ) { state.repterminationtype = -4; } if( state.repiterationscount>=state.maxits && state.maxits>0 ) { state.repterminationtype = 5; } if( state.repterminationtype!=0 ) { goto lbl_8; } // // Now, iteration is finally over // goto lbl_7; lbl_8: result = false; return result; // // Saving state // lbl_rcomm: result = true; state.rstate.ia[0] = n; state.rstate.ia[1] = m; state.rstate.ia[2] = i; state.rstate.ba[0] = b; state.rstate.ra[0] = lambdaup; state.rstate.ra[1] = lambdadown; state.rstate.ra[2] = lambdav; state.rstate.ra[3] = rho; state.rstate.ra[4] = mu; state.rstate.ra[5] = stepnorm; return result; }
/************************************************************************* NLEQ solver results INPUT PARAMETERS: State - algorithm state. OUTPUT PARAMETERS: X - array[0..N-1], solution Rep - optimization report: * Rep.TerminationType completetion code: * -4 ERROR: algorithm has converged to the stationary point Xf which is local minimum of f=F[0]^2+...+F[m-1]^2, but is not solution of nonlinear system. * 1 sqrt(f)<=EpsF. * 5 MaxIts steps was taken * 7 stopping conditions are too stringent, further improvement is impossible * Rep.IterationsCount contains iterations count * NFEV countains number of function calculations * ActiveConstraints contains number of active constraints -- ALGLIB -- Copyright 20.08.2009 by Bochkanov Sergey *************************************************************************/ public static void nleqresults(nleqstate state, ref double[] x, nleqreport rep) { x = new double[0]; nleqresultsbuf(state, ref x, rep); }
/************************************************************************* This function sets maximum step length INPUT PARAMETERS: State - structure which stores algorithm state StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't want to limit step length. Use this subroutine when target function contains exp() or other fast growing functions, and algorithm makes too large steps which lead to overflow. This function allows us to reject steps that are too large (and therefore expose us to the possible overflow) without actually calculating function value at the x+stp*d. -- ALGLIB -- Copyright 20.08.2010 by Bochkanov Sergey *************************************************************************/ public static void nleqsetstpmax(nleqstate state, double stpmax) { alglib.ap.assert(math.isfinite(stpmax), "NLEQSetStpMax: StpMax is not finite!"); alglib.ap.assert((double)(stpmax)>=(double)(0), "NLEQSetStpMax: StpMax<0!"); state.stpmax = stpmax; }
/************************************************************************* This function turns on/off reporting. INPUT PARAMETERS: State - structure which stores algorithm state NeedXRep- whether iteration reports are needed or not If NeedXRep is True, algorithm will call rep() callback function if it is provided to NLEQSolve(). -- ALGLIB -- Copyright 20.08.2010 by Bochkanov Sergey *************************************************************************/ public static void nleqsetxrep(nleqstate state, bool needxrep) { state.xrep = needxrep; }
/************************************************************************* This function sets stopping conditions for the nonlinear solver INPUT PARAMETERS: State - structure which stores algorithm state EpsF - >=0 The subroutine finishes its work if on k+1-th iteration the condition ||F||<=EpsF is satisfied MaxIts - maximum number of iterations. If MaxIts=0, the number of iterations is unlimited. Passing EpsF=0 and MaxIts=0 simultaneously will lead to automatic stopping criterion selection (small EpsF). NOTES: -- ALGLIB -- Copyright 20.08.2010 by Bochkanov Sergey *************************************************************************/ public static void nleqsetcond(nleqstate state, double epsf, int maxits) { alglib.ap.assert(math.isfinite(epsf), "NLEQSetCond: EpsF is not finite number!"); alglib.ap.assert((double)(epsf)>=(double)(0), "NLEQSetCond: negative EpsF!"); alglib.ap.assert(maxits>=0, "NLEQSetCond: negative MaxIts!"); if( (double)(epsf)==(double)(0) && maxits==0 ) { epsf = 1.0E-6; } state.epsf = epsf; state.maxits = maxits; }
/************************************************************************* LEVENBERG-MARQUARDT-LIKE NONLINEAR SOLVER DESCRIPTION: This algorithm solves system of nonlinear equations F[0](x[0], ..., x[n-1]) = 0 F[1](x[0], ..., x[n-1]) = 0 ... F[M-1](x[0], ..., x[n-1]) = 0 with M/N do not necessarily coincide. Algorithm converges quadratically under following conditions: * the solution set XS is nonempty * for some xs in XS there exist such neighbourhood N(xs) that: * vector function F(x) and its Jacobian J(x) are continuously differentiable on N * ||F(x)|| provides local error bound on N, i.e. there exists such c1, that ||F(x)||>c1*distance(x,XS) Note that these conditions are much more weaker than usual non-singularity conditions. For example, algorithm will converge for any affine function F (whether its Jacobian singular or not). REQUIREMENTS: Algorithm will request following information during its operation: * function vector F[] and Jacobian matrix at given point X * value of merit function f(x)=F[0]^2(x)+...+F[M-1]^2(x) at given point X USAGE: 1. User initializes algorithm state with NLEQCreateLM() call 2. User tunes solver parameters with NLEQSetCond(), NLEQSetStpMax() and other functions 3. User calls NLEQSolve() function which takes algorithm state and pointers (delegates, etc.) to callback functions which calculate merit function value and Jacobian. 4. User calls NLEQResults() to get solution 5. Optionally, user may call NLEQRestartFrom() to solve another problem with same parameters (N/M) but another starting point and/or another function vector. NLEQRestartFrom() allows to reuse already initialized structure. INPUT PARAMETERS: N - space dimension, N>1: * if provided, only leading N elements of X are used * if not provided, determined automatically from size of X M - system size X - starting point OUTPUT PARAMETERS: State - structure which stores algorithm state NOTES: 1. you may tune stopping conditions with NLEQSetCond() function 2. if target function contains exp() or other fast growing functions, and optimization algorithm makes too large steps which leads to overflow, use NLEQSetStpMax() function to bound algorithm's steps. 3. this algorithm is a slightly modified implementation of the method described in 'Levenberg-Marquardt method for constrained nonlinear equations with strong local convergence properties' by Christian Kanzow Nobuo Yamashita and Masao Fukushima and further developed in 'On the convergence of a New Levenberg-Marquardt Method' by Jin-yan Fan and Ya-Xiang Yuan. -- ALGLIB -- Copyright 20.08.2009 by Bochkanov Sergey *************************************************************************/ public static void nleqcreatelm(int n, int m, double[] x, nleqstate state) { alglib.ap.assert(n>=1, "NLEQCreateLM: N<1!"); alglib.ap.assert(m>=1, "NLEQCreateLM: M<1!"); alglib.ap.assert(alglib.ap.len(x)>=n, "NLEQCreateLM: Length(X)<N!"); alglib.ap.assert(apserv.isfinitevector(x, n), "NLEQCreateLM: X contains infinite or NaN values!"); // // Initialize // state.n = n; state.m = m; nleqsetcond(state, 0, 0); nleqsetxrep(state, false); nleqsetstpmax(state, 0); state.x = new double[n]; state.xbase = new double[n]; state.j = new double[m, n]; state.fi = new double[m]; state.rightpart = new double[n]; state.candstep = new double[n]; nleqrestartfrom(state, x); }
public override alglib.apobject make_copy() { nleqstate _result = new nleqstate(); _result.n = n; _result.m = m; _result.epsf = epsf; _result.maxits = maxits; _result.xrep = xrep; _result.stpmax = stpmax; _result.x = (double[])x.Clone(); _result.f = f; _result.fi = (double[])fi.Clone(); _result.j = (double[,])j.Clone(); _result.needf = needf; _result.needfij = needfij; _result.xupdated = xupdated; _result.rstate = (rcommstate)rstate.make_copy(); _result.repiterationscount = repiterationscount; _result.repnfunc = repnfunc; _result.repnjac = repnjac; _result.repterminationtype = repterminationtype; _result.xbase = (double[])xbase.Clone(); _result.fbase = fbase; _result.fprev = fprev; _result.candstep = (double[])candstep.Clone(); _result.rightpart = (double[])rightpart.Clone(); _result.cgbuf = (double[])cgbuf.Clone(); return _result; }