def minimize(func, x0, gradient=None, hessian=None, algorithm="default", **args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` - ``'powell'`` - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires ``gradient`` - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian EXAMPLES:: sage: vars=var('x y z') sage: f=100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) sage: minimize(f,[.1,.3,.4],algorithm="ncg",disp=0) (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ... xm = x[1r:-1r] ... xm_m1 = x[:-2r] ... xm_p1 = x[2r:] ... der = zeros(x.shape,dtype=float) ... der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ... der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ... der[-1] = 200r*(x[-1r]-x[-2r]**2r) ... return der sage: minimize(rosen,[.1,.3,.4],gradient=rosen_der,algorithm="bfgs",disp=0) (1.00..., 1.00..., 1.00...) """ from sage.symbolic.expression import Expression from sage.ext.fast_eval import fast_callable import scipy from scipy import optimize if isinstance(func, Expression): var_list = func.variables() var_names = map(str, var_list) fast_f = fast_callable(func, vars=var_names, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ fast_callable(gradient_list[i], vars=var_names, domain=float) for i in xrange(len(gradient_list)) ] gradient = lambda p: scipy.array( [a(*p) for a in fast_gradient_functions]) else: f = func if algorithm == "default": if gradient == None: min = optimize.fmin(f, map(float, x0), **args) else: min = optimize.fmin_bfgs(f, map(float, x0), fprime=gradient, **args) else: if algorithm == "simplex": min = optimize.fmin(f, map(float, x0), **args) elif algorithm == "bfgs": min = optimize.fmin_bfgs(f, map(float, x0), fprime=gradient, **args) elif algorithm == "cg": min = optimize.fmin_cg(f, map(float, x0), fprime=gradient, **args) elif algorithm == "powell": min = optimize.fmin_powell(f, map(float, x0), **args) elif algorithm == "ncg": if isinstance(func, Expression): hess = func.hessian() hess_fast = [[ fast_callable(a, vars=var_names, domain=float) for a in row ] for row in hess] hessian = lambda p: [[a(*p) for a in row] for row in hess_fast] hessian_p = lambda p, v: scipy.dot(scipy.array(hessian(p)), v) min = optimize.fmin_ncg(f, map(float, x0), fprime=gradient, fhess=hessian, fhess_p=hessian_p, **args) return vector(RDF, min)
def minimize(func,x0,gradient=None,hessian=None,algorithm="default",**args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` - ``'powell'`` - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires ``gradient`` - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian EXAMPLES:: sage: vars=var('x y z') sage: f=100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) sage: minimize(f,[.1,.3,.4],algorithm="ncg",disp=0) (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen,[.1,.3,.4],disp=0) (1.00..., 1.00..., 1.00...) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ... return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ... xm = x[1r:-1r] ... xm_m1 = x[:-2r] ... xm_p1 = x[2r:] ... der = zeros(x.shape,dtype=float) ... der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ... der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ... der[-1] = 200r*(x[-1r]-x[-2r]**2r) ... return der sage: minimize(rosen,[.1,.3,.4],gradient=rosen_der,algorithm="bfgs",disp=0) (1.00..., 1.00..., 1.00...) """ from sage.symbolic.expression import Expression from sage.ext.fast_eval import fast_callable import scipy from scipy import optimize if isinstance(func, Expression): var_list=func.variables() var_names=map(str,var_list) fast_f=fast_callable(func, vars=var_names, domain=float) f=lambda p: fast_f(*p) gradient_list=func.gradient() fast_gradient_functions=[fast_callable(gradient_list[i], vars=var_names, domain=float) for i in xrange(len(gradient_list))] gradient=lambda p: scipy.array([ a(*p) for a in fast_gradient_functions]) else: f=func if algorithm=="default": if gradient==None: min=optimize.fmin(f,map(float,x0),**args) else: min= optimize.fmin_bfgs(f,map(float,x0),fprime=gradient,**args) else: if algorithm=="simplex": min= optimize.fmin(f,map(float,x0),**args) elif algorithm=="bfgs": min= optimize.fmin_bfgs(f,map(float,x0),fprime=gradient,**args) elif algorithm=="cg": min= optimize.fmin_cg(f,map(float,x0),fprime=gradient,**args) elif algorithm=="powell": min= optimize.fmin_powell(f,map(float,x0),**args) elif algorithm=="ncg": if isinstance(func, Expression): hess=func.hessian() hess_fast= [ [fast_callable(a, vars=var_names, domain=float) for a in row] for row in hess] hessian=lambda p: [[a(*p) for a in row] for row in hess_fast] hessian_p=lambda p,v: scipy.dot(scipy.array(hessian(p)),v) min= optimize.fmin_ncg(f,map(float,x0),fprime=gradient,fhess=hessian,fhess_p=hessian_p,**args) return vector(RDF,min)
def minimize(func, x0, gradient=None, hessian=None, algorithm="default", verbose=False, **args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` -- using the downhill simplex algorithm - ``'powell'`` -- use the modified Powell algorithm - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires gradient - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian - ``verbose`` -- (optional, default: False) print convergence message .. NOTE:: For additional information on the algorithms implemented in this function, consult SciPy's `documentation on optimization and root finding <https://docs.scipy.org/doc/scipy/reference/optimize.html>`_ EXAMPLES: Minimize a fourth order polynomial in three variables (see the :wikipedia:`Rosenbrock_function`):: sage: vars = var('x y z') sage: f = 100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f, [.1,.3,.4]) # abs tol 1e-6 (1.0, 1.0, 1.0) Try the newton-conjugate gradient method; the gradient and hessian are computed automatically:: sage: minimize(f, [.1, .3, .4], algorithm="ncg") # abs tol 1e-6 (1.0, 1.0, 1.0) We get additional convergence information with the `verbose` option:: sage: minimize(f, [.1, .3, .4], algorithm="ncg", verbose=True) Optimization terminated successfully. ... (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen, [.1,.3,.4]) # abs tol 3e-5 (1.0, 1.0, 1.0) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ....: xm = x[1r:-1r] ....: xm_m1 = x[:-2r] ....: xm_p1 = x[2r:] ....: der = zeros(x.shape, dtype=float) ....: der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ....: der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ....: der[-1] = 200r*(x[-1r]-x[-2r]**2r) ....: return der sage: minimize(rosen, [.1,.3,.4], gradient=rosen_der, algorithm="bfgs") # abs tol 1e-6 (1.0, 1.0, 1.0) """ from sage.symbolic.expression import Expression from sage.ext.fast_eval import fast_callable import numpy from scipy import optimize if isinstance(func, Expression): var_list = func.variables() var_names = [str(_) for _ in var_list] fast_f = fast_callable(func, vars=var_names, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ fast_callable(gradient_list[i], vars=var_names, domain=float) for i in range(len(gradient_list)) ] gradient = lambda p: numpy.array( [a(*p) for a in fast_gradient_functions]) else: f = func if algorithm == "default": if gradient is None: min = optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) else: min = optimize.fmin_bfgs(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) else: if algorithm == "simplex": min = optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm == "bfgs": min = optimize.fmin_bfgs(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm == "cg": min = optimize.fmin_cg(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm == "powell": min = optimize.fmin_powell(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm == "ncg": if isinstance(func, Expression): hess = func.hessian() hess_fast = [[ fast_callable(a, vars=var_names, domain=float) for a in row ] for row in hess] hessian = lambda p: [[a(*p) for a in row] for row in hess_fast] hessian_p = lambda p, v: scipy.dot(numpy.array(hessian(p)), v) min = optimize.fmin_ncg(f, [float(_) for _ in x0], fprime=gradient, \ fhess=hessian, fhess_p=hessian_p, disp=verbose, **args) return vector(RDF, min)
def minimize(func, x0, gradient=None, hessian=None, algorithm="default", \ verbose=False, **args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` -- using the downhill simplex algorithm - ``'powell'`` -- use the modified Powell algorithm - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires gradient - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian - ``verbose`` -- (optional, default: False) print convergence message .. NOTE:: For additional information on the algorithms implemented in this function, consult SciPy's `documentation on optimization and root finding <https://docs.scipy.org/doc/scipy/reference/optimize.html>`_ EXAMPLES: Minimize a fourth order polynomial in three variables (see the :wikipedia:`Rosenbrock_function`):: sage: vars = var('x y z') sage: f = 100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f, [.1,.3,.4]) # abs tol 1e-6 (1.0, 1.0, 1.0) Try the newton-conjugate gradient method; the gradient and hessian are computed automatically:: sage: minimize(f, [.1, .3, .4], algorithm="ncg") # abs tol 1e-6 (1.0, 1.0, 1.0) We get additional convergence information with the `verbose` option:: sage: minimize(f, [.1, .3, .4], algorithm="ncg", verbose=True) Optimization terminated successfully. ... (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen, [.1,.3,.4]) # abs tol 3e-5 (1.0, 1.0, 1.0) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ....: xm = x[1r:-1r] ....: xm_m1 = x[:-2r] ....: xm_p1 = x[2r:] ....: der = zeros(x.shape, dtype=float) ....: der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ....: der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ....: der[-1] = 200r*(x[-1r]-x[-2r]**2r) ....: return der sage: minimize(rosen, [.1,.3,.4], gradient=rosen_der, algorithm="bfgs") # abs tol 1e-6 (1.0, 1.0, 1.0) """ from sage.symbolic.expression import Expression from sage.ext.fast_eval import fast_callable import scipy from scipy import optimize if isinstance(func, Expression): var_list=func.variables() var_names = [str(_) for _ in var_list] fast_f=fast_callable(func, vars=var_names, domain=float) f=lambda p: fast_f(*p) gradient_list=func.gradient() fast_gradient_functions=[fast_callable(gradient_list[i], vars=var_names, domain=float) for i in range(len(gradient_list))] gradient=lambda p: scipy.array([ a(*p) for a in fast_gradient_functions]) else: f=func if algorithm=="default": if gradient is None: min = optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) else: min= optimize.fmin_bfgs(f, [float(_) for _ in x0],fprime=gradient, disp=verbose, **args) else: if algorithm=="simplex": min= optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm=="bfgs": min= optimize.fmin_bfgs(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm=="cg": min= optimize.fmin_cg(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm=="powell": min= optimize.fmin_powell(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm=="ncg": if isinstance(func, Expression): hess=func.hessian() hess_fast= [ [fast_callable(a, vars=var_names, domain=float) for a in row] for row in hess] hessian=lambda p: [[a(*p) for a in row] for row in hess_fast] hessian_p=lambda p,v: scipy.dot(scipy.array(hessian(p)),v) min = optimize.fmin_ncg(f, [float(_) for _ in x0], fprime=gradient, \ fhess=hessian, fhess_p=hessian_p, disp=verbose, **args) return vector(RDF, min)