예제 #1
0
파일: optfcts.py 프로젝트: nplee/sherpa
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
예제 #2
0
    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 )
예제 #3
0
파일: optfcts.py 프로젝트: wmclaugh/sherpa
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
예제 #4
0
    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)
예제 #5
0
 def __init__(self, fcn):
     self.nfev, self.fcn = func_counter(fcn)
예제 #6
0
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)
예제 #7
0
 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
예제 #8
0
 def __init__(self, fcn):
     self.nfev, self.fcn = func_counter(fcn)
예제 #9
0
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)