Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweaks to minimize() behavior, for discussion #217

Merged
merged 11 commits into from Jun 9, 2012
61 changes: 40 additions & 21 deletions scipy/optimize/_minimize.py
Expand Up @@ -28,8 +28,8 @@
from slsqp import _minimize_slsqp

def minimize(fun, x0, args=(), method='BFGS', jac=None, hess=None,
hessp=None, bounds=None, constraints=(),
options=None, callback=None):
hessp=None, bounds=None, constraints=(), tol=None,
callback=None, options=None):
"""
Minimization of scalar function of one or more variables.

Expand Down Expand Up @@ -92,6 +92,9 @@ def minimize(fun, x0, args=(), method='BFGS', jac=None, hess=None,
Equality constraint means that the constraint function result is to
be zero whereas inequality means that it is to be non-negative.
Note that COBYLA only supports inequality constraints.
tol : float, optional
Tolerance for termination. For detailed control, use solver-specific
options.
options : dict, optional
A dictionary of solver options. All methods accept the following
generic options:
Expand Down Expand Up @@ -318,34 +321,47 @@ def minimize(fun, x0, args=(), method='BFGS', jac=None, hess=None,
else:
jac = None

# set default tolerances
if tol is not None:
options = dict(options)
if meth in ['nelder-mead', 'newton-cg', 'powell', 'tnc']:
options.setdefault('xtol', tol)
if meth in ['nelder-mead', 'powell', 'anneal', 'l-bfgs-b', 'tnc',
'slsqp']:
options.setdefault('ftol', tol)
if meth in ['bfgs', 'cg', 'l-bfgs-b', 'tnc']:
options.setdefault('gtol', tol)
if meth in ['cobyla']:
options.setdefault('tol', tol)

if meth == 'nelder-mead':
return _minimize_neldermead(fun, x0, args, options, callback)
return _minimize_neldermead(fun, x0, args, callback, **options)
elif meth == 'powell':
return _minimize_powell(fun, x0, args, options, callback)
return _minimize_powell(fun, x0, args, callback, **options)
elif meth == 'cg':
return _minimize_cg(fun, x0, args, jac, options, callback)
return _minimize_cg(fun, x0, args, jac, callback, **options)
elif meth == 'bfgs':
return _minimize_bfgs(fun, x0, args, jac, options, callback)
return _minimize_bfgs(fun, x0, args, jac, callback, **options)
elif meth == 'newton-cg':
return _minimize_newtoncg(fun, x0, args, jac, hess, hessp, options,
callback)
return _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,
**options)
elif meth == 'anneal':
return _minimize_anneal(fun, x0, args, options)
return _minimize_anneal(fun, x0, args, **options)
elif meth == 'l-bfgs-b':
return _minimize_lbfgsb(fun, x0, args, jac, bounds, options)
return _minimize_lbfgsb(fun, x0, args, jac, bounds, **options)
elif meth == 'tnc':
return _minimize_tnc(fun, x0, args, jac, bounds, options)
return _minimize_tnc(fun, x0, args, jac, bounds, **options)
elif meth == 'cobyla':
return _minimize_cobyla(fun, x0, args, constraints, options)
return _minimize_cobyla(fun, x0, args, constraints, **options)
elif meth == 'slsqp':
return _minimize_slsqp(fun, x0, args, jac, bounds,
constraints, options)
constraints, **options)
else:
raise ValueError('Unknown solver %s' % method)


def minimize_scalar(fun, bracket=None, bounds=None, args=(),
method='brent', options=None):
method='brent', tol=None, options=None):
"""
Minimization of scalar function of one variable.

Expand Down Expand Up @@ -374,14 +390,14 @@ def minimize_scalar(fun, bracket=None, bounds=None, args=(),
- 'Brent'
- 'Bounded'
- 'Golden'

tol : float, optional
Tolerance for termination. For detailed control, use solver-specific
options.
options : dict, optional
A dictionary of solver options.
xtol : float
Relative error in solution `xopt` acceptable for
convergence.
ftol : float
Relative error in ``fun(xopt)`` acceptable for convergence.
maxiter : int
Maximum number of iterations to perform.
disp : bool
Expand Down Expand Up @@ -443,15 +459,18 @@ def minimize_scalar(fun, bracket=None, bounds=None, args=(),
if options is None:
options = {}

if tol is not None:
options = dict(options)
options.setdefault('xtol', tol)

if meth == 'brent':
return _minimize_scalar_brent(fun, bracket, args, options)
return _minimize_scalar_brent(fun, bracket, args, **options)
elif meth == 'bounded':
if bounds is None:
raise ValueError('The `bounds` parameter is mandatory for '
'method `bounded`.')
return _minimize_scalar_bounded(fun, bounds, args, options)
return _minimize_scalar_bounded(fun, bounds, args, **options)
elif meth == 'golden':
return _minimize_scalar_golden(fun, bracket, args, options)
return _minimize_scalar_golden(fun, bracket, args, **options)
else:
raise ValueError('Unknown solver %s' % method)

158 changes: 91 additions & 67 deletions scipy/optimize/_root.py
Expand Up @@ -8,14 +8,16 @@

__all__ = ['root']

import numpy as np

from warnings import warn

from optimize import MemoizeJac, Result
from optimize import MemoizeJac, Result, _check_unknown_options
from minpack import _root_hybr, leastsq
import nonlin

def root(fun, x0, args=(), method='hybr', jac=None, options=None,
callback=None):
def root(fun, x0, args=(), method='hybr', jac=None, tol=None, callback=None,
options=None):
"""
Find a root of a vector function.

Expand Down Expand Up @@ -48,13 +50,16 @@ def root(fun, x0, args=(), method='hybr', jac=None, options=None,
Jacobian will be estimated numerically.
`jac` can also be a callable returning the Jacobian of `fun`. In
this case, it must accept the same arguments as `fun`.
options : dict, optional
A dictionary of solver options. E.g. `xtol` or `maxiter`, see
``show_options('root', method)`` for details.
tol : float, optional
Tolerance for termination. For detailed control, use solver-specific
options.
callback : function, optional
Optional callback function. It is called on every iteration as
``callback(x, f)`` where `x` is the current solution and `f`
the corresponding residual. For all methods but 'hybr' and 'lm'.
options : dict, optional
A dictionary of solver options. E.g. `xtol` or `maxiter`, see
``show_options('root', method)`` for details.

Returns
-------
Expand Down Expand Up @@ -147,76 +152,95 @@ def root(fun, x0, args=(), method='hybr', jac=None, options=None,
else:
jac = None

# set default tolerances
if tol is not None:
options = dict(options)
if meth in ('hybr', 'lm'):
options.setdefault('xtol', tol)
elif meth in ('broyden1', 'broyden2', 'anderson', 'linearmixing',
'diagbroyden', 'excitingmixing', 'krylov'):
options.setdefault('xtol', tol)
options.setdefault('xatol', np.inf)
options.setdefault('ftol', np.inf)
options.setdefault('fatol', np.inf)

if meth == 'hybr':
sol = _root_hybr(fun, x0, args=args, jac=jac, options=options)
sol = _root_hybr(fun, x0, args=args, jac=jac, **options)
elif meth == 'lm':
col_deriv = options.get('col_deriv', 0)
xtol = options.get('xtol', 1.49012e-08)
ftol = options.get('ftol', 1.49012e-08)
gtol = options.get('gtol', 0.0)
maxfev = options.get('maxfev', 0)
epsfcn = options.get('epsfcn', 0.0)
factor = options.get('factor', 100)
diag = options.get('diag', None)
x, cov_x, info, msg, ier = leastsq(fun, x0, args=args, Dfun=jac,
full_output=True,
col_deriv=col_deriv, xtol=xtol,
ftol=ftol, gtol=gtol,
maxfev=maxfev, epsfcn=epsfcn,
factor=factor, diag=diag)
sol = Result(x=x, message=msg, status=ier,
success=ier in (1, 2, 3, 4), cov_x=cov_x,
fun=info.pop('fvec'))
sol.update(info)
sol = _root_leastsq(fun, x0, args=args, jac=jac, **options)
elif meth in ('broyden1', 'broyden2', 'anderson', 'linearmixing',
'diagbroyden', 'excitingmixing', 'krylov'):
if jac is not None:
warn('Method %s does not use the jacobian (jac).' % method,
RuntimeWarning)

jacobian = {'broyden1': nonlin.BroydenFirst,
'broyden2': nonlin.BroydenSecond,
'anderson': nonlin.Anderson,
'linearmixing': nonlin.LinearMixing,
'diagbroyden': nonlin.DiagBroyden,
'excitingmixing': nonlin.ExcitingMixing,
'krylov': nonlin.KrylovJacobian
}[meth]

nit = options.get('nit')
verbose = options.get('disp', False)
maxiter = options.get('maxiter')
f_tol = options.get('ftol')
f_rtol = options.get('frtol')
x_tol = options.get('xtol')
x_rtol = options.get('xrtol')
tol_norm = options.get('tol_norm')
line_search = options.get('line_search', 'armijo')

jac_opts = options.get('jac_options', dict())

if args:
def f(x):
if jac == True:
r = fun(x, *args)[0]
else:
r = fun(x, *args)
return r
else:
f = fun

x, info = nonlin.nonlin_solve(f, x0, jacobian=jacobian(**jac_opts),
iter=nit, verbose=verbose,
maxiter=maxiter, f_tol=f_tol,
f_rtol=f_rtol, x_tol=x_tol,
x_rtol=x_rtol, tol_norm=tol_norm,
line_search=line_search,
callback=callback, full_output=True,
raise_exception=False)
sol = Result(x=x)
sol.update(info)
sol = _root_nonlin_solve(fun, x0, args=args, jac=jac,
_method=meth, _callback=callback,
**options)
else:
raise ValueError('Unknown solver %s' % method)

return sol

def _root_leastsq(func, x0, args=(), jac=None,
col_deriv=0, xtol=1.49012e-08, ftol=1.49012e-08,
gtol=0.0, maxiter=0, eps=0.0, factor=100, diag=None,
**unknown_options):
_check_unknown_options(unknown_options)
x, cov_x, info, msg, ier = leastsq(func, x0, args=args, Dfun=jac,
full_output=True,
col_deriv=col_deriv, xtol=xtol,
ftol=ftol, gtol=gtol,
maxfev=maxiter, epsfcn=eps,
factor=factor, diag=diag)
sol = Result(x=x, message=msg, status=ier,
success=ier in (1, 2, 3, 4), cov_x=cov_x,
fun=info.pop('fvec'))
sol.update(info)
return sol

def _root_nonlin_solve(func, x0, args=(), jac=None,
_callback=None, _method=None,
nit=None, disp=False, maxiter=None,
ftol=None, fatol=None, xtol=None, xatol=None,
tol_norm=None, line_search='armijo', jac_options=None,
**unknown_options):
_check_unknown_options(unknown_options)

f_tol = fatol
f_rtol = ftol
x_tol = xatol
x_rtol = xtol
verbose = disp
if jac_options is None:
jac_options = dict()

jacobian = {'broyden1': nonlin.BroydenFirst,
'broyden2': nonlin.BroydenSecond,
'anderson': nonlin.Anderson,
'linearmixing': nonlin.LinearMixing,
'diagbroyden': nonlin.DiagBroyden,
'excitingmixing': nonlin.ExcitingMixing,
'krylov': nonlin.KrylovJacobian
}[_method]

if args:
if jac == True:
def f(x):
return func(x, *args)[0]
else:
def f(x):
return func(x, *args)
else:
f = func

x, info = nonlin.nonlin_solve(f, x0, jacobian=jacobian(**jac_options),
iter=nit, verbose=verbose,
maxiter=maxiter, f_tol=f_tol,
f_rtol=f_rtol, x_tol=x_tol,
x_rtol=x_rtol, tol_norm=tol_norm,
line_search=line_search,
callback=_callback, full_output=True,
raise_exception=False)
sol = Result(x=x)
sol.update(info)
return sol
34 changes: 11 additions & 23 deletions scipy/optimize/anneal.py
Expand Up @@ -5,7 +5,7 @@
import numpy
from numpy import asarray, tan, exp, ones, squeeze, sign, \
all, log, sqrt, pi, shape, array, minimum, where, random
from optimize import Result
from optimize import Result, _check_unknown_options

__all__ = ['anneal']

Expand Down Expand Up @@ -303,15 +303,20 @@ def anneal(func, x0, args=(), schedule='fast', full_output=0,
'dwell' : dwell,
'disp' : disp}

res = _minimize_anneal(func, x0, args, opts)
res = _minimize_anneal(func, x0, args, **opts)

if full_output:
return res['x'], res['fun'], res['T'], res['nfev'], res['nit'], \
res['accept'], res['status']
else:
return res['x'], res['status']

def _minimize_anneal(func, x0, args=(), options=None):
def _minimize_anneal(func, x0, args=(),
schedule='fast', T0=None, Tf=1e-12, maxfev=None,
maxaccept=None, maxiter=400, boltzmann=1.0, learn_rate=0.5,
ftol=1e-6, quench=1.0, m=1.0, n=1.0, lower=-100,
upper=100, dwell=50, disp=False,
**unknown_options):
"""
Minimization of scalar function of one or more variables using the
simulated annealing algorithm.
Expand Down Expand Up @@ -350,26 +355,9 @@ def _minimize_anneal(func, x0, args=(), options=None):
This function is called by the `minimize` function with
`method=anneal`. It is not supposed to be called directly.
"""
if options is None:
options = {}
# retrieve useful options
schedule = options.get('schedule', 'fast')
T0 = options.get('T0')
Tf = options.get('Tf', 1e-12)
maxeval = options.get('maxfev')
maxaccept = options.get('maxaccept')
maxiter = options.get('maxiter', 400)
boltzmann = options.get('boltzmann', 1.0)
learn_rate = options.get('learn_rate', 0.5)
feps = options.get('ftol', 1e-6)
quench = options.get('quench', 1.0)
m = options.get('m', 1.0)
n = options.get('n', 1.0)
lower = options.get('lower', -100)
upper = options.get('upper', 100)
dwell = options.get('dwell', 50)
disp = options.get('disp', False)

_check_unknown_options(unknown_options)
maxeval = maxfev
feps = ftol

x0 = asarray(x0)
lower = asarray(lower)
Expand Down