[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