## module goldSearch
''' a,b = bracket(f,xStart,h)
    Finds the brackets (a,b) of a minimum point of the
    user-supplied scalar function f(x).
    The search starts downhill from xStart with a step
    length h.

    x,fMin = search(f,a,b,tol=1.0e-6)
    Golden section method for determining x that minimizes
    the user-supplied scalar function f(x).
    The minimum must be bracketed in (a,b).
'''       
import math
def bracket(f,x1,h):
    c = 1.618033989 
    f1 = f(x1)
    x2 = x1 + h; f2 = f(x2)
  # Determine downhill direction and change sign of h if needed
    if f2 > f1:
        h = -h
        x2 = x1 + h; f2 = f(x2)
      # Check if minimum between x1 - h and x1 + h
        if f2 > f1: return x2,x1 - h 
  # Search loop
    for i in range (100):    
        h = c*h
        x3 = x2 + h; f3 = f(x3)
        if f3 > f2: return x1,x3
        x1 = x2; x2 = x3
        f1 = f2; f2 = f3
    print("Bracket did not find a mimimum")

def search(f,a,b,tol=1.0e-9):
    nIter = int(math.ceil(-2.078087*math.log(tol/abs(b-a))))
    R = 0.618033989
    C = 1.0 - R
  # First telescoping
    x1 = R*a + C*b; x2 = C*a + R*b
    f1 = f(x1); f2 = f(x2)
  # Main loop
    for i in range(nIter):
        if f1 > f2:
            a = x1
            x1 = x2; f1 = f2
            x2 = C*a + R*b; f2 = f(x2)
        else:
            b = x2
            x2 = x1; f2 = f1
            x1 = R*a + C*b; f1 = f(x1)
    if f1 < f2: return x1,f1
    else: return x2,f2
















































## example10_1
# Use goldSearch to find x that minimizes
# f (x) = 1.6x^3 + 3x^2 − 2x
# subject to the constraint x >= 0. Compare the result with the analytical solution 
#(the global minimum occurs at x = 0.273494)
from goldSearch import *
def f(x):
	lam = 1.0 # Constraint multiplier
	c = min(0.0, x) # Constraint function
	return 1.6*x**3 + 3.0*x**2 - 2.0*x + lam*c**2
xStart = 1.0
h = 0.01
x1,x2 = bracket(f,xStart,h)
x,fMin = search(f,x1,x2)
print("x =",x)
print("f(x) =",fMin)
input ("\nPress return to exit")
















































The trapezoid shown below is the cross section of a beam. It is formed by removing the top from a triangle of base B = 48mm and height H = 60 mm. The problem is to find the height y of the trapezoid that maximizes the section modulus \(S=I_{\bar{x}}/c \) where \(I_{\bar{x}}\) is the second moment of the cross-sectional area about the axis that passes through the centroid C of the cross section. By optimizing the section modulus, we minimize the maximum bending stress σmax = M/S in the beam, M being the bending moment.

Solution: Considering the area of the trapezoid as a composite of a rectangle and two triangles, the section modulus is found through the following sequence of computations:

Because we wish to maximize S with a minimization algorithm, the merit function is −S. There are no constraints in this problem.

## example10_2
# calculates the height y of a trapezoid that maximizes the section modulus cross section of a beam
from goldSearch import *
def f(y):
	B = 48.0
	H = 60.0
	a = B*(H - y)/H
	b = (B - a)/2.0
	A = (B + a)*y/2.0
	Q = (a*y**2)/2.0 + (b*y**2)/3.0
	d = Q/A
	c = y - d
	I = (a*y**3)/3.0 + (b*y**3)/6.0
	Ibar = I - A*d**2
	return -Ibar/c
yStart = 60.0 # Starting value of y
h = 1.0 # Size of first step used in bracketing
a,b = bracket(f,yStart,h)
yOpt,fOpt = search(f,a,b)
print("Optimal y =",yOpt)
print("Optimal S =",-fOpt)
print("S of triangle =",-f(60.0))
input("Press return to exit")
















































## module powell
''' xMin,nCyc = powell(F,x,h=0.1,tol=1.0e-6)
    Powell's method of minimizing user-supplied function F(x).
    x    = starting point
    h   = initial search increment used in 'bracket'
    xMin = mimimum point
    nCyc = number of cycles
'''
import numpy as np
from goldSearch import *
import math

def powell(F,x,h=0.1,tol=1.0e-6):
    
    def f(s): return F(x + s*v)    # F in direction of v

    n = len(x)                     # Number of design variables
    df = np.zeros(n)               # Decreases of F stored here
    u = np.identity(n)             # Vectors v stored here by rows
    for j in range(30):            # Allow for 30 cycles:
        xOld = x.copy()            # Save starting point
        fOld = F(xOld)
      # First n line searches record decreases of F
        for i in range(n):
            v = u[i]
            a,b = bracket(f,0.0,h)
            s,fMin = search(f,a,b)
            df[i] = fOld - fMin
            fOld = fMin
            x = x + s*v
      # Last line search in the cycle    
        v = x - xOld
        a,b = bracket(f,0.0,h)
        s,fLast = search(f,a,b)
        x = x + s*v
      # Check for convergence
        if math.sqrt(np.dot(x-xOld,x-xOld)/n) < tol: return x,j+1
      # Identify biggest decrease & update search directions
        iMax = np.argmax(df)
        for i in range(iMax,n-1):
            u[i] = u[i+1]
        u[n-1] = v
    print("Powell did not converge")
















































## example10_3
# Find the minimum of the function
# F = 100(y − x^2)^2 + (1 − x)^2
from powell import *
from numpy import array
def F(x): return 100.0*(x[1] - x[0]**2)**2 + (1 - x[0])**2
xStart = array([-1.0, 1.0])
xMin,nIter = powell(F,xStart)
print("x =",xMin)
print("F(x) =",F(xMin))
print("Number of cycles =",nIter)
input ("Press return to exit")
















































Use powell to determine the smallest distance from the point (5, 8) to the curve xy = 5.
Solution: This is a constrained optimization problem:Minimize F(x, y) = (x − 5)^2 + (y − 8)^2 (the square of the distance) subject to the equality constraint xy − 5 = 0.

## example10_4
# Use powell to determine the smallest distance from the point (5, 8) to the curve
# xy = 5
from powell import *
from numpy import array
from math import sqrt
def F(x):
	lam = 1.0 # Penalty multiplier
	c = x[0]*x[1] - 5.0 # Constraint equation
	return distSq(x) + lam*c**2 # Penalized merit function
def distSq(x): return (x[0] - 5)**2 + (x[1] - 8)**2
xStart = array([ 1.0,5.0])
x,numIter = powell(F,xStart,0.1)
print("Intersection point =",x)
print("Minimum distance =", sqrt(distSq(x)))
print("xy =", x[0]*x[1])
print("Number of cycles =",numIter)
input ("Press return to exit")


Check: Since we have an equality constraint, the optimal point can readily be found by calculus. The function is (here λ is the Lagrangian multiplier)
F*(x, y, λ) = (x − 5)2 + (y − 8)2 + λ(xy − 5) so we derive:
∂F*/∂x = 2(x − 5) + λy = 0
∂F*/∂y = 2(y − 8) + λx = 0
g(x) = xy − 5 = 0
which can be solved with the Newton-Raphson method

## example10_4_check
import numpy as np
from newtonRaphson2 import *
def F(x):
	return np.array([2.0*(x[0] - 5.0) + x[2]*x[1], \
					2.0*(x[1] - 8.0) + x[2]*x[0], \
					x[0]*x[1] - 5.0])
xStart = np.array([1.0, 5.0, 1.0])
print("x = ", newtonRaphson2(F,xStart))
input("Press return to exit")














































The displacement formulation of the truss shown results in the following simultaneous equations for the joint displacements u:


where E represents the modulus of elasticity of the material and Ai is the cross sectional area of member i. Use Powell’s method to minimize the structural volume (the weight) of the truss while keeping the displacement u2 below a given value δ.

## example10_5
# minimize the structural volume(the weight) of the truss
from powell import *
from numpy import array
from math import sqrt
from gaussElimin import *
def F(x):
	global v, weight
	lam = 100.0
	c = 2.0*sqrt(2.0)
	A = array([[c*x[1] + x[2], -x[2], x[2]], \
	[-x[2], x[2], -x[2]], \
	[ x[2], -x[2], c*x[0] + x[2]]])/c
	b = array([0.0, -1.0, 0.0])
	v = gaussElimin(A,b)
	weight = x[0] + x[1] + sqrt(2.0)*x[2]
	penalty = max(0.0,abs(v[1]) - 1.0)**2 \
	+ max(0.0,-x[0])**2 \
	+ max(0.0,-x[1])**2 \
	+ max(0.0,-x[2])**2
	return weight + penalty*lam
xStart = array([1.0, 1.0, 1.0])
x,numIter = powell(F,xStart)
print("x = ",x)
print("v = ",v)
print("Relative weight F = ",weight)
print("Number of cycles = ",numIter)
input("Press return to exit")
















































## module downhill
''' x = downhill(F,xStart,side=0.1,tol=1.0e-6)
    Downhill simplex method for minimizing the user-supplied
    scalar function F(x) with respect to the vector x.
    xStart = starting vector x.
    side   = side length of the starting simplex (default is 0.1)
'''
import numpy as np
import math

def downhill(F,xStart,side=0.1,tol=1.0e-6):
    n = len(xStart)                 # Number of variables
    x = np.zeros((n+1,n)) 
    f = np.zeros(n+1)
    
  # Generate starting simplex
    x[0] = xStart
    for i in range(1,n+1):
        x[i] = xStart
        x[i,i-1] = xStart[i-1] + side        
  # Compute values of F at the vertices of the simplex     
    for i in range(n+1): f[i] = F(x[i])
    
  # Main loop
    for k in range(500):
      # Find highest and lowest vertices
        iLo = np.argmin(f)
        iHi = np.argmax(f)       
      # Compute the move vector d
        d = (-(n+1)*x[iHi] + np.sum(x,axis=0))/n
      # Check for convergence
        if math.sqrt(np.dot(d,d)/n) < tol: return x[iLo]
        
      # Try reflection
        xNew = x[iHi] + 2.0*d              
        fNew = F(xNew)        
        if fNew <= f[iLo]:        # Accept reflection 
            x[iHi] = xNew
            f[iHi] = fNew
          # Try expanding the reflection
            xNew = x[iHi] + d               
            fNew = F(xNew)
            if fNew <= f[iLo]:    # Accept expansion
                x[iHi] = xNew
                f[iHi] = fNew
        else:
          # Try reflection again
            if fNew <= f[iHi]:    # Accept reflection
                x[iHi] = xNew
                f[iHi] = fNew
            else:
              # Try contraction
                xNew = x[iHi] + 0.5*d
                fNew = F(xNew)
                if fNew <= f[iHi]: # Accept contraction
                    x[iHi] = xNew
                    f[iHi] = fNew
                else:
                  # Use shrinkage
                    for i in range(len(x)):
                        if i != iLo:
                            x[i] = (x[i] - x[iLo])*0.5
                            f[i] = F(x[i])
    print("Too many iterations in downhill")
    return x[iLo]
















































The figure shows the cross section of a channel carrying water. Use the downhill simplex to determine h, b, and θ that minimize the length of the wetted perimeter while maintaining a cross-sectional area of 8m2. (Minimizing the wetted perimeter results in the least resistance to the flow.)

Solution: The cross-sectional area of the channel is
A = 1/2[b + (b + 2htan θ)]h = (b + htan θ)h
the length of the wetted perimeter is
S = b + 2(hsec θ)
The optimization problem is to minimize S subject to the constraint A − 8 = 0. Using the penalty function to take care of the equality constraint, the function to be minimized is S* = b + 2hsec θ + λ[(b + htan θ)h − 8]2

## example10_7
# minimize the length of the wetted perimeter
import numpy as np
import math
from downhill import *
def S(x):
	global perimeter,area
	lam = 10000.0
	perimeter = x[0] + 2.0*x[1]/math.cos(x[2])
	area = (x[0] + x[1]*math.tan(x[2]))*x[1]
	return perimeter + lam*(area - 8.0)**2
xStart = np.array([4.0, 2.0, 0.0])
x = downhill(S,xStart)
area = (x[0] + x[1]*math.tan(x[2]))*x[1]
print("b = ",x[0])
print("h = ",x[1])
print("theta (deg) = ",x[2]*180.0/math.pi)
print("area = ",area)
print("perimeter = ",perimeter)
input("Finished. Press return to exit")
















































The fundamental circular frequency of the stepped shaft shown is required to be higher than ω0 (a given value). Use the downhill simplex to determine the diameters d1 and d2 that minimize the volume of the material without violating the frequency constraint.


The approximate value of the fundamental frequency can be computed by solving the eigenvalue problem (obtainable from the finite element approximation)


Solution: We start by introducing the dimensionless variables xi = di/d0, where d0 is an arbitrary “base” diameter. As a result, the eigenvalue problem becomes

## example10_8
# Use the downhill simplex to determine the diameters d1 and d2
# that minimize the volume of the material without violating the 
# frequency constraint.
import numpy as np
from stdForm import *
from inversePower import *
from downhill import *
def F(x):
	global eVal
	lam = 1.0e6
	eVal_min = 0.4
	A = np.array([[4.0*(x[0]**4 + x[1]**4), 2.0*x[1]**4], \
		[2.0*x[1]**4, 4.0*x[1]**4]])
	B = np.array([[4.0*(x[0]**2 + x[1]**2), -3.0*x[1]**2], \
		[-3*x[1]**2, 4.0*x[1]**2]])
	H,t = stdForm(A,B)
	eVal,eVec = inversePower(H,0.0)
	return x[0]**2 + x[1]**2 + lam*(max(0.0,eVal_min - eVal))**2
xStart = np.array([1.0,1.0])
x = downhill(F,xStart,0.1)
print("x = ", x)
print("eigenvalue = ",eVal)
input ("Press return to exit")