Skip to main content
6 of 8
added 30 characters in body
DCM
  • 778
  • 3
  • 9

I suppose one general situation where Nelder-Mead might perform better is when the objective function is 'simple' at the length scale of the initial simplex, but is very oscillatory at the 'microscopic' scale; in this sort of situation, Nelder-Mead won't 'see' the complicated local behaviour, whereas a derivative-based method could be confused and impeded by it.

For a concrete example on the real line, I had a play at finding the global minimum of $x\longmapsto x^2 + \sin(1000 x)$ with different initial conditions:

enter image description here

Zooming in to look at what went wrong for BFGS:

enter image description here

I've included the Python 3 code used to generate the plots in case that's helpful :)

"""
Script for illustrating one situation when derivative-based optimisation methods can get into trouble.
"""

import matplotlib.pyplot as plt
import numpy as np
import sys
from scipy.optimize import minimize

# Read inputs from command line
amplitude = float(sys.argv[3])  # e.g. 1.0
wavelength = float(sys.argv[4])  # e.g. 1.0e-3

# Set up objective function and its derivative
objective = lambda x: np.square(x) + amplitude * np.sin(x / wavelength)
derivative = lambda x: 2 * x + (amplitude / wavelength) * np.cos(x / wavelength)

# Set up a starting position and a large initial simplex
starting_position = 100
initial_simplex_length = 10
initial_simplex = np.array([
    [starting_position],
    [starting_position + initial_simplex_length]
])

# Minimise using NM
results = {}
results["Nelder-Mead"] = minimize(
    objective,
    starting_position,
    method="Nelder-Mead",
    options={
        "initial_simplex": initial_simplex
    }
)

# Minimise using BFGS
results["BFGS"] = minimize(
    objective,
    starting_position,
    method="BFGS",
    jac=derivative
)

# Initialise plot
_, ax = plt.subplots()

# Plot objective function
xs = np.arange(-0.2 * starting_position, 1.2 * starting_position, 0.5 * wavelength)
ax.plot(xs, objective(xs))

# Mark minima found by both methods
for method, result in results.items():
    ax.scatter(
        [np.squeeze(result.x)],
        [result.fun],
        label=method
    )

ax.legend()
ax.set_title(
    f"Nelder-Mead vs BFGS on x^2 + {amplitude} sin(x / {wavelength})\n"
    f"with x0 = {starting_position}, initial_simplex = {np.squeeze(initial_simplex)}"
)
plt.show()

# Print results
print(results)
DCM
  • 778
  • 3
  • 9