Oddbean new post about | logout
  -1732.947060918
-0.5
--


```python
#
#    Author:  Michael D. Johnson
#    Date:    13 March 2015
#    Email:    michael.d.johnson@ucsf.edu
#

import numpy as np

# define the value of the target function
def f(x):
    return x**2 + x - 2

# set the initial guess for the variables
def x0():
    return [1,2,3,4]

# apply a method of steepest descent to find the minimum value of the function
# returns an array with the best values found and also returns the number of iterations
# required to find the minima.
def steepest_descent(f=f, x0=x0(), epsilon=1e-24, maxiters=100):
    """
    Applies a method of steepest descent to find the minimum value of the function.

    f:        The target function to minimize (default is f(x) = x^2 + x - 2).
    x0:      Initial guess for the variables (default is [1,2,3,4]).
    epsilon:  Tolerance to be used in determining whether an acceptable minimum value has been found.
    maxiters: Maximum number of iterations allowed (default is 100).

    Returns:  A list containing the best values found and also returns the number of iterations required to find the minima.
    """

    # set up the variables needed for the algorithm
    x = x0()
    H = np.zeros((len(x), len(x)))
    d = None

    i = 0
    while True:
        i += 1

        # evaluate the function at the current point
        fval = f(x)

        if fval < epsilon:
            break

        # calculate the gradient of the function at the current point
        g = np.zeros((len(x),))
        for j in range(len(x)):
            g[j] = (f(np.append(x, -1)) - f(np.delete(x, j)))/(2*(-1)**j)

        # update the Hessian matrix if it has been calculated previously
        if d is not None:
            H += np.outer(g,g).reshape((len(x),len(x)))/i

        # update the direction vector
        if d is None:
            d = -g
        else:
            H_inv = np.linalg.pinv(H)
            g = f(x) - np.outer(d, x).reshape((1, len(x)))
            d = np.dot(H_inv, g)

        # update the variables
        x -= d

    return x, i

# calculate the inverse Hessian matrix using a limited memory algorithm
def invHessian(H):
    """
    Calculates the inverse Hessian matrix using a limited memory algorithm.

    Returns:  The inverse Hessian matrix.
    """

    # get the dimensions of the Hessian matrix
    n = len(H)

    # initialize the variables needed for the limited memory algorithm
    L = np.eye(n)
    r = np.zeros((n,1))

    for k in range(5):  # limited number of iterations
        L_inv = np.linalg.pinv(L)
        r = np.dot(L_inv, np.outer(H, r)) + np.outer(L, np.eye(n))
        L = np.dot(L, L_inv)

    return L

# calculate the inverse Hessian matrix and then use it to find a minimum value of the function
def min_func_with_Hessian(f=f, x0=x0(), epsilon=1e-24, maxiters=100):
    """
    Calculates the inverse Hessian matrix and then uses it to find a minimum value of the function.

    f:        The target function to minimize (default is f(x) = x^2 + x - 2).
    x0:      Initial guess for the variables (default is [1,2,3,4]).
    epsilon:  Tolerance to be used in determining whether an acceptable minimum value has been found.
    maxiters: Maximum number of iterations allowed (default is 100).

    Returns:  A list containing the best values found and also returns the number of iterations required to find the minima.
    """

    # evaluate the function at the initial point
    fval = f(x0())

    # calculate the Hessian matrix
    H = np.zeros((len(x0()), len(x0())))
    for j in range(len(x0())):
        for i in range(j+1, len(x0())):
            x = x0()
            x[i] = -1
            H[j][i] = (f(np.append(x, -1)) - f(np.delete(x, i)))/(2*(-1)**j)
    H = np.dot(H.T, H)/len(x0())

    # calculate the inverse Hessian matrix
    L = invHessian(H)

    # apply a method of steepest descent to find the minimum value of the function
    x, i = steepest_descent(f=f, x0=x0(), epsilon=epsilon, maxiters=maxiters)

    return x, i

# calculate the inverse Hessian matrix and then use it to find a global minimum value of the function
def min_func_with_Hessian_global(f=f, x0=x0(), epsilon=1e-24, maxiters=100):
    """
    Calculates the inverse Hessian matrix and then uses it to find a global minimum value of the function.

    f:        The target function to minimize (default is f(x) = x^2 + x - 2).
    x0:      Initial guess for the variables (default is [1,2,3,4]).
    epsilon:  Tolerance to be used in determining whether an acceptable minimum value has been found.
    maxiters: Maximum number of iterations allowed (default is 100).

    Returns:  A list containing the best values found and also returns the number of iterations required to find the minima.
    """

    # evaluate the function at the initial point
    fval = f(x0())

    # calculate the Hessian matrix
    H = np.zeros((len(x0()), len(x0())))
    for j in range(len(x0())):
        for i in range(j+1, len(x0())):
            x = x0()
            x[i] = -1
            H[j][i] = (f(np.append(x, -1)) - f(np.delete(x, i)))/(2*(-1)**j)
    H = np.dot(H.T, H)/len(x0())

    # calculate the inverse Hessian matrix
    L = invHessian(H)

    # apply a method of steepest descent to find the minimum value of the function
    x, i = steepest_descent(f=f, x0=x0(), epsilon=epsilon, maxiters=maxiters)

    # check if this is the global minimum
    while True:
        i += 1
        x, fval = min_func_with_Hessian(f=f, x0=x, epsilon=epsilon/i, maxiters=maxiters)
        if fval > i*epsilon/i:
            break
    return x, i
```