| """ |
| Unified interfaces to minimization algorithms. |
| |
| Functions |
| --------- |
| - minimize : minimization of a function of several variables. |
| - minimize_scalar : minimization of a function of one variable. |
| """ |
|
|
| __all__ = ['minimize', 'minimize_scalar'] |
|
|
|
|
| from warnings import warn |
|
|
| import numpy as np |
|
|
| |
| from ._optimize import (_minimize_neldermead, _minimize_powell, _minimize_cg, |
| _minimize_bfgs, _minimize_newtoncg, |
| _minimize_scalar_brent, _minimize_scalar_bounded, |
| _minimize_scalar_golden, MemoizeJac, OptimizeResult, |
| _wrap_callback, _recover_from_bracket_error) |
| from ._trustregion_dogleg import _minimize_dogleg |
| from ._trustregion_ncg import _minimize_trust_ncg |
| from ._trustregion_krylov import _minimize_trust_krylov |
| from ._trustregion_exact import _minimize_trustregion_exact |
| from ._trustregion_constr import _minimize_trustregion_constr |
|
|
| |
| from ._lbfgsb_py import _minimize_lbfgsb |
| from ._tnc import _minimize_tnc |
| from ._cobyla_py import _minimize_cobyla |
| from ._cobyqa_py import _minimize_cobyqa |
| from ._slsqp_py import _minimize_slsqp |
| from ._constraints import (old_bound_to_new, new_bounds_to_old, |
| old_constraint_to_new, new_constraint_to_old, |
| NonlinearConstraint, LinearConstraint, Bounds, |
| PreparedConstraint) |
| from ._differentiable_functions import FD_METHODS |
|
|
| MINIMIZE_METHODS = ['nelder-mead', 'powell', 'cg', 'bfgs', 'newton-cg', |
| 'l-bfgs-b', 'tnc', 'cobyla', 'cobyqa', 'slsqp', |
| 'trust-constr', 'dogleg', 'trust-ncg', 'trust-exact', |
| 'trust-krylov'] |
|
|
| |
| MINIMIZE_METHODS_NEW_CB = ['nelder-mead', 'powell', 'cg', 'bfgs', 'newton-cg', |
| 'l-bfgs-b', 'trust-constr', 'dogleg', 'trust-ncg', |
| 'trust-exact', 'trust-krylov', 'cobyqa'] |
|
|
| MINIMIZE_SCALAR_METHODS = ['brent', 'bounded', 'golden'] |
|
|
| def minimize(fun, x0, args=(), method=None, jac=None, hess=None, |
| hessp=None, bounds=None, constraints=(), tol=None, |
| callback=None, options=None): |
| """Minimization of scalar function of one or more variables. |
| |
| Parameters |
| ---------- |
| fun : callable |
| The objective function to be minimized:: |
| |
| fun(x, *args) -> float |
| |
| where ``x`` is a 1-D array with shape (n,) and ``args`` |
| is a tuple of the fixed parameters needed to completely |
| specify the function. |
| |
| Suppose the callable has signature ``f0(x, *my_args, **my_kwargs)``, where |
| ``my_args`` and ``my_kwargs`` are required positional and keyword arguments. |
| Rather than passing ``f0`` as the callable, wrap it to accept |
| only ``x``; e.g., pass ``fun=lambda x: f0(x, *my_args, **my_kwargs)`` as the |
| callable, where ``my_args`` (tuple) and ``my_kwargs`` (dict) have been |
| gathered before invoking this function. |
| x0 : ndarray, shape (n,) |
| Initial guess. Array of real elements of size (n,), |
| where ``n`` is the number of independent variables. |
| args : tuple, optional |
| Extra arguments passed to the objective function and its |
| derivatives (`fun`, `jac` and `hess` functions). |
| method : str or callable, optional |
| Type of solver. Should be one of |
| |
| - 'Nelder-Mead' :ref:`(see here) <optimize.minimize-neldermead>` |
| - 'Powell' :ref:`(see here) <optimize.minimize-powell>` |
| - 'CG' :ref:`(see here) <optimize.minimize-cg>` |
| - 'BFGS' :ref:`(see here) <optimize.minimize-bfgs>` |
| - 'Newton-CG' :ref:`(see here) <optimize.minimize-newtoncg>` |
| - 'L-BFGS-B' :ref:`(see here) <optimize.minimize-lbfgsb>` |
| - 'TNC' :ref:`(see here) <optimize.minimize-tnc>` |
| - 'COBYLA' :ref:`(see here) <optimize.minimize-cobyla>` |
| - 'COBYQA' :ref:`(see here) <optimize.minimize-cobyqa>` |
| - 'SLSQP' :ref:`(see here) <optimize.minimize-slsqp>` |
| - 'trust-constr':ref:`(see here) <optimize.minimize-trustconstr>` |
| - 'dogleg' :ref:`(see here) <optimize.minimize-dogleg>` |
| - 'trust-ncg' :ref:`(see here) <optimize.minimize-trustncg>` |
| - 'trust-exact' :ref:`(see here) <optimize.minimize-trustexact>` |
| - 'trust-krylov' :ref:`(see here) <optimize.minimize-trustkrylov>` |
| - custom - a callable object, see below for description. |
| |
| If not given, chosen to be one of ``BFGS``, ``L-BFGS-B``, ``SLSQP``, |
| depending on whether or not the problem has constraints or bounds. |
| jac : {callable, '2-point', '3-point', 'cs', bool}, optional |
| Method for computing the gradient vector. Only for CG, BFGS, |
| Newton-CG, L-BFGS-B, TNC, SLSQP, dogleg, trust-ncg, trust-krylov, |
| trust-exact and trust-constr. |
| If it is a callable, it should be a function that returns the gradient |
| vector:: |
| |
| jac(x, *args) -> array_like, shape (n,) |
| |
| where ``x`` is an array with shape (n,) and ``args`` is a tuple with |
| the fixed parameters. If `jac` is a Boolean and is True, `fun` is |
| assumed to return a tuple ``(f, g)`` containing the objective |
| function and the gradient. |
| Methods 'Newton-CG', 'trust-ncg', 'dogleg', 'trust-exact', and |
| 'trust-krylov' require that either a callable be supplied, or that |
| `fun` return the objective and gradient. |
| If None or False, the gradient will be estimated using 2-point finite |
| difference estimation with an absolute step size. |
| Alternatively, the keywords {'2-point', '3-point', 'cs'} can be used |
| to select a finite difference scheme for numerical estimation of the |
| gradient with a relative step size. These finite difference schemes |
| obey any specified `bounds`. |
| hess : {callable, '2-point', '3-point', 'cs', HessianUpdateStrategy}, optional |
| Method for computing the Hessian matrix. Only for Newton-CG, dogleg, |
| trust-ncg, trust-krylov, trust-exact and trust-constr. |
| If it is callable, it should return the Hessian matrix:: |
| |
| hess(x, *args) -> {LinearOperator, spmatrix, array}, (n, n) |
| |
| where ``x`` is a (n,) ndarray and ``args`` is a tuple with the fixed |
| parameters. |
| The keywords {'2-point', '3-point', 'cs'} can also be used to select |
| a finite difference scheme for numerical estimation of the hessian. |
| Alternatively, objects implementing the `HessianUpdateStrategy` |
| interface can be used to approximate the Hessian. Available |
| quasi-Newton methods implementing this interface are: |
| |
| - `BFGS` |
| - `SR1` |
| |
| Not all of the options are available for each of the methods; for |
| availability refer to the notes. |
| hessp : callable, optional |
| Hessian of objective function times an arbitrary vector p. Only for |
| Newton-CG, trust-ncg, trust-krylov, trust-constr. |
| Only one of `hessp` or `hess` needs to be given. If `hess` is |
| provided, then `hessp` will be ignored. `hessp` must compute the |
| Hessian times an arbitrary vector:: |
| |
| hessp(x, p, *args) -> ndarray shape (n,) |
| |
| where ``x`` is a (n,) ndarray, ``p`` is an arbitrary vector with |
| dimension (n,) and ``args`` is a tuple with the fixed |
| parameters. |
| bounds : sequence or `Bounds`, optional |
| Bounds on variables for Nelder-Mead, L-BFGS-B, TNC, SLSQP, Powell, |
| trust-constr, COBYLA, and COBYQA methods. There are two ways to specify |
| the bounds: |
| |
| 1. Instance of `Bounds` class. |
| 2. Sequence of ``(min, max)`` pairs for each element in `x`. None |
| is used to specify no bound. |
| |
| constraints : {Constraint, dict} or List of {Constraint, dict}, optional |
| Constraints definition. Only for COBYLA, COBYQA, SLSQP and trust-constr. |
| |
| Constraints for 'trust-constr' and 'cobyqa' are defined as a single object |
| or a list of objects specifying constraints to the optimization problem. |
| Available constraints are: |
| |
| - `LinearConstraint` |
| - `NonlinearConstraint` |
| |
| Constraints for COBYLA, SLSQP are defined as a list of dictionaries. |
| Each dictionary with fields: |
| |
| type : str |
| Constraint type: 'eq' for equality, 'ineq' for inequality. |
| fun : callable |
| The function defining the constraint. |
| jac : callable, optional |
| The Jacobian of `fun` (only for SLSQP). |
| args : sequence, optional |
| Extra arguments to be passed to the function and Jacobian. |
| |
| 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. When `tol` is specified, the selected |
| minimization algorithm sets some relevant solver-specific tolerance(s) |
| equal to `tol`. For detailed control, use solver-specific |
| options. |
| options : dict, optional |
| A dictionary of solver options. All methods except `TNC` accept the |
| following generic options: |
| |
| maxiter : int |
| Maximum number of iterations to perform. Depending on the |
| method each iteration may use several function evaluations. |
| |
| For `TNC` use `maxfun` instead of `maxiter`. |
| disp : bool |
| Set to True to print convergence messages. |
| |
| For method-specific options, see :func:`show_options()`. |
| callback : callable, optional |
| A callable called after each iteration. |
| |
| All methods except TNC, SLSQP, and COBYLA support a callable with |
| the signature:: |
| |
| callback(intermediate_result: OptimizeResult) |
| |
| where ``intermediate_result`` is a keyword parameter containing an |
| `OptimizeResult` with attributes ``x`` and ``fun``, the present values |
| of the parameter vector and objective function. Note that the name |
| of the parameter must be ``intermediate_result`` for the callback |
| to be passed an `OptimizeResult`. These methods will also terminate if |
| the callback raises ``StopIteration``. |
| |
| All methods except trust-constr (also) support a signature like:: |
| |
| callback(xk) |
| |
| where ``xk`` is the current parameter vector. |
| |
| Introspection is used to determine which of the signatures above to |
| invoke. |
| |
| Returns |
| ------- |
| res : OptimizeResult |
| The optimization result represented as a ``OptimizeResult`` object. |
| Important attributes are: ``x`` the solution array, ``success`` a |
| Boolean flag indicating if the optimizer exited successfully and |
| ``message`` which describes the cause of the termination. See |
| `OptimizeResult` for a description of other attributes. |
| |
| See also |
| -------- |
| minimize_scalar : Interface to minimization algorithms for scalar |
| univariate functions |
| show_options : Additional options accepted by the solvers |
| |
| Notes |
| ----- |
| This section describes the available solvers that can be selected by the |
| 'method' parameter. The default method is *BFGS*. |
| |
| **Unconstrained minimization** |
| |
| Method :ref:`CG <optimize.minimize-cg>` uses a nonlinear conjugate |
| gradient algorithm by Polak and Ribiere, a variant of the |
| Fletcher-Reeves method described in [5]_ pp.120-122. Only the |
| first derivatives are used. |
| |
| Method :ref:`BFGS <optimize.minimize-bfgs>` uses the quasi-Newton |
| method of Broyden, Fletcher, Goldfarb, and Shanno (BFGS) [5]_ |
| pp. 136. It uses the first derivatives only. BFGS has proven good |
| performance even for non-smooth optimizations. This method also |
| returns an approximation of the Hessian inverse, stored as |
| `hess_inv` in the OptimizeResult object. |
| |
| Method :ref:`Newton-CG <optimize.minimize-newtoncg>` uses a |
| Newton-CG algorithm [5]_ pp. 168 (also known as the truncated |
| Newton method). It uses a CG method to the compute the search |
| direction. See also *TNC* method for a box-constrained |
| minimization with a similar algorithm. Suitable for large-scale |
| problems. |
| |
| Method :ref:`dogleg <optimize.minimize-dogleg>` uses the dog-leg |
| trust-region algorithm [5]_ for unconstrained minimization. This |
| algorithm requires the gradient and Hessian; furthermore the |
| Hessian is required to be positive definite. |
| |
| Method :ref:`trust-ncg <optimize.minimize-trustncg>` uses the |
| Newton conjugate gradient trust-region algorithm [5]_ for |
| unconstrained minimization. This algorithm requires the gradient |
| and either the Hessian or a function that computes the product of |
| the Hessian with a given vector. Suitable for large-scale problems. |
| |
| Method :ref:`trust-krylov <optimize.minimize-trustkrylov>` uses |
| the Newton GLTR trust-region algorithm [14]_, [15]_ for unconstrained |
| minimization. This algorithm requires the gradient |
| and either the Hessian or a function that computes the product of |
| the Hessian with a given vector. Suitable for large-scale problems. |
| On indefinite problems it requires usually less iterations than the |
| `trust-ncg` method and is recommended for medium and large-scale problems. |
| |
| Method :ref:`trust-exact <optimize.minimize-trustexact>` |
| is a trust-region method for unconstrained minimization in which |
| quadratic subproblems are solved almost exactly [13]_. This |
| algorithm requires the gradient and the Hessian (which is |
| *not* required to be positive definite). It is, in many |
| situations, the Newton method to converge in fewer iterations |
| and the most recommended for small and medium-size problems. |
| |
| **Bound-Constrained minimization** |
| |
| Method :ref:`Nelder-Mead <optimize.minimize-neldermead>` uses the |
| Simplex algorithm [1]_, [2]_. This algorithm is robust in many |
| applications. However, if numerical computation of derivative can be |
| trusted, other algorithms using the first and/or second derivatives |
| information might be preferred for their better performance in |
| general. |
| |
| Method :ref:`L-BFGS-B <optimize.minimize-lbfgsb>` uses the L-BFGS-B |
| algorithm [6]_, [7]_ for bound constrained minimization. |
| |
| Method :ref:`Powell <optimize.minimize-powell>` is a modification |
| of Powell's method [3]_, [4]_ which is a conjugate direction |
| method. It performs sequential one-dimensional minimizations along |
| each vector of the directions set (`direc` field in `options` and |
| `info`), which is updated at each iteration of the main |
| minimization loop. The function need not be differentiable, and no |
| derivatives are taken. If bounds are not provided, then an |
| unbounded line search will be used. If bounds are provided and |
| the initial guess is within the bounds, then every function |
| evaluation throughout the minimization procedure will be within |
| the bounds. If bounds are provided, the initial guess is outside |
| the bounds, and `direc` is full rank (default has full rank), then |
| some function evaluations during the first iteration may be |
| outside the bounds, but every function evaluation after the first |
| iteration will be within the bounds. If `direc` is not full rank, |
| then some parameters may not be optimized and the solution is not |
| guaranteed to be within the bounds. |
| |
| Method :ref:`TNC <optimize.minimize-tnc>` uses a truncated Newton |
| algorithm [5]_, [8]_ to minimize a function with variables subject |
| to bounds. This algorithm uses gradient information; it is also |
| called Newton Conjugate-Gradient. It differs from the *Newton-CG* |
| method described above as it wraps a C implementation and allows |
| each variable to be given upper and lower bounds. |
| |
| **Constrained Minimization** |
| |
| Method :ref:`COBYLA <optimize.minimize-cobyla>` uses the |
| Constrained Optimization BY Linear Approximation (COBYLA) method |
| [9]_, [10]_, [11]_. The algorithm is based on linear |
| approximations to the objective function and each constraint. The |
| method wraps a FORTRAN implementation of the algorithm. The |
| constraints functions 'fun' may return either a single number |
| or an array or list of numbers. |
| |
| Method :ref:`COBYQA <optimize.minimize-cobyqa>` uses the Constrained |
| Optimization BY Quadratic Approximations (COBYQA) method [18]_. The |
| algorithm is a derivative-free trust-region SQP method based on quadratic |
| approximations to the objective function and each nonlinear constraint. The |
| bounds are treated as unrelaxable constraints, in the sense that the |
| algorithm always respects them throughout the optimization process. |
| |
| Method :ref:`SLSQP <optimize.minimize-slsqp>` uses Sequential |
| Least SQuares Programming to minimize a function of several |
| variables with any combination of bounds, equality and inequality |
| constraints. The method wraps the SLSQP Optimization subroutine |
| originally implemented by Dieter Kraft [12]_. Note that the |
| wrapper handles infinite values in bounds by converting them into |
| large floating values. |
| |
| Method :ref:`trust-constr <optimize.minimize-trustconstr>` is a |
| trust-region algorithm for constrained optimization. It switches |
| between two implementations depending on the problem definition. |
| It is the most versatile constrained minimization algorithm |
| implemented in SciPy and the most appropriate for large-scale problems. |
| For equality constrained problems it is an implementation of Byrd-Omojokun |
| Trust-Region SQP method described in [17]_ and in [5]_, p. 549. When |
| inequality constraints are imposed as well, it switches to the trust-region |
| interior point method described in [16]_. This interior point algorithm, |
| in turn, solves inequality constraints by introducing slack variables |
| and solving a sequence of equality-constrained barrier problems |
| for progressively smaller values of the barrier parameter. |
| The previously described equality constrained SQP method is |
| used to solve the subproblems with increasing levels of accuracy |
| as the iterate gets closer to a solution. |
| |
| **Finite-Difference Options** |
| |
| For Method :ref:`trust-constr <optimize.minimize-trustconstr>` |
| the gradient and the Hessian may be approximated using |
| three finite-difference schemes: {'2-point', '3-point', 'cs'}. |
| The scheme 'cs' is, potentially, the most accurate but it |
| requires the function to correctly handle complex inputs and to |
| be differentiable in the complex plane. The scheme '3-point' is more |
| accurate than '2-point' but requires twice as many operations. If the |
| gradient is estimated via finite-differences the Hessian must be |
| estimated using one of the quasi-Newton strategies. |
| |
| **Method specific options for the** `hess` **keyword** |
| |
| +--------------+------+----------+-------------------------+-----+ |
| | method/Hess | None | callable | '2-point/'3-point'/'cs' | HUS | |
| +==============+======+==========+=========================+=====+ |
| | Newton-CG | x | (n, n) | x | x | |
| | | | LO | | | |
| +--------------+------+----------+-------------------------+-----+ |
| | dogleg | | (n, n) | | | |
| +--------------+------+----------+-------------------------+-----+ |
| | trust-ncg | | (n, n) | x | x | |
| +--------------+------+----------+-------------------------+-----+ |
| | trust-krylov | | (n, n) | x | x | |
| +--------------+------+----------+-------------------------+-----+ |
| | trust-exact | | (n, n) | | | |
| +--------------+------+----------+-------------------------+-----+ |
| | trust-constr | x | (n, n) | x | x | |
| | | | LO | | | |
| | | | sp | | | |
| +--------------+------+----------+-------------------------+-----+ |
| |
| where LO=LinearOperator, sp=Sparse matrix, HUS=HessianUpdateStrategy |
| |
| **Custom minimizers** |
| |
| It may be useful to pass a custom minimization method, for example |
| when using a frontend to this method such as `scipy.optimize.basinhopping` |
| or a different library. You can simply pass a callable as the ``method`` |
| parameter. |
| |
| The callable is called as ``method(fun, x0, args, **kwargs, **options)`` |
| where ``kwargs`` corresponds to any other parameters passed to `minimize` |
| (such as `callback`, `hess`, etc.), except the `options` dict, which has |
| its contents also passed as `method` parameters pair by pair. Also, if |
| `jac` has been passed as a bool type, `jac` and `fun` are mangled so that |
| `fun` returns just the function values and `jac` is converted to a function |
| returning the Jacobian. The method shall return an `OptimizeResult` |
| object. |
| |
| The provided `method` callable must be able to accept (and possibly ignore) |
| arbitrary parameters; the set of parameters accepted by `minimize` may |
| expand in future versions and then these parameters will be passed to |
| the method. You can find an example in the scipy.optimize tutorial. |
| |
| References |
| ---------- |
| .. [1] Nelder, J A, and R Mead. 1965. A Simplex Method for Function |
| Minimization. The Computer Journal 7: 308-13. |
| .. [2] Wright M H. 1996. Direct search methods: Once scorned, now |
| respectable, in Numerical Analysis 1995: Proceedings of the 1995 |
| Dundee Biennial Conference in Numerical Analysis (Eds. D F |
| Griffiths and G A Watson). Addison Wesley Longman, Harlow, UK. |
| 191-208. |
| .. [3] Powell, M J D. 1964. An efficient method for finding the minimum of |
| a function of several variables without calculating derivatives. The |
| Computer Journal 7: 155-162. |
| .. [4] Press W, S A Teukolsky, W T Vetterling and B P Flannery. |
| Numerical Recipes (any edition), Cambridge University Press. |
| .. [5] Nocedal, J, and S J Wright. 2006. Numerical Optimization. |
| Springer New York. |
| .. [6] Byrd, R H and P Lu and J. Nocedal. 1995. A Limited Memory |
| Algorithm for Bound Constrained Optimization. SIAM Journal on |
| Scientific and Statistical Computing 16 (5): 1190-1208. |
| .. [7] Zhu, C and R H Byrd and J Nocedal. 1997. L-BFGS-B: Algorithm |
| 778: L-BFGS-B, FORTRAN routines for large scale bound constrained |
| optimization. ACM Transactions on Mathematical Software 23 (4): |
| 550-560. |
| .. [8] Nash, S G. Newton-Type Minimization Via the Lanczos Method. |
| 1984. SIAM Journal of Numerical Analysis 21: 770-778. |
| .. [9] Powell, M J D. A direct search optimization method that models |
| the objective and constraint functions by linear interpolation. |
| 1994. Advances in Optimization and Numerical Analysis, eds. S. Gomez |
| and J-P Hennart, Kluwer Academic (Dordrecht), 51-67. |
| .. [10] Powell M J D. Direct search algorithms for optimization |
| calculations. 1998. Acta Numerica 7: 287-336. |
| .. [11] Powell M J D. A view of algorithms for optimization without |
| derivatives. 2007.Cambridge University Technical Report DAMTP |
| 2007/NA03 |
| .. [12] Kraft, D. A software package for sequential quadratic |
| programming. 1988. Tech. Rep. DFVLR-FB 88-28, DLR German Aerospace |
| Center -- Institute for Flight Mechanics, Koln, Germany. |
| .. [13] Conn, A. R., Gould, N. I., and Toint, P. L. |
| Trust region methods. 2000. Siam. pp. 169-200. |
| .. [14] F. Lenders, C. Kirches, A. Potschka: "trlib: A vector-free |
| implementation of the GLTR method for iterative solution of |
| the trust region problem", :arxiv:`1611.04718` |
| .. [15] N. Gould, S. Lucidi, M. Roma, P. Toint: "Solving the |
| Trust-Region Subproblem using the Lanczos Method", |
| SIAM J. Optim., 9(2), 504--525, (1999). |
| .. [16] Byrd, Richard H., Mary E. Hribar, and Jorge Nocedal. 1999. |
| An interior point algorithm for large-scale nonlinear programming. |
| SIAM Journal on Optimization 9.4: 877-900. |
| .. [17] Lalee, Marucha, Jorge Nocedal, and Todd Plantenga. 1998. On the |
| implementation of an algorithm for large-scale equality constrained |
| optimization. SIAM Journal on Optimization 8.3: 682-706. |
| .. [18] Ragonneau, T. M. *Model-Based Derivative-Free Optimization Methods |
| and Software*. PhD thesis, Department of Applied Mathematics, The Hong |
| Kong Polytechnic University, Hong Kong, China, 2022. URL: |
| https://theses.lib.polyu.edu.hk/handle/200/12294. |
| |
| Examples |
| -------- |
| Let us consider the problem of minimizing the Rosenbrock function. This |
| function (and its respective derivatives) is implemented in `rosen` |
| (resp. `rosen_der`, `rosen_hess`) in the `scipy.optimize`. |
| |
| >>> from scipy.optimize import minimize, rosen, rosen_der |
| |
| A simple application of the *Nelder-Mead* method is: |
| |
| >>> x0 = [1.3, 0.7, 0.8, 1.9, 1.2] |
| >>> res = minimize(rosen, x0, method='Nelder-Mead', tol=1e-6) |
| >>> res.x |
| array([ 1., 1., 1., 1., 1.]) |
| |
| Now using the *BFGS* algorithm, using the first derivative and a few |
| options: |
| |
| >>> res = minimize(rosen, x0, method='BFGS', jac=rosen_der, |
| ... options={'gtol': 1e-6, 'disp': True}) |
| Optimization terminated successfully. |
| Current function value: 0.000000 |
| Iterations: 26 |
| Function evaluations: 31 |
| Gradient evaluations: 31 |
| >>> res.x |
| array([ 1., 1., 1., 1., 1.]) |
| >>> print(res.message) |
| Optimization terminated successfully. |
| >>> res.hess_inv |
| array([ |
| [ 0.00749589, 0.01255155, 0.02396251, 0.04750988, 0.09495377], # may vary |
| [ 0.01255155, 0.02510441, 0.04794055, 0.09502834, 0.18996269], |
| [ 0.02396251, 0.04794055, 0.09631614, 0.19092151, 0.38165151], |
| [ 0.04750988, 0.09502834, 0.19092151, 0.38341252, 0.7664427 ], |
| [ 0.09495377, 0.18996269, 0.38165151, 0.7664427, 1.53713523] |
| ]) |
| |
| |
| Next, consider a minimization problem with several constraints (namely |
| Example 16.4 from [5]_). The objective function is: |
| |
| >>> fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2 |
| |
| There are three constraints defined as: |
| |
| >>> cons = ({'type': 'ineq', 'fun': lambda x: x[0] - 2 * x[1] + 2}, |
| ... {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6}, |
| ... {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2}) |
| |
| And variables must be positive, hence the following bounds: |
| |
| >>> bnds = ((0, None), (0, None)) |
| |
| The optimization problem is solved using the SLSQP method as: |
| |
| >>> res = minimize(fun, (2, 0), method='SLSQP', bounds=bnds, |
| ... constraints=cons) |
| |
| It should converge to the theoretical solution (1.4 ,1.7). |
| |
| """ |
| x0 = np.atleast_1d(np.asarray(x0)) |
|
|
| if x0.ndim != 1: |
| raise ValueError("'x0' must only have one dimension.") |
|
|
| if x0.dtype.kind in np.typecodes["AllInteger"]: |
| x0 = np.asarray(x0, dtype=float) |
|
|
| if not isinstance(args, tuple): |
| args = (args,) |
|
|
| if method is None: |
| |
| if constraints: |
| method = 'SLSQP' |
| elif bounds is not None: |
| method = 'L-BFGS-B' |
| else: |
| method = 'BFGS' |
|
|
| if callable(method): |
| meth = "_custom" |
| else: |
| meth = method.lower() |
|
|
| if options is None: |
| options = {} |
| |
| |
| if meth in ('nelder-mead', 'powell', 'cobyla', 'cobyqa') and bool(jac): |
| warn(f'Method {method} does not use gradient information (jac).', |
| RuntimeWarning, stacklevel=2) |
| |
| if meth not in ('newton-cg', 'dogleg', 'trust-ncg', 'trust-constr', |
| 'trust-krylov', 'trust-exact', '_custom') and hess is not None: |
| warn(f'Method {method} does not use Hessian information (hess).', |
| RuntimeWarning, stacklevel=2) |
| |
| if meth not in ('newton-cg', 'trust-ncg', 'trust-constr', |
| 'trust-krylov', '_custom') \ |
| and hessp is not None: |
| warn(f'Method {method} does not use Hessian-vector product' |
| ' information (hessp).', |
| RuntimeWarning, stacklevel=2) |
| |
| if (meth not in ('cobyla', 'cobyqa', 'slsqp', 'trust-constr', '_custom') and |
| np.any(constraints)): |
| warn(f'Method {method} cannot handle constraints.', |
| RuntimeWarning, stacklevel=2) |
| if meth not in ( |
| 'nelder-mead', 'powell', 'l-bfgs-b', 'cobyla', 'cobyqa', 'slsqp', |
| 'tnc', 'trust-constr', '_custom') and bounds is not None: |
| warn(f'Method {method} cannot handle bounds.', |
| RuntimeWarning, stacklevel=2) |
| |
| if (meth in ('l-bfgs-b', 'tnc', 'cobyla', 'cobyqa', 'slsqp') and |
| options.get('return_all', False)): |
| warn(f'Method {method} does not support the return_all option.', |
| RuntimeWarning, stacklevel=2) |
|
|
| |
| if callable(jac): |
| pass |
| elif jac is True: |
| |
| fun = MemoizeJac(fun) |
| jac = fun.derivative |
| elif (jac in FD_METHODS and |
| meth in ['trust-constr', 'bfgs', 'cg', 'l-bfgs-b', 'tnc', 'slsqp']): |
| |
| pass |
| elif meth in ['trust-constr']: |
| |
| jac = '2-point' |
| elif jac is None or bool(jac) is False: |
| |
| jac = None |
| else: |
| |
| jac = None |
|
|
| |
| if tol is not None: |
| options = dict(options) |
| if meth == 'nelder-mead': |
| options.setdefault('xatol', tol) |
| options.setdefault('fatol', tol) |
| if meth in ('newton-cg', 'powell', 'tnc'): |
| options.setdefault('xtol', tol) |
| if meth in ('powell', 'l-bfgs-b', 'tnc', 'slsqp'): |
| options.setdefault('ftol', tol) |
| if meth in ('bfgs', 'cg', 'l-bfgs-b', 'tnc', 'dogleg', |
| 'trust-ncg', 'trust-exact', 'trust-krylov'): |
| options.setdefault('gtol', tol) |
| if meth in ('cobyla', '_custom'): |
| options.setdefault('tol', tol) |
| if meth == 'cobyqa': |
| options.setdefault('final_tr_radius', tol) |
| if meth == 'trust-constr': |
| options.setdefault('xtol', tol) |
| options.setdefault('gtol', tol) |
| options.setdefault('barrier_tol', tol) |
|
|
| if meth == '_custom': |
| |
| |
| |
| return method(fun, x0, args=args, jac=jac, hess=hess, hessp=hessp, |
| bounds=bounds, constraints=constraints, |
| callback=callback, **options) |
|
|
| constraints = standardize_constraints(constraints, x0, meth) |
|
|
| remove_vars = False |
| if bounds is not None: |
| |
| bounds = standardize_bounds(bounds, x0, 'new') |
| bounds = _validate_bounds(bounds, x0, meth) |
|
|
| if meth in {"tnc", "slsqp", "l-bfgs-b"}: |
| |
| |
| |
| |
| |
| |
|
|
| |
| i_fixed = (bounds.lb == bounds.ub) |
|
|
| if np.all(i_fixed): |
| |
| |
| return _optimize_result_for_equal_bounds( |
| fun, bounds, meth, args=args, constraints=constraints |
| ) |
|
|
| |
| fd_needed = (not callable(jac)) |
| for con in constraints: |
| if not callable(con.get('jac', None)): |
| fd_needed = True |
|
|
| |
| |
| remove_vars = i_fixed.any() and (fd_needed or meth == "tnc") |
| if remove_vars: |
| x_fixed = (bounds.lb)[i_fixed] |
| x0 = x0[~i_fixed] |
| bounds = _remove_from_bounds(bounds, i_fixed) |
| fun = _remove_from_func(fun, i_fixed, x_fixed) |
| if callable(callback): |
| callback = _remove_from_func(callback, i_fixed, x_fixed) |
| if callable(jac): |
| jac = _remove_from_func(jac, i_fixed, x_fixed, remove=1) |
|
|
| |
| |
| constraints = [con.copy() for con in constraints] |
| for con in constraints: |
| con['fun'] = _remove_from_func(con['fun'], i_fixed, |
| x_fixed, min_dim=1, |
| remove=0) |
| if callable(con.get('jac', None)): |
| con['jac'] = _remove_from_func(con['jac'], i_fixed, |
| x_fixed, min_dim=2, |
| remove=1) |
| bounds = standardize_bounds(bounds, x0, meth) |
|
|
| callback = _wrap_callback(callback, meth) |
|
|
| if meth == 'nelder-mead': |
| res = _minimize_neldermead(fun, x0, args, callback, bounds=bounds, |
| **options) |
| elif meth == 'powell': |
| res = _minimize_powell(fun, x0, args, callback, bounds, **options) |
| elif meth == 'cg': |
| res = _minimize_cg(fun, x0, args, jac, callback, **options) |
| elif meth == 'bfgs': |
| res = _minimize_bfgs(fun, x0, args, jac, callback, **options) |
| elif meth == 'newton-cg': |
| res = _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback, |
| **options) |
| elif meth == 'l-bfgs-b': |
| res = _minimize_lbfgsb(fun, x0, args, jac, bounds, |
| callback=callback, **options) |
| elif meth == 'tnc': |
| res = _minimize_tnc(fun, x0, args, jac, bounds, callback=callback, |
| **options) |
| elif meth == 'cobyla': |
| res = _minimize_cobyla(fun, x0, args, constraints, callback=callback, |
| bounds=bounds, **options) |
| elif meth == 'cobyqa': |
| res = _minimize_cobyqa(fun, x0, args, bounds, constraints, callback, |
| **options) |
| elif meth == 'slsqp': |
| res = _minimize_slsqp(fun, x0, args, jac, bounds, |
| constraints, callback=callback, **options) |
| elif meth == 'trust-constr': |
| res = _minimize_trustregion_constr(fun, x0, args, jac, hess, hessp, |
| bounds, constraints, |
| callback=callback, **options) |
| elif meth == 'dogleg': |
| res = _minimize_dogleg(fun, x0, args, jac, hess, |
| callback=callback, **options) |
| elif meth == 'trust-ncg': |
| res = _minimize_trust_ncg(fun, x0, args, jac, hess, hessp, |
| callback=callback, **options) |
| elif meth == 'trust-krylov': |
| res = _minimize_trust_krylov(fun, x0, args, jac, hess, hessp, |
| callback=callback, **options) |
| elif meth == 'trust-exact': |
| res = _minimize_trustregion_exact(fun, x0, args, jac, hess, |
| callback=callback, **options) |
| else: |
| raise ValueError(f'Unknown solver {method}') |
|
|
| if remove_vars: |
| res.x = _add_to_array(res.x, i_fixed, x_fixed) |
| res.jac = _add_to_array(res.jac, i_fixed, np.nan) |
| if "hess_inv" in res: |
| res.hess_inv = None |
|
|
| if getattr(callback, 'stop_iteration', False): |
| res.success = False |
| res.status = 99 |
| res.message = "`callback` raised `StopIteration`." |
|
|
| return res |
|
|
|
|
| def minimize_scalar(fun, bracket=None, bounds=None, args=(), |
| method=None, tol=None, options=None): |
| """Local minimization of scalar function of one variable. |
| |
| Parameters |
| ---------- |
| fun : callable |
| Objective function. |
| Scalar function, must return a scalar. |
| |
| Suppose the callable has signature ``f0(x, *my_args, **my_kwargs)``, where |
| ``my_args`` and ``my_kwargs`` are required positional and keyword arguments. |
| Rather than passing ``f0`` as the callable, wrap it to accept |
| only ``x``; e.g., pass ``fun=lambda x: f0(x, *my_args, **my_kwargs)`` as the |
| callable, where ``my_args`` (tuple) and ``my_kwargs`` (dict) have been |
| gathered before invoking this function. |
| |
| bracket : sequence, optional |
| For methods 'brent' and 'golden', `bracket` defines the bracketing |
| interval and is required. |
| Either a triple ``(xa, xb, xc)`` satisfying ``xa < xb < xc`` and |
| ``func(xb) < func(xa) and func(xb) < func(xc)``, or a pair |
| ``(xa, xb)`` to be used as initial points for a downhill bracket search |
| (see `scipy.optimize.bracket`). |
| The minimizer ``res.x`` will not necessarily satisfy |
| ``xa <= res.x <= xb``. |
| bounds : sequence, optional |
| For method 'bounded', `bounds` is mandatory and must have two finite |
| items corresponding to the optimization bounds. |
| args : tuple, optional |
| Extra arguments passed to the objective function. |
| method : str or callable, optional |
| Type of solver. Should be one of: |
| |
| - :ref:`Brent <optimize.minimize_scalar-brent>` |
| - :ref:`Bounded <optimize.minimize_scalar-bounded>` |
| - :ref:`Golden <optimize.minimize_scalar-golden>` |
| - custom - a callable object (added in version 0.14.0), see below |
| |
| Default is "Bounded" if bounds are provided and "Brent" otherwise. |
| See the 'Notes' section for details of each solver. |
| |
| tol : float, optional |
| Tolerance for termination. For detailed control, use solver-specific |
| options. |
| options : dict, optional |
| A dictionary of solver options. |
| |
| maxiter : int |
| Maximum number of iterations to perform. |
| disp : bool |
| Set to True to print convergence messages. |
| |
| See :func:`show_options()` for solver-specific options. |
| |
| Returns |
| ------- |
| res : OptimizeResult |
| The optimization result represented as a ``OptimizeResult`` object. |
| Important attributes are: ``x`` the solution array, ``success`` a |
| Boolean flag indicating if the optimizer exited successfully and |
| ``message`` which describes the cause of the termination. See |
| `OptimizeResult` for a description of other attributes. |
| |
| See also |
| -------- |
| minimize : Interface to minimization algorithms for scalar multivariate |
| functions |
| show_options : Additional options accepted by the solvers |
| |
| Notes |
| ----- |
| This section describes the available solvers that can be selected by the |
| 'method' parameter. The default method is the ``"Bounded"`` Brent method if |
| `bounds` are passed and unbounded ``"Brent"`` otherwise. |
| |
| Method :ref:`Brent <optimize.minimize_scalar-brent>` uses Brent's |
| algorithm [1]_ to find a local minimum. The algorithm uses inverse |
| parabolic interpolation when possible to speed up convergence of |
| the golden section method. |
| |
| Method :ref:`Golden <optimize.minimize_scalar-golden>` uses the |
| golden section search technique [1]_. It uses analog of the bisection |
| method to decrease the bracketed interval. It is usually |
| preferable to use the *Brent* method. |
| |
| Method :ref:`Bounded <optimize.minimize_scalar-bounded>` can |
| perform bounded minimization [2]_ [3]_. It uses the Brent method to find a |
| local minimum in the interval x1 < xopt < x2. |
| |
| Note that the Brent and Golden methods do not guarantee success unless a |
| valid ``bracket`` triple is provided. If a three-point bracket cannot be |
| found, consider `scipy.optimize.minimize`. Also, all methods are intended |
| only for local minimization. When the function of interest has more than |
| one local minimum, consider :ref:`global_optimization`. |
| |
| **Custom minimizers** |
| |
| It may be useful to pass a custom minimization method, for example |
| when using some library frontend to minimize_scalar. You can simply |
| pass a callable as the ``method`` parameter. |
| |
| The callable is called as ``method(fun, args, **kwargs, **options)`` |
| where ``kwargs`` corresponds to any other parameters passed to `minimize` |
| (such as `bracket`, `tol`, etc.), except the `options` dict, which has |
| its contents also passed as `method` parameters pair by pair. The method |
| shall return an `OptimizeResult` object. |
| |
| The provided `method` callable must be able to accept (and possibly ignore) |
| arbitrary parameters; the set of parameters accepted by `minimize` may |
| expand in future versions and then these parameters will be passed to |
| the method. You can find an example in the scipy.optimize tutorial. |
| |
| .. versionadded:: 0.11.0 |
| |
| References |
| ---------- |
| .. [1] Press, W., S.A. Teukolsky, W.T. Vetterling, and B.P. Flannery. |
| Numerical Recipes in C. Cambridge University Press. |
| .. [2] Forsythe, G.E., M. A. Malcolm, and C. B. Moler. "Computer Methods |
| for Mathematical Computations." Prentice-Hall Series in Automatic |
| Computation 259 (1977). |
| .. [3] Brent, Richard P. Algorithms for Minimization Without Derivatives. |
| Courier Corporation, 2013. |
| |
| Examples |
| -------- |
| Consider the problem of minimizing the following function. |
| |
| >>> def f(x): |
| ... return (x - 2) * x * (x + 2)**2 |
| |
| Using the *Brent* method, we find the local minimum as: |
| |
| >>> from scipy.optimize import minimize_scalar |
| >>> res = minimize_scalar(f) |
| >>> res.fun |
| -9.9149495908 |
| |
| The minimizer is: |
| |
| >>> res.x |
| 1.28077640403 |
| |
| Using the *Bounded* method, we find a local minimum with specified |
| bounds as: |
| |
| >>> res = minimize_scalar(f, bounds=(-3, -1), method='bounded') |
| >>> res.fun # minimum |
| 3.28365179850e-13 |
| >>> res.x # minimizer |
| -2.0000002026 |
| |
| """ |
| if not isinstance(args, tuple): |
| args = (args,) |
|
|
| if callable(method): |
| meth = "_custom" |
| elif method is None: |
| meth = 'brent' if bounds is None else 'bounded' |
| else: |
| meth = method.lower() |
| if options is None: |
| options = {} |
|
|
| if bounds is not None and meth in {'brent', 'golden'}: |
| message = f"Use of `bounds` is incompatible with 'method={method}'." |
| raise ValueError(message) |
|
|
| if tol is not None: |
| options = dict(options) |
| if meth == 'bounded' and 'xatol' not in options: |
| warn("Method 'bounded' does not support relative tolerance in x; " |
| "defaulting to absolute tolerance.", |
| RuntimeWarning, stacklevel=2) |
| options['xatol'] = tol |
| elif meth == '_custom': |
| options.setdefault('tol', tol) |
| else: |
| options.setdefault('xtol', tol) |
|
|
| |
| disp = options.get('disp') |
| if isinstance(disp, bool): |
| options['disp'] = 2 * int(disp) |
|
|
| if meth == '_custom': |
| res = method(fun, args=args, bracket=bracket, bounds=bounds, **options) |
| elif meth == 'brent': |
| res = _recover_from_bracket_error(_minimize_scalar_brent, |
| fun, bracket, args, **options) |
| elif meth == 'bounded': |
| if bounds is None: |
| raise ValueError('The `bounds` parameter is mandatory for ' |
| 'method `bounded`.') |
| res = _minimize_scalar_bounded(fun, bounds, args, **options) |
| elif meth == 'golden': |
| res = _recover_from_bracket_error(_minimize_scalar_golden, |
| fun, bracket, args, **options) |
| else: |
| raise ValueError(f'Unknown solver {method}') |
|
|
| |
| |
| |
| res.fun = np.asarray(res.fun)[()] |
| res.x = np.reshape(res.x, res.fun.shape)[()] |
| return res |
|
|
|
|
| def _remove_from_bounds(bounds, i_fixed): |
| """Removes fixed variables from a `Bounds` instance""" |
| lb = bounds.lb[~i_fixed] |
| ub = bounds.ub[~i_fixed] |
| return Bounds(lb, ub) |
|
|
|
|
| def _remove_from_func(fun_in, i_fixed, x_fixed, min_dim=None, remove=0): |
| """Wraps a function such that fixed variables need not be passed in""" |
| def fun_out(x_in, *args, **kwargs): |
| x_out = np.zeros_like(i_fixed, dtype=x_in.dtype) |
| x_out[i_fixed] = x_fixed |
| x_out[~i_fixed] = x_in |
| y_out = fun_in(x_out, *args, **kwargs) |
| y_out = np.array(y_out) |
|
|
| if min_dim == 1: |
| y_out = np.atleast_1d(y_out) |
| elif min_dim == 2: |
| y_out = np.atleast_2d(y_out) |
|
|
| if remove == 1: |
| y_out = y_out[..., ~i_fixed] |
| elif remove == 2: |
| y_out = y_out[~i_fixed, ~i_fixed] |
|
|
| return y_out |
| return fun_out |
|
|
|
|
| def _add_to_array(x_in, i_fixed, x_fixed): |
| """Adds fixed variables back to an array""" |
| i_free = ~i_fixed |
| if x_in.ndim == 2: |
| i_free = i_free[:, None] @ i_free[None, :] |
| x_out = np.zeros_like(i_free, dtype=x_in.dtype) |
| x_out[~i_free] = x_fixed |
| x_out[i_free] = x_in.ravel() |
| return x_out |
|
|
|
|
| def _validate_bounds(bounds, x0, meth): |
| """Check that bounds are valid.""" |
|
|
| msg = "An upper bound is less than the corresponding lower bound." |
| if np.any(bounds.ub < bounds.lb): |
| raise ValueError(msg) |
|
|
| msg = "The number of bounds is not compatible with the length of `x0`." |
| try: |
| bounds.lb = np.broadcast_to(bounds.lb, x0.shape) |
| bounds.ub = np.broadcast_to(bounds.ub, x0.shape) |
| except Exception as e: |
| raise ValueError(msg) from e |
|
|
| return bounds |
|
|
| def standardize_bounds(bounds, x0, meth): |
| """Converts bounds to the form required by the solver.""" |
| if meth in {'trust-constr', 'powell', 'nelder-mead', 'cobyla', 'cobyqa', |
| 'new'}: |
| if not isinstance(bounds, Bounds): |
| lb, ub = old_bound_to_new(bounds) |
| bounds = Bounds(lb, ub) |
| elif meth in ('l-bfgs-b', 'tnc', 'slsqp', 'old'): |
| if isinstance(bounds, Bounds): |
| bounds = new_bounds_to_old(bounds.lb, bounds.ub, x0.shape[0]) |
| return bounds |
|
|
|
|
| def standardize_constraints(constraints, x0, meth): |
| """Converts constraints to the form required by the solver.""" |
| all_constraint_types = (NonlinearConstraint, LinearConstraint, dict) |
| new_constraint_types = all_constraint_types[:-1] |
| if constraints is None: |
| constraints = [] |
| elif isinstance(constraints, all_constraint_types): |
| constraints = [constraints] |
| else: |
| constraints = list(constraints) |
|
|
| if meth in ['trust-constr', 'cobyqa', 'new']: |
| for i, con in enumerate(constraints): |
| if not isinstance(con, new_constraint_types): |
| constraints[i] = old_constraint_to_new(i, con) |
| else: |
| |
| for i, con in enumerate(list(constraints)): |
| if isinstance(con, new_constraint_types): |
| old_constraints = new_constraint_to_old(con, x0) |
| constraints[i] = old_constraints[0] |
| constraints.extend(old_constraints[1:]) |
|
|
| return constraints |
|
|
|
|
| def _optimize_result_for_equal_bounds( |
| fun, bounds, method, args=(), constraints=() |
| ): |
| """ |
| Provides a default OptimizeResult for when a bounded minimization method |
| has (lb == ub).all(). |
| |
| Parameters |
| ---------- |
| fun: callable |
| bounds: Bounds |
| method: str |
| constraints: Constraint |
| """ |
| success = True |
| message = 'All independent variables were fixed by bounds.' |
|
|
| |
| x0 = bounds.lb |
|
|
| if constraints: |
| message = ("All independent variables were fixed by bounds at values" |
| " that satisfy the constraints.") |
| constraints = standardize_constraints(constraints, x0, 'new') |
|
|
| maxcv = 0 |
| for c in constraints: |
| pc = PreparedConstraint(c, x0) |
| violation = pc.violation(x0) |
| if np.sum(violation): |
| maxcv = max(maxcv, np.max(violation)) |
| success = False |
| message = (f"All independent variables were fixed by bounds, but " |
| f"the independent variables do not satisfy the " |
| f"constraints exactly. (Maximum violation: {maxcv}).") |
|
|
| return OptimizeResult( |
| x=x0, fun=fun(x0, *args), success=success, message=message, nfev=1, |
| njev=0, nhev=0, |
| ) |
|
|