def lmdif(fcn, x0, xmin, xmax, ftol=EPSILON, xtol=EPSILON, gtol=EPSILON, maxfev=None, epsfcn=EPSILON, factor=100.0, numcores=1, verbose=0): class fdJac: def __init__(self, func, fvec, pars): self.func = func self.fvec = fvec epsmch = numpy.float_(numpy.finfo(numpy.float).eps) self.eps = numpy.sqrt(max(epsmch, epsfcn)) self.h = self.calc_h(pars) self.pars = numpy.copy(pars) return def __call__(self, param): wa = self.func(param[1:]) return (wa - self.fvec) / self.h[int(param[0])] def calc_h(self, pars): nn = len(pars) h = numpy.empty((nn, )) for ii in range(nn): h[ii] = self.eps * pars[ii] if h[ii] == 0.0: h[ii] = self.eps if pars[ii] + h[ii] > xmax[ii]: h[ii] = -h[ii] return h def calc_params(self): h = self.calc_h(self.pars) params = [] for ii in range(len(h)): tmp_pars = numpy.copy(self.pars) tmp_pars[ii] += h[ii] tmp_pars = numpy.append(ii, tmp_pars) params.append(tmp_pars) return tuple(params) x, xmin, xmax = _check_args(x0, xmin, xmax) if maxfev is None: maxfev = 256 * len(x) def stat_cb0(pars): return fcn(pars)[0] def stat_cb1(pars): return fcn(pars)[1] def fcn_parallel(pars, fvec): fd_jac = fdJac(stat_cb1, fvec, pars) params = fd_jac.calc_params() fjac = parallel_map(fd_jac, params, numcores) return numpy.concatenate(fjac) num_parallel_map, fcn_parallel_counter = func_counter(fcn_parallel) # TO DO: reduce 1 model eval by passing the resulting 'fvec' to cpp_lmdif m = numpy.asanyarray(stat_cb1(x)).size error = [] n = len(x) fjac = numpy.empty((m * n, )) x, fval, nfev, info, fjac = \ _saoopt.cpp_lmdif(stat_cb1, fcn_parallel_counter, numcores, m, x, ftol, xtol, gtol, maxfev, epsfcn, factor, verbose, xmin, xmax, fjac) if info > 0: fjac = numpy.reshape(numpy.ravel(fjac, order='F'), (m, n), order='F') if m != n: covar = fjac[:n, :n] else: covar = fjac if _par_at_boundary(xmin, x, xmax, xtol): nm_result = neldermead(fcn, x, xmin, xmax, ftol=numpy.sqrt(ftol), maxfev=maxfev - nfev, finalsimplex=2, iquad=0, verbose=0) nfev += nm_result[4]['nfev'] x = nm_result[1] fval = nm_result[2] if error: raise error.pop() if 0 == info: info = 1 elif info >= 1 or info <= 4: info = 0 else: info = 3 status, msg = _get_saofit_msg(maxfev, info) if info == 0: rv = (status, x, fval, msg, { 'info': info, 'nfev': nfev, 'covar': covar, 'num_parallel_map': num_parallel_map[0] }) else: rv = (status, x, fval, msg, { 'info': info, 'nfev': nfev, 'num_parallel_map': num_parallel_map[0] }) return rv
def func( counter, singleparnum, lock=None ): # nfev contains the number of times it was fitted nfev, counter_cb = func_counter( fit_cb ) # # These are the bounds to be returned by this method # conf_int = [ [], [] ] error_flags = [] # # If the user has requested a specific parameter to be # calculated then 'ith_par' represents the index of the # free parameter to deal with. # myargs.ith_par = singleparnum fitcb = translated_fit_cb( counter_cb, myargs ) par_name = get_par_name( myargs.ith_par ) ith_covar_err = get_step_size( error_scales, upper_scales, counter, pars[ myargs.ith_par ] ) trial_points = [ [ ], [ ] ] fitcb = monitor_func( fitcb, trial_points ) bracket = ConfBracket( myargs, trial_points ) # the parameter name is set, may as well get the prefix prefix = get_prefix( counter, par_name, ['-', '+' ] ) myfitcb = [ verbose_fitcb( fitcb, ConfBlog(sherpablog,prefix[0],verbose,lock) ), verbose_fitcb( fitcb, ConfBlog(sherpablog,prefix[1],verbose,lock) ) ] for dir in range( 2 ): # # trial_points stores the history of the points for the # parameter which has been evaluated in order to locate # the root. Note the first point is 'given' since the info # of the minimum is crucial to the search. # bracket.trial_points[0].append( pars[ myargs.ith_par ] ) bracket.trial_points[1].append( - delta_stat ) myblog = ConfBlog( sherpablog, prefix[ dir ], verbose, lock, debug ) # have to set the callback func otherwise disaster. bracket.fcn = myfitcb[ dir ] root = bracket( dir, iter, ith_covar_err, open_interval, maxiters, eps, myblog ) myzero = root( eps, myblog ) delta_zero = get_delta_root( myzero, dir, pars[ myargs.ith_par ] ) conf_int[ dir ].append( delta_zero ) status_prefix = get_prefix( counter, par_name, ['lower bound', 'upper bound' ] ) print_status( myblog.blogger.info, verbose, status_prefix[ dir ], delta_zero, lock ) error_flags.append( est_success ) # # include the minimum point to seperate the -/+ interval # dict[ par_name ] = trial_points return ( conf_int[ 0 ][0], conf_int[ 1 ][0], error_flags[0], nfev[0], None )
def lmdif(fcn, x0, xmin, xmax, ftol=EPSILON, xtol=EPSILON, gtol=EPSILON, maxfev=None, epsfcn=EPSILON, factor=100.0, numcores=1, verbose=0): """Levenberg-Marquardt optimization method. The Levenberg-Marquardt method is an interface to the MINPACK subroutine lmdif to find the local minimum of nonlinear least squares functions of several variables by a modification of the Levenberg-Marquardt algorithm [1]_. Parameters ---------- fcn : function reference Returns the current statistic and per-bin statistic value when given the model parameters. x0, xmin, xmax : sequence of number The starting point, minimum, and maximum values for each parameter. ftol : number The function tolerance to terminate the search for the minimum; the default is FLT_EPSILON ~ 1.19209289551e-07, where FLT_EPSILON is the smallest number x such that ``1.0 != 1.0 + x``. The conditions are satisfied when both the actual and predicted relative reductions in the sum of squares are, at most, ftol. xtol : number The relative error desired in the approximate solution; default is FLT_EPSILON ~ 1.19209289551e-07, where FLT_EPSILON is the smallest number x such that ``1.0 != 1.0 + x``. The conditions are satisfied when the relative error between two consecutive iterates is, at most, `xtol`. gtol : number The orthogonality desired between the function vector and the columns of the jacobian; default is FLT_EPSILON ~ 1.19209289551e-07, where FLT_EPSILON is the smallest number x such that ``1.0 != 1.0 + x``. The conditions are satisfied when the cosine of the angle between fvec and any column of the jacobian is, at most, `gtol` in absolute value. maxfev : int or `None` The maximum number of function evaluations; the default value of `None` means to use ``1024 * n``, where `n` is the number of free parameters. epsfcn : number This is used in determining a suitable step length for the forward-difference approximation; default is FLT_EPSILON ~ 1.19209289551e-07, where FLT_EPSILON is the smallest number x such that ``1.0 != 1.0 + x``. 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. factor : int Used in determining the initial step bound; default is 100. The initial step 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 be from the interval (.1,100.). numcores : int The number of CPU cores to use. The default is `1`. verbose: int The amount of information to print during the fit. The default is `0`, which means no output. References ---------- .. [1] J.J. More, "The Levenberg Marquardt algorithm: implementation and theory," in Lecture Notes in Mathematics 630: Numerical Analysis, G.A. Watson (Ed.), Springer-Verlag: Berlin, 1978, pp.105-116. """ class fdJac: def __init__(self, func, fvec, pars): self.func = func self.fvec = fvec epsmch = numpy.finfo(float).eps self.eps = numpy.sqrt(max(epsmch, epsfcn)) self.h = self.calc_h(pars) self.pars = numpy.copy(pars) return def __call__(self, param): wa = self.func(param[1:]) return (wa - self.fvec) / self.h[int(param[0])] def calc_h(self, pars): nn = len(pars) h = numpy.empty((nn,)) for ii in range(nn): h[ii] = self.eps * pars[ii] if h[ii] == 0.0: h[ii] = self.eps if pars[ii] + h[ii] > xmax[ii]: h[ii] = - h[ii] return h def calc_params(self): h = self.calc_h(self.pars) params = [] for ii in range(len(h)): tmp_pars = numpy.copy(self.pars) tmp_pars[ii] += h[ii] tmp_pars = numpy.append(ii, tmp_pars) params.append(tmp_pars) return tuple(params) x, xmin, xmax = _check_args(x0, xmin, xmax) if maxfev is None: maxfev = 256 * len(x) def stat_cb0(pars): return fcn(pars)[0] def stat_cb1(pars): return fcn(pars)[1] def fcn_parallel(pars, fvec): fd_jac = fdJac(stat_cb1, fvec, pars) params = fd_jac.calc_params() fjac = parallel_map(fd_jac, params, numcores) return numpy.concatenate(fjac) num_parallel_map, fcn_parallel_counter = func_counter(fcn_parallel) # TO DO: reduce 1 model eval by passing the resulting 'fvec' to cpp_lmdif m = numpy.asanyarray(stat_cb1(x)).size error = [] n = len(x) fjac = numpy.empty((m*n,)) x, fval, nfev, info, fjac = \ _saoopt.cpp_lmdif(stat_cb1, fcn_parallel_counter, numcores, m, x, ftol, xtol, gtol, maxfev, epsfcn, factor, verbose, xmin, xmax, fjac) if info > 0: fjac = numpy.reshape(numpy.ravel(fjac, order='F'), (m, n), order='F') if m != n: covar = fjac[:n, :n] else: covar = fjac if _par_at_boundary(xmin, x, xmax, xtol): nm_result = neldermead(fcn, x, xmin, xmax, ftol=numpy.sqrt(ftol), maxfev=maxfev-nfev, finalsimplex=2, iquad=0, verbose=0) nfev += nm_result[4]['nfev'] x = nm_result[1] fval = nm_result[2] if error: raise error.pop() if 0 == info: info = 1 elif info >= 1 or info <= 4: info = 0 else: info = 3 status, msg = _get_saofit_msg(maxfev, info) if info == 0: rv = (status, x, fval, msg, {'info': info, 'nfev': nfev, 'covar': covar, 'num_parallel_map': num_parallel_map[0]}) else: rv = (status, x, fval, msg, {'info': info, 'nfev': nfev, 'num_parallel_map': num_parallel_map[0]}) return rv
def func(counter, singleparnum, lock=None): # nfev contains the number of times it was fitted nfev, counter_cb = func_counter(fit_cb) # # These are the bounds to be returned by this method # conf_int = [[], []] error_flags = [] # # If the user has requested a specific parameter to be # calculated then 'ith_par' represents the index of the # free parameter to deal with. # myargs.ith_par = singleparnum fitcb = translated_fit_cb(counter_cb, myargs) par_name = get_par_name(myargs.ith_par) ith_covar_err = get_step_size(error_scales, upper_scales, counter, pars[myargs.ith_par]) trial_points = [[], []] fitcb = monitor_func(fitcb, trial_points) bracket = ConfBracket(myargs, trial_points) # the parameter name is set, may as well get the prefix prefix = get_prefix(counter, par_name, ['-', '+']) myfitcb = [ verbose_fitcb(fitcb, ConfBlog(sherpablog, prefix[0], verbose, lock)), verbose_fitcb(fitcb, ConfBlog(sherpablog, prefix[1], verbose, lock)) ] for dir in range(2): # # trial_points stores the history of the points for the # parameter which has been evaluated in order to locate # the root. Note the first point is 'given' since the info # of the minimum is crucial to the search. # bracket.trial_points[0].append(pars[myargs.ith_par]) bracket.trial_points[1].append(-delta_stat) myblog = ConfBlog(sherpablog, prefix[dir], verbose, lock, debug) # have to set the callback func otherwise disaster. bracket.fcn = myfitcb[dir] root = bracket(dir, iter, ith_covar_err, open_interval, maxiters, eps, myblog) myzero = root(eps, myblog) delta_zero = get_delta_root(myzero, dir, pars[myargs.ith_par]) conf_int[dir].append(delta_zero) status_prefix = get_prefix(counter, par_name, ['lower bound', 'upper bound']) print_status(myblog.blogger.info, verbose, status_prefix[dir], delta_zero, lock) error_flags.append(est_success) # # include the minimum point to seperate the -/+ interval # dict[par_name] = trial_points return (conf_int[0][0], conf_int[1][0], error_flags[0], nfev[0], None)
def __init__(self, fcn): self.nfev, self.fcn = func_counter(fcn)
def optneldermead( afcn, x0, xmin, xmax, ftol=EPSILON, maxfev=None, initsimplex=0, finalsimplex=None, step=None, multicore=False, verbose=None, ): x, xmin, xmax = _check_args(x0, xmin, xmax) nfev, fcn = func_counter(afcn) if step is None or (numpy.iterable(step) and len(step) != len(x)): step = 1.2 * numpy.ones(x.shape, numpy.float_, numpy.isfortran(x)) elif numpy.isscalar(step): step = step * numpy.ones(x.shape, numpy.float_, numpy.isfortran(x)) if maxfev is None: maxfev = 1024 * len(x) if finalsimplex is None: finalsimplex = [0, 2] if False == is_iterable(finalsimplex): finalsimplex = [finalsimplex] # For internal use only: debug = True def myloop(xxx, mymaxfev, mystep): nms = classicNelderMead(fcn) mynfev = 0 for myfinalsimplex in finalsimplex: starttime = time.time() result = nms( xxx, xmin, xmax, mymaxfev - mynfev, ftol, mystep, initsimplex, myfinalsimplex, multicore, verbose ) mynfev += result[3] if 0 != result[0]: return result[0], result[1], result[2], mynfev if debug: print "neldermead::myloop: result = ", result, " took ", time.time() - starttime, " secs" if multicore: return result[0], result[1], result[2], mynfev + nfev[0] else: return result[0], result[1], result[2], mynfev def myneldermead(xxx, mymaxfev, mystep, fold=numpy.float_(numpy.finfo(numpy.float_).max)): result = myloop(xxx, mymaxfev, mystep) mynfev = result[3] fnew = result[2] if ( fnew < fold * 0.995 and 0 == Knuth_close(fnew, fold, ftol) and 0 == Knuth_close(fnew, fold, ftol) and 0 == result[0] and mynfev < mymaxfev ): if debug: print "neldermead: fnew = ", fnew, "\tfold = ", fold, "\tx = ", result[1] result = myneldermead(result[1], mymaxfev - mynfev, step, fold=fnew) mynfev += result[3] return result[0], result[1], result[2], mynfev # result = myneldermead( x, maxfev, step ) result = myloop(x, maxfev, step) return get_result(result, maxfev)
def func_counter_bounds_wrappers(self, func, npar, xmin, xmax): """Wraps the calls to func_counter then func_bounds""" nfev, afunc = func_counter(func) myfunc = self.func_bounds(afunc, npar, xmin, xmax) return nfev, myfunc
def optneldermead(afcn, x0, xmin, xmax, ftol=EPSILON, maxfev=None, initsimplex=0, finalsimplex=None, step=None, multicore=False, verbose=None): x, xmin, xmax = _check_args(x0, xmin, xmax) nfev, fcn = func_counter(afcn) if step is None or (numpy.iterable(step) and len(step) != len(x)): step = 1.2 * numpy.ones(x.shape, numpy.float_, numpy.isfortran(x)) elif numpy.isscalar(step): step = step * numpy.ones(x.shape, numpy.float_, numpy.isfortran(x)) if maxfev is None: maxfev = 1024 * len(x) if finalsimplex is None: finalsimplex = [0, 2] if False == is_iterable(finalsimplex): finalsimplex = [finalsimplex] # For internal use only: debug = True def myloop(xxx, mymaxfev, mystep): nms = classicNelderMead(fcn) mynfev = 0 for myfinalsimplex in finalsimplex: starttime = time.time() result = nms(xxx, xmin, xmax, mymaxfev - mynfev, ftol, mystep, initsimplex, myfinalsimplex, multicore, verbose) mynfev += result[3] if 0 != result[0]: return result[0], result[1], result[2], mynfev if debug: print 'neldermead::myloop: result = ', result, ' took ', time.time( ) - starttime, ' secs' if multicore: return result[0], result[1], result[2], mynfev + nfev[0] else: return result[0], result[1], result[2], mynfev def myneldermead(xxx, mymaxfev, mystep, fold=numpy.float_(numpy.finfo(numpy.float_).max)): result = myloop(xxx, mymaxfev, mystep) mynfev = result[3] fnew = result[2] if fnew < fold * 0.995 and 0 == Knuth_close( fnew, fold, ftol ) and \ 0 == Knuth_close( fnew, fold, ftol ) and \ 0 == result[0] and mynfev < mymaxfev: if debug: print 'neldermead: fnew = ', fnew, '\tfold = ', fold, \ '\tx = ', result[ 1 ] result = myneldermead(result[1], mymaxfev - mynfev, step, fold=fnew) mynfev += result[3] return result[0], result[1], result[2], mynfev #result = myneldermead( x, maxfev, step ) result = myloop(x, maxfev, step) return get_result(result, maxfev)