A Practical Global Optimization Example
Optimization problems are everywhere in engineering, machine learning, and data science, but not all objective functions are easy to minimize. Some are riddled with local minima that trap naive algorithms long before they reach the true global minimum. The Ackley function is one of the most famous benchmark functions used to test how well an optimization algorithm can escape these traps. In this article, we’ll explore the Ackley function in depth, minimize it using Python, and visualize the results in both 2D and 3D.
What Is the Ackley Function?
The Ackley function is a widely used benchmark for testing global optimization algorithms because of its nearly flat outer region combined with a large number of local minima near the center. In two dimensions, it is defined as:
$$
f(x, y) = -20 \exp\left(-0.2 \sqrt{0.5(x^2 + y^2)}\right) - \exp\left(0.5(\cos(2\pi x) + \cos(2\pi y))\right) + e + 20
$$
The global minimum is located at $(x, y) = (0, 0)$, where $f(0, 0) = 0$. What makes this function tricky is the combination of an exponential term that creates a huge, nearly flat “bowl” and a cosine term that riddles the surface with countless small local minima. A simple gradient-descent-based method will almost always get stuck in one of these local minima instead of finding the true global minimum at the origin.
Why Use a Global Optimization Algorithm?
Because of the many local minima, a gradient-based local optimizer (like scipy.optimize.minimize with BFGS) is not reliable here unless it starts very close to the global minimum. Instead, we need a global optimization algorithm. In this example, we use scipy.optimize.differential_evolution, a population-based evolutionary algorithm that explores the search space broadly before converging, making it much more robust against local minima traps.
Python Implementation
Below is the complete, self-contained source code. It defines the Ackley function, runs the global optimization, prints the results, and generates both a 3D surface plot and a 2D contour plot with the discovered minimum marked on it. The differential evolution step uses workers=-1 to parallelize across all available CPU cores, which significantly speeds up the search compared to the default single-threaded execution.
1 | import numpy as np |
Optimization successful: True Number of iterations: 123 Best solution found: x = 0.000000, y = 0.000000 Minimum function value: f(x, y) = 0.0000000000

Code Walkthrough
1. Defining the Ackley Function
The ackley() function directly implements the mathematical formula introduced earlier. It takes a 2-element array pos (representing $x$ and $y$) and returns a single scalar value. The parameters a=20, b=0.2, and c=2π are the standard constants used in the canonical definition of the function, and keeping them as arguments makes the function reusable for variations of the benchmark.
2. Global Optimization with Differential Evolution
differential_evolution maintains a population of candidate solutions and iteratively “evolves” them using mutation, crossover, and selection — a strategy inspired by biological evolution. This makes it far more resistant to getting trapped in the countless small local minima created by the cosine terms compared to gradient-based methods.
Key parameters worth understanding:
bounds: Defines the search space, here $[-5, 5]$ for both $x$ and $y$, which comfortably contains the interesting region of the function.strategy='best1bin': A standard and reliable mutation strategy that tends to converge well on smooth, bowl-shaped benchmark functions like this one.popsize=20: Controls how many candidate solutions are evaluated per generation. Larger values improve robustness at the cost of speed.tol=1e-10: A tight convergence tolerance to make sure the algorithm doesn’t stop prematurely before finding a highly precise minimum.workers=-1andupdating='deferred': These two settings together enable multi-core parallel evaluation of the population, which is the key speed-up trick here. Without them,differential_evolutionevaluates each candidate solution sequentially; with them, all available CPU cores are used simultaneously, cutting runtime significantly — especially valuable if you extend this example to higher dimensions or more expensive objective functions.
After the optimization finishes, the script prints whether the run converged successfully, how many generations it took, and the best $(x, y)$ pair found along with its function value, which should be extremely close to the true global minimum of $0$ at $(0, 0)$.
3. Building the Visualization Grid
To visualize the function, we create a dense $200 \times 200$ grid of $(x, y)$ points spanning the search space using np.meshgrid, then evaluate the Ackley function across the entire grid at once using NumPy’s vectorized operations. This is far faster than looping over each point individually in Python.
4. Two Complementary Plots
- 3D Surface Plot: This shows the overall shape of the function — a broad, nearly flat plateau near the edges that drops sharply into a narrow, spiky funnel near the center. The red marker highlights exactly where the optimizer converged, letting you visually confirm it landed at the bottom of the funnel rather than on one of the small surrounding bumps.
- 2D Contour Plot: This provides a top-down view using color gradients to represent function value, making it easy to see the ring-like pattern of local minima surrounding the true global minimum. The star marker again shows the optimizer’s final solution, with its exact coordinates included in the legend.
Together, these two plots make it intuitive to understand both the global structure of the Ackley function and why gradient-based local search methods struggle with it — the small ripples visible in the contour plot near the center are individual local minima that a naive optimizer could easily get stuck in.
Conclusion
The Ackley function is a great illustration of why the choice of algorithm matters as much as the implementation when tackling non-convex optimization problems. By using differential_evolution, a population-based global optimizer, we reliably converge to the true minimum at the origin — even though the function’s surface is filled with deceptive local minima. The parallelized workers=-1 setting also demonstrates a practical, simple way to speed up evolutionary optimization runs on any multi-core machine, which becomes increasingly valuable as the problem dimensionality grows.
























