/// <summary> /// Calls the managed lmmin API ported from the lmfit package /// </summary> /// <param name="f">The user supplied function to update the residue vector</param> /// <param name="parameters">initial guess for the parameters</param> /// <param name="mData">Number of data points == number of equations == length of the residue vector</param> /// <returns>Optimization outcome and optimal paramters, if successful</returns> private OptimizationResult CallManagedSolver(Action <double[], double[]> f, double[] parameters, int mData) { // build control structure understood by lmmin LMControlStruct ctrl = new LMControlStruct { ftol = this.Ftol, gtol = this.Gtol, xtol = this.Xtol, patience = this.Patience, epsilon = this.Epsilon, msgfile = IntPtr.Zero, m_maxpri = -1, n_maxpri = -1, scale_diag = this.ScaleDiagonal ? 1 : 0, stepbound = this.InitialStepbound, verbosity = this.VerboseOutput ? 3 : 0 }; LMStatusStruct stat = new LMStatusStruct(); // LMMin modifies optimizedPars => copy parameters var optimizedPars = new double[parameters.Length]; parameters.CopyTo(optimizedPars, 0); LMMinManaged.LMMin(parameters.Length, optimizedPars, mData, f, ref ctrl, ref stat); // extract results from lmmin's result data struct return(new OptimizationResult( optimizedPars, stat.fnorm, stat.nfev, (SolverStatus)stat.outcome, LMSolver.outcomeMessages[stat.outcome], stat.userbreak > 0 ? true : false )); }
/// <summary> /// Calls the native lmmin API from the lmfit package /// </summary> /// <param name="f">The user supplied function to update the residue vector</param> /// <param name="parameters">initial guess for the parameters</param> /// <param name="mData">Number of data points == number of equations == length of the residue vector</param> /// <returns>Optimization outcome and optimal paramters, if successful</returns> private OptimizationResult CallNativeSolver( Action <double[], double[]> f, double[] parameters, int mData) { // build control structure understood by lmmin LMControlStruct ctrl = new LMControlStruct { ftol = this.Ftol, gtol = this.Gtol, xtol = this.Xtol, patience = this.Patience, epsilon = this.Epsilon, msgfile = IntPtr.Zero, m_maxpri = -1, n_maxpri = -1, scale_diag = this.ScaleDiagonal ? 1 : 0, stepbound = this.InitialStepbound, verbosity = this.VerboseOutput ? 3 : 0 }; LMStatusStruct stat = new LMStatusStruct(); OptimizationResult result = null; using (var pool = new PinnedArrayPool <double>()) { // optimizedPars must be pinned, because // the first callback-call (== call to f) passes a // pointer to optimizedPars in "par" parameter ("working space") var optimizedPars = pool.AllocatePinnedArray(parameters.Length); parameters.CopyTo(optimizedPars, 0); // call native lmmin from lmfit package (modifies optimizedPars) LMFit.lmmin( optimizedPars.Array.Length, optimizedPars.Address, mData, IntPtr.Zero, // translate Action<double[], double[]> to LMDelegate: (par, m_dat, dataPtr, fvec, userbreak) => f(pool[par], pool[fvec]), ref ctrl, ref stat, pool.Calloc, pool.Free); // extract results from lmmin's result data struct result = new OptimizationResult( optimizedPars, stat.fnorm, stat.nfev, (SolverStatus)stat.outcome, LMSolver.outcomeMessages[stat.outcome], stat.userbreak > 0 ? true : false ); // pinned managed arrays allocated by lmmin may be garbage collected // starting from here (if unpinned and not referenced anymore) pool.UnpinArray(optimizedPars); GC.KeepAlive(pool); // really neccessary? } return(result); }