from numpy import cos, arctan2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
plt.rcParams["font.size"] = 10
plt.rcParams["mathtext.fontset"] = "cm"
# Minimization
def nelder_mead_step(fun, verts, alpha=1, gamma=2, rho=0.5,
sigma=0.5, beta=1.0):
"""Nelder-Mead iteration according to Wikipedia _[1]
References
----------
.. [1] Wikipedia contributors. "Nelder–Mead method." Wikipedia,
The Free Encyclopedia. Wikipedia, The Free Encyclopedia,
1 Sep. 2016. Web. 20 Sep. 2016.
"""
nverts, _ = verts.shape
f = np.apply_along_axis(fun, 1, verts, beta=beta)
# 1. Order
order = np.argsort(f)
verts = verts[order, :]
f = f[order]
# 2. Calculate xo, the centroid"
xo = verts[:-1, :].mean(axis=0)
# 3. Reflection
xr = xo + alpha*(xo - verts[-1, :])
fr = fun(xr, beta)
if f[0]<=fr and fr<f[-2]:
new_verts = np.vstack((verts[:-1, :], xr))
# 4. Expansion
elif fr<f[0]:
xe = xo + gamma*(xr - xo)
fe = fun(xe, beta)
if fe < fr:
new_verts = np.vstack((verts[:-1, :], xe))
else:
new_verts = np.vstack((verts[:-1, :], xe))
# 5. Contraction
else:
xc = xo + rho*(verts[-1, :] - xo)
fc = fun(xc, beta)
if fc < f[-1]:
new_verts = np.vstack((verts[:-1, :], xc))
# 6. Shrink
else:
new_verts = np.zeros_like(verts)
new_verts[0, :] = verts[0, :]
for k in range(1, nverts):
new_verts[k, :] = sigma*(verts[k,:] - verts[0,:])
return new_verts
def fun(x, beta=1.0):
"""Simionescu function using log-barrier method"""
x1, x2 = x
if x1**2 + x2**2 < (1 + 0.2*cos(8*arctan2(x1, x2)))**2:
barrier = -beta*np.log((1 + 0.2*cos(8*arctan2(x1, x2)))**2 - x1**2 - x2**2)
else:
barrier = np.inf
return x1*x2 + barrier
# Animation
def data_gen(num):
plt.gca().cla
x0 = np.array([0.4, -0.6])
x1 = np.array([-0.3, -0.6])
x2 = np.array([0.7, 0.6])
verts = np.vstack((x0, x1, x2))
beta = 1.0
for cont in range(num):
verts = nelder_mead_step(fun, verts, beta=beta)
beta /=2
# Plots
plt.cla()
poly = plt.Polygon(verts, facecolor="none", edgecolor="k",
linewidth=0.5, zorder=4)
plt.gca().add_patch(poly)
x1, x2 = np.mgrid[-1.25:1.25:101j, -1.25:1.25:101j]
z = x1*x2
cons = x1**2 + x2**2 - (1 + 0.2*cos(8*arctan2(x1, x2)))**2
z[cons > 0.02] = np.nan
levels = np.linspace(-1, 1, 30)
plt.contour(x1, x2, z, levels, cmap="seismic", linewidths=1)
plt.contour(x1, x2, cons, [0], colors="black", linewidths=1)
plt.axis("image")
plt.xlabel(r"$x$", fontsize=14)
plt.ylabel(r"$y$", fontsize=14)
fig = plt.figure(figsize=(5, 5))
ani = animation.FuncAnimation(fig, data_gen, range(25))
ani.save("Nelder-Mead_Simionescu.gif", writer='imagemagick', fps=2,
dpi=200)
plt.show()