#!/usr/bin/env python
# coding: utf-8

# In[10]:


#n iterations of golden section search
#to find a local minimum of f
from numpy import sqrt
def gs(f, a, b, n):
    phi = (sqrt(5)+1)/2 #golden ratio
    for i in range(n):
        c = a + (phi-1)*(b-a)
        d = a + (2 - phi)*(b-a)
        #print([a, b, c, d])
        fc = f(c)
        fd = f(d)
        if fc < fd:
            a = b           
            b = d
        else:
            b = c
    return c
    


# In[38]:


from numpy import sin
f = lambda x: x - 5*sin(x)


# In[4]:


from matplotlib.pyplot import plot
from numpy import linspace
p = linspace(-10, 10, 200)
y = f(p)
plot(p, y)


# In[11]:


gs(f, 1, 2, 30)


# In[17]:


#Newton's method applied for optimization
def newton_o_while(df, d2f, a):
    not_converged = True
    tol = 1E-6
    max_iter = 100
    x = a
    iter = 0
    while not_converged:
        change = df(x)/d2f(x)
        not_converged = (abs(change) > tol)
        x = x - change
        print(x)
        iter = iter + 1
        if iter > max_iter:
            print('No convergence after maximum number of iterations')
            break
    return x


# In[19]:


from numpy import cos
df = lambda x: 1 - 5*cos(x)
d2f = lambda x: 5*sin(x)
x = newton_o_while(df, d2f, 1)


# In[39]:


from scipy.optimize import minimize_scalar
minimize_scalar(f, [1, 2])


# In[30]:


#minimization of a function of multiple variables (collected in a vector)
from numpy import exp
g = lambda x: x[0]**2 + x[1]**2 - x[0]*x[1] - exp(-(x[0]**2)) + 10 * cos(x[0] - x[1])
grad = lambda x: [2*x[0] - x[1] + 2 * x[0] * exp(-(x[0]**2)) - 10 * sin(x[0] - x[1]),
                  2*x[1] - x[0] + 10 * sin(x[0] - x[1])]
H = lambda x: [[2 + (2 - 4 * x[0]**2) * exp(-(x[0]**2)) - 10 * cos(x[0] - x[1]),
                -1 + 10 * cos(x[0] - x[1])],
               [-1 + 10 * cos(x[0] - x[1]), 
                2 - 10 * cos(x[0] - x[1])]]


# In[20]:


#visualize the function
from numpy import meshgrid
x0 = linspace(-2, 2, 50)
x1 = linspace(-2, 2, 50)
X0, X1 = meshgrid(x0, x1)


# In[23]:


y = g([X0, X1])


# In[27]:


X0[0, 0], X1[0, 0], g([-2, -2]), y[0, 0]


# In[29]:


#filled contour plot of 2-D function
#see https://matplotlib.org/stable/gallery/images_contours_and_fields/contourf_demo.html
from matplotlib.pyplot import subplots
fig1, ax2 = subplots()
CS = ax2.contourf(X0, X1, y)
cbar = fig1.colorbar(CS)


# In[33]:


x = [1, -1]
g(x), grad(x), H(x)


# In[34]:


#does one iteration of Newton's method for a system of equations
from numpy.linalg import solve
def newton_s(grad, H, a):
    x = a - solve(H(a), grad(a))
    return x


# In[36]:


a = [1, -1]
for i in range(5):
    a = newton_s(grad, H, a)
    print(a)


# In[40]:


from scipy.optimize import minimize
minimize(g, [1, -1])


# In[41]:


minimize(g, [1, -1], jac=grad, hess=H)


# In[ ]:




