[Scipy-svn] r3679 - in trunk/scipy/optimize: . tests
scipy-svn at scipy.org
scipy-svn at scipy.org
Mon Dec 17 17:39:32 EST 2007
Author: stefan
Date: 2007-12-17 16:39:03 -0600 (Mon, 17 Dec 2007)
New Revision: 3679
Added:
trunk/scipy/optimize/tests/test_slsqp.py
Modified:
trunk/scipy/optimize/slsqp.py
Log:
Add tests for slsqp. Reformat docstring. Remove Windows line endings.
Modified: trunk/scipy/optimize/slsqp.py
===================================================================
--- trunk/scipy/optimize/slsqp.py 2007-12-17 20:13:41 UTC (rev 3678)
+++ trunk/scipy/optimize/slsqp.py 2007-12-17 22:39:03 UTC (rev 3679)
@@ -1,3 +1,5 @@
+__all__ = ['fmin_slsqp']
+
from _slsqp import slsqp
from numpy import zeros, array, identity, linalg, rank, squeeze, append, \
asfarray,product
@@ -10,80 +12,99 @@
def fmin_slsqp( func, x0 , eqcons=[], f_eqcons=None, ieqcons=[], f_ieqcons=None,
bounds = [], fprime = None, fprime_cons=None,args = (),
- iter = 100, acc = 1.0E-6, iprint = 1, full_output = 0,
+ iter = 100, acc = 1.0E-6, iprint = 1, full_output = 0,
epsilon = _epsilon ):
"""
Minimize a function using Sequential Least SQuares Programming
-
- Description:
- Python interface function for the SLSQP Optimization subroutine
- originally implemented by Dieter Kraft.
-
- Inputs:
- func - Objective function (in the form func(x, *args))
- x0 - Initial guess for the independent variable(s).
- eqcons - A list of functions of length n such that
- eqcons[j](x0,*args) == 0.0 in a successfully optimized
- problem.
- f_eqcons - A function of the form f_eqcons(x, *args) that returns an
- array in which each element must equal 0.0 in a
- successfully optimized problem. If f_eqcons is
- specified, eqcons is ignored.
- ieqcons - A list of functions of length n such that
- ieqcons[j](x0,*args) >= 0.0 in a successfully optimized
- problem.
- f_ieqcons - A function of the form f_ieqcons(x0, *args) that returns
- an array in which each element must be greater or equal
- to 0.0 in a successfully optimized problem. If
- f_ieqcons is specified, ieqcons is ignored.
- bounds - A list of tuples specifying the lower and upper bound
- for each independent variable [(xl0, xu0),(xl1, xu1),...]
- fprime - A function that evaluates the partial derivatives of func
- fprime_cons - A function of the form f(x, *args) that returns the
- m by n array of constraint normals. If not provided,
- the normals will be approximated. Equality constraint
- normals precede inequality constraint normals. The
- array returned by fprime_cons should be sized as
- ( len(eqcons) + len(ieqcons), len(x0) ). If
- fprime_cons is not supplied (normals are approximated)
- then the constraints must be supplied via the eqcons
- and ieqcons structures, not f_eqcons and f_ieqcons.
- args - A sequence of additional arguments passed to func and fprime
- iter - The maximum number of iterations (int)
- acc - Requested accuracy (float)
- iprint - The verbosity of fmin_slsqp.
- iprint <= 0 : Silent operation
- iprint == 1 : Print summary upon completion (default)
- iprint >= 2 : Print status of each iterate and summary
- full_output - If 0, return only the minimizer of func (default).
- Otherwise, output final objective function and summary
- information.
- epsilon - The step size for finite-difference derivative estimates.
-
- Outputs: ( x, { fx, gnorm, its, imode, smode })
- x - The final minimizer of func.
- fx - The final value of the objective function.
- its - The number of iterations.
- imode - The exit mode from the optimizer, as an integer.
- smode - A string describing the exit mode from the optimizer.
-
- Exit modes are defined as follows:
- -1 : Gradient evaluation required (g & a)
- 0 : Optimization terminated successfully.
- 1 : Function evaluation required (f & c)
- 2 : More equality constraints than independent variables
- 3 : More than 3*n iterations in LSQ subproblem
- 4 : Inequality constraints incompatible
- 5 : Singular matrix E in LSQ subproblem
- 6 : Singular matrix C in LSQ subproblem
- 7 : Rank-deficient equality constraint subproblem HFTI
- 8 : Positive directional derivative for linesearch
- 9 : Iteration limit exceeded
+ Python interface function for the SLSQP Optimization subroutine
+ originally implemented by Dieter Kraft.
+
+ *Parameters*:
+ func : callable f(x,*args)
+ Objective function.
+ x0 : ndarray of float
+ Initial guess for the independent variable(s).
+ eqcons : list
+ A list of functions of length n such that
+ eqcons[j](x0,*args) == 0.0 in a successfully optimized
+ problem.
+ f_eqcons : callable f(x,*args)
+ Returns an array in which each element must equal 0.0 in a
+ successfully optimized problem. If f_eqcons is specified,
+ eqcons is ignored.
+ ieqcons : list
+ A list of functions of length n such that
+ ieqcons[j](x0,*args) >= 0.0 in a successfully optimized
+ problem.
+ f_ieqcons : callable f(x0,*args)
+ Returns an array in which each element must be greater or
+ equal to 0.0 in a successfully optimized problem. If
+ f_ieqcons is specified, ieqcons is ignored.
+ bounds : list
+ A list of tuples specifying the lower and upper bound
+ for each independent variable [(xl0, xu0),(xl1, xu1),...]
+ fprime : callable f(x,*args)
+ A function that evaluates the partial derivatives of func.
+ fprime_cons : callable f(x,*args)
+ A function of the form f(x, *args) that returns the m by n
+ array of constraint normals. If not provided, the normals
+ will be approximated. Equality constraint normals precede
+ inequality constraint normals. The array returned by
+ fprime_cons should be sized as ( len(eqcons) +
+ len(ieqcons), len(x0) ). If fprime_cons is not supplied
+ (normals are approximated) then the constraints must be
+ supplied via the eqcons and ieqcons structures, not
+ f_eqcons and f_ieqcons.
+ args : sequence
+ Additional arguments passed to func and fprime.
+ iter : int
+ The maximum number of iterations.
+ acc : float
+ Requested accuracy.
+ iprint : int
+ The verbosity of fmin_slsqp:
+ iprint <= 0 : Silent operation
+ iprint == 1 : Print summary upon completion (default)
+ iprint >= 2 : Print status of each iterate and summary
+ full_output : bool
+ If False, return only the minimizer of func (default).
+ Otherwise, output final objective function and summary
+ information.
+ epsilon : float
+ The step size for finite-difference derivative estimates.
+
+ *Returns*: ( x, { fx, its, imode, smode })
+ x : ndarray of float
+ The final minimizer of func.
+ fx : ndarray of float
+ The final value of the objective function.
+ its : int
+ The number of iterations.
+ imode : int
+ The exit mode from the optimizer (see below).
+ smode : string
+ Message describing the exit mode from the optimizer.
+
+ *Notes*
+
+ Exit modes are defined as follows:
+ -1 : Gradient evaluation required (g & a)
+ 0 : Optimization terminated successfully.
+ 1 : Function evaluation required (f & c)
+ 2 : More equality constraints than independent variables
+ 3 : More than 3*n iterations in LSQ subproblem
+ 4 : Inequality constraints incompatible
+ 5 : Singular matrix E in LSQ subproblem
+ 6 : Singular matrix C in LSQ subproblem
+ 7 : Rank-deficient equality constraint subproblem HFTI
+ 8 : Positive directional derivative for linesearch
+ 9 : Iteration limit exceeded
+
"""
exit_modes = { -1 : "Gradient evaluation required (g & a)",
- 0 : "Optimization terminated successfully.",
+ 0 : "Optimization terminated successfully.",
1 : "Function evaluation required (f & c)",
2 : "More equality constraints than independent variables",
3 : "More than 3*n iterations in LSQ subproblem",
@@ -93,16 +114,16 @@
7 : "Rank-deficient equality constraint subproblem HFTI",
8 : "Positive directional derivative for linesearch",
9 : "Iteration limit exceeded" }
-
+
# Wrap the functions
# Wrap func
- feval, func = wrap_function(func, args)
+ feval, func = wrap_function(func, args)
if fprime:
# Wrap fprime, if provided
- geval, fprime = wrap_function(fprime,args)
+ geval, fprime = wrap_function(fprime,args)
else:
# Wrap approx_fprime, if fprime not provided
- geval, fprime = wrap_function(approx_fprime,(func,epsilon))
+ geval, fprime = wrap_function(approx_fprime,(func,epsilon))
if fprime_cons:
approx_constraint_norms = False
if f_eqcons:
@@ -118,7 +139,7 @@
if ieqcons[i]:
ceval, ieqcons[i] = wrap_function(ieqcons[i],args)
geval, fprime_cons = wrap_function(fprime_cons,args)
- else:
+ else:
approx_constraint_norms = True
eqcons_prime = []
for i in range(len(eqcons)):
@@ -134,16 +155,16 @@
ceval, ieqcons[i] = wrap_function(ieqcons[i],args)
geval, ieqcons_prime[i] = \
wrap_function(approx_fprime, (ieqcons[i],epsilon))
-
- # Transform x0 into an array.
+
+ # Transform x0 into an array.
x = asfarray(x0).flatten()
# Set the parameters that SLSQP will need
meq = len(eqcons) # meq = The number of equality constraints
m = meq + len(ieqcons) # m = The total number of constraints
- la = array([1,m]).max() # la =
+ la = array([1,m]).max() # la =
n = len(x) # n = The number of independent variables
-
+
# Define the workspaces for SLSQP
n1 = n+1
mineq = m - meq + n1 + n1
@@ -151,8 +172,8 @@
+ 2*meq + n1 +(n+1)*n/2 + 2*m + 3*n + 3*n1 + 1
len_jw = mineq
w = zeros(len_w)
- jw = zeros(len_jw)
-
+ jw = zeros(len_jw)
+
# Decompose bounds into xl and xu
if len(bounds) == 0:
bounds = [(-1.0E12, 1.0E12) for i in range(n)]
@@ -160,10 +181,10 @@
raise IndexError, 'SLSQP Error: If bounds is specified, len(bounds) == len(x0)'
xl = array( [ b[0] for b in bounds ] )
xu = array( [ b[1] for b in bounds ] )
-
- # Initialize the iteration counter and the mode value
+
+ # Initialize the iteration counter and the mode value
mode = array(0,int)
- acc = array(acc,float)
+ acc = array(acc,float)
majiter = array(iter,int)
majiter_prev = 0
@@ -171,10 +192,10 @@
if iprint >= 2:
print "%5s %5s %16s %16s" % ("NIT","FC","OBJFUN","GNORM")
- while 1:
- if mode == 0 or mode == 1: # objective and constraint evaluation requird
+ while 1:
+ if mode == 0 or mode == 1: # objective and constraint evaluation requird
# Compute objective function
- fx = func(x)
+ fx = func(x)
# Compute the constraints
if f_eqcons:
ceq = f_eqcons(x)
@@ -183,15 +204,15 @@
if f_ieqcons:
cieq = f_ieqcons(x)
else:
- cieq = [ ieqcons[i](x) for i in range(len(ieqcons)) ]
+ cieq = [ ieqcons[i](x) for i in range(len(ieqcons)) ]
c = numpy.concatenate( (ceq,cieq), 1)
- #c = array ( [ eqcons[i](x) for i in range(meq) ] +
- # [ ieqcons[i](x) for i in range(len(ieqcons)) ] )
+ #c = array ( [ eqcons[i](x) for i in range(meq) ] +
+ # [ ieqcons[i](x) for i in range(len(ieqcons)) ] )
if mode == 0 or mode == -1: # gradient evaluation required
# Compute the derivatives of the objective function
# For some reason SLSQP wants g dimensioned to n+1
- g = append(fprime(x),0.0)
-
+ g = append(fprime(x),0.0)
+
# Compute the normals of the constraints
if approx_constraint_norms:
a = zeros([la,n1])
@@ -200,37 +221,36 @@
for i in range(meq+1,m):
a[i] = append(ieqcons_prime[i](x),0.0)
else:
- a = numpy.concatenate( (fprime_cons(x),zeros([la,1])),1)
-
+ a = numpy.concatenate( (fprime_cons(x),zeros([la,1])),1)
+
# Call SLSQP
slsqp(m, meq, x, xl, xu, fx, c, g, a, acc, majiter, mode, w, jw)
-
- # Print the status of the current iterate if iprint > 2 and the
+
+ # Print the status of the current iterate if iprint > 2 and the
# major iteration has incremented
if iprint >= 2 and majiter > majiter_prev:
print "%5i %5i % 16.6E % 16.6E" % (majiter,feval[0],
- fx,linalg.norm(g))
-
+ fx,linalg.norm(g))
+
# If exit mode is not -1 or 1, slsqp has completed
if abs(mode) != 1:
break
-
+
majiter_prev = int(majiter)
-
+
# Optimization loop complete. Print status if requested
if iprint >= 1:
- print exit_modes[int(mode)] + " (Exit mode " + str(mode) + ')'
- print " Current function value:", fx
+ print exit_modes[int(mode)] + " (Exit mode " + str(mode) + ')'
+ print " Current function value:", fx
print " Iterations:", majiter
print " Function evaluations:", feval[0]
print " Gradient evaluations:", geval[0]
-
+
if full_output == 0:
return x
- else:
- return [list(x),
- float(fx),
- int(majiter),
- int(mode),
+ else:
+ return [list(x),
+ float(fx),
+ int(majiter),
+ int(mode),
exit_modes[int(mode)] ]
-
Added: trunk/scipy/optimize/tests/test_slsqp.py
===================================================================
--- trunk/scipy/optimize/tests/test_slsqp.py 2007-12-17 20:13:41 UTC (rev 3678)
+++ trunk/scipy/optimize/tests/test_slsqp.py 2007-12-17 22:39:03 UTC (rev 3679)
@@ -0,0 +1,90 @@
+from numpy.testing import *
+import numpy as np
+
+set_package_path()
+from scipy.optimize import fmin_slsqp
+from numpy import matrix, diag
+restore_path()
+
+class TestSLSQP(NumpyTestCase):
+ """Test fmin_slsqp using Example 14.4 from Numerical Methods for
+ Engineers by Steven Chapra and Raymond Canale. This example
+ maximizes the function f(x) = 2*x*y + 2*x - x**2 - 2*y**2, which
+ has a maximum at x=2,y=1.
+
+ """
+
+ def _testfunc(self,d,*args):
+ """
+ Arguments:
+ d - A list of two elements, where d[0] represents x and d[1] represents y
+ in the following equation.
+ sign - A multiplier for f. Since we want to optimize it, and the scipy
+ optimizers can only minimize functions, we need to multiply it by
+ -1 to achieve the desired solution
+ Returns:
+ 2*x*y + 2*x - x**2 - 2*y**2
+
+ """
+ try:
+ sign = args[0]
+ except:
+ sign = 1.0
+ x = d[0]
+ y = d[1]
+ return sign*(2*x*y + 2*x - x**2 - 2*y**2)
+
+ def _testfunc_deriv(self,d,*args):
+ """
+ This is the derivative of testfunc, returning a numpy array
+ representing df/dx and df/dy.
+
+ """
+ try:
+ sign = args[0]
+ except:
+ sign = 1.0
+ x = d[0]
+ y = d[1]
+ dfdx = sign*(-2*x + 2*y + 2)
+ dfdy = sign*(2*x - 4*y)
+ return np.array([ dfdx, dfdy ],float)
+
+ def test_unbounded_approximated(self):
+ res = fmin_slsqp(self._testfunc, [-1.0,1.0], args = (-1.0,),
+ iprint = 0, full_output = 1)
+ x,fx,its,imode,smode = res
+ assert_array_almost_equal(x,[2,1])
+
+ def test_unbounded_given(self):
+ res = fmin_slsqp(self._testfunc,[-1.0,1.0], args = (-1.0,),
+ iprint = 0, full_output = 1)
+ x,fx,its,imode,smode = res
+ assert_array_almost_equal(x,[2,1])
+
+ def test_bound_approximated(self):
+ res = fmin_slsqp(self._testfunc,[-1.0,1.0], args = (-1.0,),
+ eqcons = [lambda x, y: x[0]-x[1] ],
+ iprint = 0, full_output = 1)
+ x,fx,its,imode,smode = res
+ assert_array_almost_equal(x,[1,1])
+
+ def test_bound_equality_given(self):
+ res = fmin_slsqp(self._testfunc,[-1.0,1.0],
+ fprime = self._testfunc_deriv,
+ args = (-1.0,), eqcons = [lambda x, y: x[0]-x[1] ],
+ iprint = 0, full_output = 1)
+ x,fx,its,imode,smode = res
+ assert_array_almost_equal(x,[1,1])
+
+ def test_bound_equality_inequality_given(self):
+ res = fmin_slsqp(self._testfunc,[-1.0,1.0],
+ fprime = self._testfunc_deriv,
+ args = (-1.0,),
+ ieqcons = [lambda x, y: x[0]-x[1]-1.0],
+ iprint=0, full_output=1)
+ x,fx,its,imode,smode = res
+ assert_array_almost_equal(x,[2,1])
+
+if __name__ == "__main__":
+ NumpyTest().run()
More information about the Scipy-svn
mailing list