/Cyclic-Cordinate-Search-Python-implementation

This notebook contains the implementation of cyclic cordinate search method.

Primary LanguageJupyter NotebookGNU General Public License v3.0GPL-3.0

All imported packages are used to visualize the function and result of the algorithm.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from tabulate import tabulate

png

func = lambda x1,x2 :(5*x1-x2)**4 + (x1-2)**2 + x1 - 2*x2 + 12
funcx1 = lambda x1,x2 : 20*(5*x1-x2)**3 + 2*(x1-2)
funcx2 = lambda x1,x2 : -4*(5*x1-x2)**3 -2

# returns a new function where x2 is set to a constant c.
def set_x2_constant(c):
    return (lambda x1 : (5*x1-c)**4 + (x1-2)**2 + x1 - 2*c + 12)

# returns a new function where x1 is set to a constant c.
def set_x1_constant(c):
    return (lambda x2 : (5*c-x2)**4 + (c-2)**2 + c - 2*x2 + 12)
# x1 and x2 are the intervals where function is plotted.
x1 = np.arange(0,12,0.1)
x2 = np.arange(0,70,0.1)

X1,X2 = np.meshgrid(x1,x2)
f = func(X1,X2)
fig = plt.figure()
ax = Axes3D(fig)
ax.plot_surface(X1,X2,f)
plt.xlabel('X1')
plt.ylabel('X2')
ax.set_zlabel('f(x1,x2)')
plt.gcf().set_size_inches(15, 10)
plt.show()

png

png

fig,ax2 = plt.subplots()
cs = ax2.contour(X1,X2,f,50)
ax2.clabel(cs,inline=1,fontsize=10)
ax2.set_xlabel("X1")
ax2.set_ylabel("X2")
ax2.set_title('Contour Plot of Function')
plt.gcf().set_size_inches(15, 10)

png

png

"""
    GOLDEN SECTION METHOD
    It finds the point where the f(x) is the minimum value of the convex function f.
    
    f :     function we want to find minimum value.
    x_low : lower bound of the interval.
    x_up :  upper bound of the interval.
    tol :   tolerance is the stopping condition.
    max_n:  max number of iteration is also a stopping condition.
    
    return: it returns the average of final interval's bounds which is
            approximately equals to point where f(x) is minimum.
"""
def golden(f,x_low, x_up, tol, max_n):
    
    iter_ctr = 0
    while (x_up-x_low)>tol and iter_ctr<max_n:
        x_n1 = x_up - 0.618*(x_up - x_low)
        x_n2 = x_low + 0.618*(x_up - x_low)
        
        if f(x_n1) <= f(x_n2):
            x_up = x_n2
            x_n2 = x_n1
            x_n1 = x_up - 0.618*(x_up - x_low)
        else:
            x_low = x_n1
            x_n1 = x_n2
            x_n2  = x_low + 0.618*(x_up - x_low)
        iter_ctr = iter_ctr + 1
    return (x_n1 + x_n2)/2.0

png

"""
    CYCLIC COORDINATE SEARCH METHOD
    It finds the point which is the global minimum of the two variable
    function. => f(x1,x2)
    
    f :     function we want to find minimum value.
    ip :    initial points where algorithm starts.
    
    return: it returns the points where f(x1,x2) is minimum and values while iterating.
"""
def cyclic_coordinate(f,ip):
    x1,x2 = ip
    results = [[0,x1,x2,f(x1,x2)]]
    turn = 1
    diff = 10**10
    iter_ctr = 0
    # epsilon could be changed to adjust the sensitivity of algorithm.
    epsilon = 0.0001
    while diff>epsilon:
        func_x1 = set_x2_constant(x2)
        func_x2 = set_x1_constant(x1)
        if (turn == 1):
            new_x1 = golden(func_x1,x1-5000,x1+5000,0.0001,100)
            diff = abs(f(x1,x2)-f(new_x1,x2))
            x1 = new_x1
            turn = 2
        else:
            new_x2 = golden(func_x2,x2-5000,x2+5000,0.0001,100)
            diff = abs(f(x1,x2)-f(x1,new_x2))
            x2 = new_x2
            turn = 1
        iter_ctr += 1
        results.append([iter_ctr,x1,x2,f(x1,x2)])
    return results
# this is the point where algorithm starts. points with high function value could cause
# overflow since the chosen function increases rapidly.
initial_point = [1,60]

ccs = cyclic_coordinate(func,initial_point)
print(tabulate(ccs[:10]+ccs[-10:],headers=['Iteration','x1','x2','f(x1,x2)']))
  Iteration        x1       x2       f(x1,x2)
-----------  --------  -------  -------------
          0   1        60         9.15052e+06
          1  11.798    60         0.839408
          2  11.798    59.7838    0.628533
          3  11.7551   59.7838    0.383139
          4  11.7551   59.5691    0.175354
          5  11.7124   59.5691   -0.0662981
          6  11.7124   59.3559   -0.271019
          7  11.6701   59.3559   -0.50907
          8  11.6701   59.1439   -0.711115
          9  11.6279   59.1439   -0.945628
        812   6.60187  33.8031  -27.4302
        813   6.60083  33.8031  -27.4303
        814   6.60083  33.7979  -27.4304
        815   6.59979  33.7979  -27.4305
        816   6.59979  33.7927  -27.4306
        817   6.59874  33.7927  -27.4307
        818   6.59874  33.7874  -27.4308
        819   6.5977   33.7874  -27.4309
        820   6.5977   33.7822  -27.431
        821   6.5967   33.7822  -27.4311

png

ccs=np.array(ccs)
fig,ax2 = plt.subplots()
cs = ax2.contour(X1,X2,f,50)
ax2.clabel(cs,inline=1,fontsize=10)
ax2.set_xlabel("X1")
ax2.set_ylabel("X2")
ax2.set_title('Contour Plot of Function')
plt.plot(ccs[:,1],ccs[:,2],'-.')
plt.gcf().set_size_inches(15, 10)

png

In this image every dot represent one iteration of cyclic cordinate search algorithm. Basically algorithm find a direction where function's value gets smaller and moves on that direction until function's value do not get smaller. Then it finds a new direction to move. On the next cells algorithm will be tried with a simpler function.