Introduction into Evolution Strategy

The Principle of Evolution

Evolution Strategies (ES) are a class of stochastic, population-based optimization algorithms inspired by natural evolution. They are "black-box" optimizers, meaning they treat the objective function as a mystery: they don't require gradients or knowledge of the function’s internal structure—they simply observe the output for a given input.

Mathematically, we seek to minimize an objective function $f(\mathbf{x})$ (in general a non-linear, non-convex function) with respect to a real-parameter vector $\mathbf{x} \in \mathbb{R}^n$ (continuous domain). $$f: \mathbb R^n \rightarrow \mathbb R$$ Typically, the global optimum is often neither feasible (NP-hard) nor relevant in practice. Therefore, we want to find $\mathbf x$-values where $f(\mathbf{x})$ is as small as possible.

Estimation of Distribution Algorithm (EDA)

Here, we just consider the family of Estimation of Distribution Algorithms. Unlike traditional Genetic Algorithms, which evolve a population of encoded individuals (genotypes) with genetic operators such as crossover and bit-flip mutation, an EDA maintains a probabilistic model of the search space. In each generation, it "estimates" where the best solutions lie and updates its parameters accordingly.

General Framework

  • $\lambda$: Population size (number of offspring per generation).
  • $\theta^{(g)}$: State parameters of the distribution at generation $g$ (e.g., mean, variance).
  • $P(x|\theta)$: The probability distribution (typically Gaussian) used to mutate the parents into offspring.
  • $F_\theta$: The update function for $\theta$: it adjusts $\theta$ so that the probability of generating the successful individuals from the current generation is maximized for the next.

The Algorithm and the Ask-Tell design

Initialization: Choose $\theta^{(0)}$

The optimization follows a repetitive four-step cycle:

  1. Sampling (Ask): The solver generates $\lambda$ new candidate solutions (offspring) independently from the current distribution:$$\mathbf{x}_1, \dots, \mathbf{x}_\lambda \sim P(\mathbf{x} | \theta^{(g)})$$
  2. Evaluation: Calculate the cost (objective value) for each offspring with the objective function: $$f_k = f(\mathbf{x}_k) \quad \text{for } k=1 \dots \lambda$$ The solver doesn't know how this function works; it only receives the resulting cost scores $f_k$.
  3. Tell & Update: The cost scores are returned to the solver. This is where the "intelligence" of the algorithm resides. The update function $F_\theta$ processes this feedback to re-estimate $\theta$, shifting the distribution toward high-quality (low-cost) regions: $$\theta^{(g+1)} = F_\theta\left(\theta^{(g)}, \{(\mathbf{x}_1, f_1), \dots, (\mathbf{x}_\lambda, f_\lambda)\}\right)$$ In modern Evolution Strategies, this update step implicitly includes Ranking, Selection, and Recombination.
  4. Iteration: Repeat until a termination criterion (e.g., target cost reached, max generations, or distribution collapse) is met.

The software component solver encapsulates the algorithmic engine that maintains a probabilistic model of the search space, proposing candidate solutions through sampling and iteratively refining its internal parameters based on fitness feedback.

Selection and Recombination: From Offspring to the New Mean

Before looking at a specific example, we must define how the algorithm chooses which solutions "win" and how it uses them to move forward. In ES literature, we use two Greek letters to describe the population:

  • $\lambda$ (Lambda): The number of offspring generated per generation. A larger $\lambda$ allows for better exploration but costs more function evaluations.
  • $\mu$ (Mu): The number of top-performing individuals selected to influence the next generation.

Selection: Who survives?

There are two primary ways to handle the transition between generations:

  • $(\mu, \lambda)$ Selection: The $\mu$ best are chosen only from the new offspring. The parents are discarded. This is often preferred because it allows the algorithm to "forget" bad areas and escape local optima.
  • $(\mu + \lambda)$ Selection: The $\mu$ best are chosen from both offspring and parents (elitism).

While these discrete selection methods were the standard in early Evolution Strategies, modern ES has evolved toward Recombination. Instead of treating the $\mu$ survivors as "individuals" that simply live to the next round, modern strategies treat them as statistical data points. We don't just "pick" the winners; we recombine them to update the center of our search distribution.

Recombination: Creating the new center

In a strategy where $\mu > 1$, we have multiple survivors. Instead of having each survivor start its own independent family, we merge their "knowledge." This process is called Recombination.

In Evolution Strategies, we use Intermediate Recombination. We calculate a single "Center of Mass" from all $\mu$ survivors. This new center becomes the mean $\mathbf{m}^{(g+1)}$ for the entire next generation. Mathematically: $$\mathbf{m}^{(g+1)} = \sum_{i=1}^{\mu} w_i \mathbf{x}_{i:\lambda}$$ Where:

  • $\mathbf{x}_{i:\lambda}$ represents the $i$-th best individual of the current generation.
  • $w_i$ are the weights assigned to each survivor.

Weighting Strategies: $(\mu/\mu, \lambda)$ vs. $(\mu/\mu_w, \lambda)$

How much influence should each survivor have? There are two main approaches:

Strategy Notation Description
Equal Weights $(\mu / \mu, \lambda)$ All $\mu$ survivors contribute equally ($w_i = 1/\mu$). The new mean is a simple average.
Weighted Recombination $(\mu / \mu_w, \lambda)$ The best individual has the highest weight, the second-best slightly less, and so on.

"Weighted Recombination" is explained in detail later. Let's look at an example before.

Why Recombine?

Recombination is generally more robust than selecting a single winner because it provides:

  • Noise Reduction: If one offspring is "lucky" due to a random jump in a noisy landscape, averaging it with other top performers pulls the search back toward a more reliable consensus.
  • Stability: The movement of the population center becomes smoother. Instead of "zig-zagging" toward the optimum by following single lucky points, the mean "drifts" steadily toward the best region.
  • Faster Convergence: In high-dimensional spaces, the average of several good points is often closer to the true optimum than any single point in the group.

Mutation and the Distribution Parameters $\theta$

Mutation is the process of adding stochastic (random) noise to the parameters to explore the search space. In Evolution Strategies, this is typically done using a Gaussian (Normal) Distribution.

In this context, the state of our search is defined by the parameter set $\theta$. In the most general case, $\theta$ is a composite of three elements: $$\theta = \{ \mathbf{m}, \sigma, \mathbf{C} \}$$

  • $\mathbf{m}$ (Location): The mean vector, representing the center of the "search cloud."
  • $\sigma$ (Scale): The step-size, controlling the global mutation strength.
  • $\mathbf{C}$ (Shape): The covariance matrix, determining the orientation and "stretch" of the cloud.

The standard mutation for an individual $i$ is the realization of the current model:

$$\mathbf{x}_i = \mathbf{m} + \sigma \cdot \mathcal{N}(0, \mathbf{C})$$ Where:

  • $\mathcal{N}(0, \mathbf{C})$: A random vector drawn from a normal distribution with mean $0$ and covariance matrix $\mathbf C$ .

Simplifying $\theta$ for Basic ES

In many basic Evolution Strategies, we do not yet try to learn the correlations between variables. If we fix the covariance matrix as the Identity matrix ($\mathbf{C} = \mathbf{I}$), our distribution remains perfectly spherical (isotropic). In this case, our state parameters simplify to:

$$\theta = \{ \mathbf{m}, \sigma \}$$

The "intelligence" of the algorithm resides in the update function $F_\theta$. It takes the results from the current generation and calculates the parameters for the next. For the simplified case where $\mathbf{C} = \mathbf{I}$, the function $F_\theta$ is composed of two distinct parts:

  1. Updating the Mean ($F_{\mathbf{m}}$): This "Update of the Mean" was already explained.
  2. Updating the Step-size ($F_\sigma$): This is explained later in section "Step-Size Control (Meta-Evolution)".

A Simple 2D Example: The $(2/2, 10)$ Strategy

Now, let's visualize this. Imagine we are searching for the minimum of a function $f(\mathbf{x})$. We use a $(2/2, 10)$ strategy: we create 10 offspring, and the top 2 are averaged to find the next center.

  1. Initialization: Start with a mean $\mathbf{m}^{(g)}$ and step-size $\sigma$.
  2. The Loop:
    • Sample: Generate 10 offspring $\mathbf{x}_k = \mathbf{m}^{(g)} + \sigma \mathcal{N}(0, \mathbf{I})$.
    • Evaluate: Calculate $f(\mathbf{x}_k)$ for all 10.
    • Select & Recombine: Rank them, take the 2 best, and calculate the new mean: $\mathbf{m}^{(g+1)} = \frac{1}{2}\mathbf{x}_{1:10} + \frac{1}{2}\mathbf{x}_{2:10}$.
  3. Iteration: The next 10 offspring are sampled from this updated $\mathbf{m}^{(g+1)}$.

Note: for clarity this example keeps the step-size $\sigma$ fixed. Adapting $\sigma$ is introduced in the "Step-Size Control" section below — with a constant $\sigma$ the search settles near the target but cannot tighten onto it.

In [1]:
import numpy as np
import matplotlib.pyplot as plt

# 1. Setup Parameters
mu_size = 2         # Number of parents to recombine
lambda_size = 10    # Total offspring per generation
sigma = 0.5
target = np.array([2.0, -3.0])

# Initialize a SINGLE mean (the center of the distribution)
m = np.array([-4.0, 4.0]) 

def evaluate(x):
    return np.sum((x - target)**2)

# Grid for the landscape visualization
x_grid = np.linspace(-6, 6, 100)
y_grid = np.linspace(-6, 6, 100)
X, Y = np.meshgrid(x_grid, y_grid)
Z = (X - target[0])**2 + (Y - target[1])**2

# Visualization setup
gens_to_plot = [0, 2, 9, 14]
fig, axes = plt.subplots(2, 2, figsize=(12, 11))
axes = axes.flatten()

def plot_generation(ax, gen, target, offspring, current_mean, winners, new_mean, is_first):
    ax.contourf(X, Y, Z, levels=25, cmap='viridis', alpha=0.2)
    ax.scatter(target[0], target[1], c='green', marker='*', s=200, label='Target', zorder=5)
    
    # Plot all offspring (The "Cloud")
    ax.scatter(offspring[:, 0], offspring[:, 1], c='blue', alpha=0.4, s=20, label='Offspring')
    
    # Plot current mean (The Center)
    ax.scatter(current_mean[0], current_mean[1], edgecolors='blue', marker='o', 
               facecolors='none', s=100, linewidth=2, label='Current Mean (m)')
    
    # Plot the survivors (The best mu)
    ax.scatter(winners[:, 0], winners[:, 1], c='red', marker='x', s=80, label='Best mu=2')
    
    # Plot the resulting new mean (The average of survivors)
    ax.scatter(new_mean[0], new_mean[1], c='red', marker='P', s=100, label='New Mean (m_next)', zorder=6)
        
    ax.set_title(f"Generation {gen}")
    ax.set_aspect('equal')
    if is_first: ax.legend(loc='upper right', fontsize='8')

# 2. The Evolution Loop (Ask-Tell)
plot_idx = 0
for gen in range(15):
    # --- ASK ---
    # All offspring are sampled from the same mean m
    noise = np.random.standard_normal((lambda_size, 2))
    offspring = m + sigma * noise
    
    # --- EVALUATE ---
    costs = np.array([evaluate(child) for child in offspring])
    
    # --- TELL ---
    # Sort by fitness and pick the mu_size best
    best_indices = np.argsort(costs)[:mu_size]
    winners = offspring[best_indices]
    
    # RECOMBINATION: Calculate the new mean (Intermediate Recombination)
    # Using equal weights (1/mu) for this example
    m_next = np.mean(winners, axis=0)
    
    # Snapshot plotting
    if gen in gens_to_plot:
        plot_generation(axes[plot_idx], gen, target, offspring, m, winners, m_next, plot_idx == 0)
        plot_idx += 1
    
    # UPDATE parameter for next generation
    m = m_next

plt.tight_layout()
plt.show()
No description has been provided for this image

Weighting Strategies

When the solver performs the Tell & Update step, it must decide how much to trust each survivor. While equal weights are simple, they treat the "champion" and the "borderline survivor" as equals.

Logarithmic Weighting

A common choice in modern ES (and the default in CMA-ES) is logarithmic weighting. This strategy penalizes lower-ranked individuals more harshly than a linear drop-off would. It ensures the "champion" has a significantly stronger influence on the new mean $\mathbf{m}^{(g+1)}$ than the mediocre survivors at the bottom of the top-$\mu$ list.

The raw weights $w'$ are calculated as:

$$w'_i = \ln(\mu + 0.5) - \ln(i) \quad \text{for } i = 1 \dots \mu$$

Normalization:

To ensure the weights sum to 1, we normalize the raw values:

$$w_i = \frac{w'_i}{\sum_{j=1}^{\mu} w'_j}$$

Example Weight Calculation ($\mu=5$)

In this example, we use the common logarithmic weighting scheme: $w'_i = \ln(\mu + 0.5) - \ln(i)$.

Rank ($i$) Formula: $\ln(5.5) - \ln(i)$ Raw Weight ($w'$) Normalized Weight ($w_i$)
1 (Best) $\ln(5.5) - \ln(1)$ $1.7047$ 0.456
2 $\ln(5.5) - \ln(2)$ $1.0116$ 0.271
3 $\ln(5.5) - \ln(3)$ $0.6061$ 0.162
4 $\ln(5.5) - \ln(4)$ $0.3185$ 0.085
5 $\ln(5.5) - \ln(5)$ $0.0953$ 0.026
Sum 3.7362 1.000

Notice that the Best individual has about 18 times the influence of the 5th individual. This aggressive weighting allows the search center to shift decisively toward the most promising direction while still benefiting from the "smoothing" effect of the other survivors.

Step-Size Control (Meta-Evolution)

One of the most critical challenges in ES is determining the value of $\sigma$ (mutation rate).

  • If $\sigma$ is too large: The algorithm "overshoots" the target and bounces around the optimum without ever settling.
  • If $\sigma$ is too small: The algorithm moves too slowly (crawls) or gets stuck in tiny local "potholes."

Step-size control allows the algorithm to automatically adapt $\sigma$ during the search. This is often called Meta-Evolution because the algorithm is evolving not just the solution, but also the parameters of the search itself.

The 1/5th Success Rule

One of the oldest and most intuitive methods for controlling step size is the 1/5th Success Rule. It is based on the observation that if more than 20% of mutations are successful (better than the parent), the search is likely in a smooth area and should move faster.

Observation Action Logic
Success rate > 1/5 Increase $\sigma = \sigma / 0.85$ ($\approx 1.17$) We are finding better points easily; we should explore further and "sprint."
Success rate < 1/5 Decrease $\sigma = \sigma * 0.85$ We are missing the "good" areas; we should search more locally to "settle."

Why $0.85$ for increase/decrease?

  • Stability: A $15\%$ change per generation allows the algorithm to adapt to the landscape's curvature without losing control.
  • Symmetry: Using $c$ and $1/c$ ensures that if the success rate is exactly $1/5$, the step size remains neutral over time.
  • Damping: In higher dimensions, the "volume" of the search space grows exponentially. Smaller multipliers prevent the "curse of dimensionality" from making $\sigma$ explode or collapse too quickly.

Modern Approach: Cumulative Step-Size Adaptation (CSA)

Modern algorithms like CMA-ES (later in this course) use Evolution Paths. Instead of just looking at the current generation, they track the direction the mean has moved over several generations.

  1. Consistent direction: If the mean has been moving in a straight line, $\sigma$ is increased (the algorithm "sprints").
  2. Changing direction: If the mean is zig-zagging or turning back, $\sigma$ is decreased (the algorithm "slows down" to investigate).
Scenario Movement Pattern Adjustment Goal
Sprinting Multiple generations move in the same direction. Increase $\sigma$ Reach the neighborhood of the optimum faster.
Zig-Zagging Generations jump back and forth across a valley. Decrease $\sigma$ Increase precision and "settle" into the minimum.
Stagnation No offspring are better than the parent. Increase $\sigma$ Break out of a local optimum or flat region.

So far our search cloud has been isotropic: with $\mathbf{C} = \mathbf{I}$, the offspring $\mathbf{x} = \mathbf{m} + \sigma\,\mathcal{N}(0, \mathbf{I})$ scatter equally in all directions — a circle (in 2D) or a sphere (in $n$D). This works well when the landscape looks the same in every direction near the optimum.

Real objective functions rarely do. Consider a long, narrow, tilted valley — the contour lines are stretched, rotated ellipses (a high condition number). A circular cloud now faces a dilemma:

  • To avoid overshooting the narrow direction, $\sigma$ must stay small.
  • But a small $\sigma$ means tiny steps along the long direction — so the search crawls down the valley.

The circle simply does not match the shape of the problem, and most samples land in unpromising directions. The figure below contrasts an isotropic cloud with one that has been reshaped to fit the valley.

In [2]:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

rng = np.random.default_rng(0)

# A tilted, elongated quadratic valley: f(x) = x^T A x, minimum at the origin.
theta = np.deg2rad(35)
Rrot = np.array([[np.cos(theta), -np.sin(theta)],
                 [np.sin(theta),  np.cos(theta)]])
A = Rrot @ np.diag([1.0, 12.0]) @ Rrot.T          # curvature ratio 12:1

def f(X, Y):
    return A[0, 0]*X**2 + (A[0, 1] + A[1, 0])*X*Y + A[1, 1]*Y**2

# Put the mean ON the valley floor (the low-curvature eigen-direction), away from
# the target, so the search must travel along the valley to reach the optimum.
floor_dir = Rrot @ np.array([1.0, 0.0])           # eigenvector with the small eigenvalue
m = 4.5 * floor_dir
sigma = 0.9

# Two search shapes, normalized to the SAME area (det = 1) so we compare
# orientation / shape only, not overall size.
C_iso = np.eye(2)
C_ada = np.linalg.inv(A)                           # optimal shape ~ inverse Hessian
C_ada /= np.sqrt(np.linalg.det(C_ada))            # area-normalize -> det = 1
noise = rng.standard_normal((50, 2))

lim = 7.0
xg = yg = np.linspace(-lim, lim, 200)
Xg, Yg = np.meshgrid(xg, yg)
Z = f(Xg, Yg)

fig, axes = plt.subplots(1, 2, figsize=(13, 6))
for ax, C, title in [(axes[0], C_iso, "Isotropic  N(0, I):  circular"),
                     (axes[1], C_ada, "Adapted  N(0, C):  aligned to the valley")]:
    ax.contourf(Xg, Yg, np.log1p(Z), levels=30, cmap='viridis', alpha=0.35)  # log scale reveals the narrow floor
    L = np.linalg.cholesky(C)
    pts = m + sigma * noise @ L.T
    ax.scatter(pts[:, 0], pts[:, 1], c='red', s=16, alpha=0.6, label='Offspring')
    ax.scatter(*m, c='black', marker='o', s=60, zorder=6, label='Mean m')
    ax.scatter(0, 0, c='lime', marker='*', s=250, zorder=6, label='Target (optimum)')

    # The 2-sigma search ellipse = the actual shape of N(0, sigma^2 C)
    vals, vecs = np.linalg.eigh(C)                 # ascending eigenvalues
    angle = np.degrees(np.arctan2(vecs[1, -1], vecs[0, -1]))   # major-axis direction
    width, height = 2 * 2.0 * sigma * np.sqrt(vals[::-1])      # full axis lengths at 2 sigma
    ax.add_patch(Ellipse(m, width, height, angle=angle, edgecolor='blue',
                         fc='none', lw=2, ls='--', label=r'Search area (2$\sigma$)'))

    ax.set_title(title); ax.set_aspect('equal')
    ax.set_xlim(-lim, lim); ax.set_ylim(-lim, lim)
    ax.legend(loc='upper right', fontsize=8)
plt.tight_layout(); plt.show()
No description has been provided for this image

Shaping the Cloud = Learning the Geometry

The right-hand cloud is clearly better matched to the valley: it takes large steps along the long direction and small, careful steps across the narrow one. That elongated, rotated ellipse is nothing more than the geometry of $\mathcal{N}(0, \mathbf{C})$ — the covariance matrix $\mathbf{C}$ stretches (through its eigenvalues) and rotates (through its eigenvectors) the search.

But we don't know the valley's shape in advance — that is the whole point of a black-box optimizer. The key insight of CMA-ES: the successful offspring reveal it for us. If the winners of a generation cluster along a particular direction, that direction is promising — so we adapt $\mathbf{C}$ toward the covariance of the selected winners (a small, incremental update each generation rather than a full reset) and sample the next generation from the updated shape. The cloud gradually molds itself to the local landscape, while $\sigma$ remains the global scale on top of it.

The Covariance Connection

This closes the arc of the course. The same covariance matrix you have already met reappears here as the engine of the search:

  • PCA: the eigen-decomposition of $\mathbf{C}$ gives the principal axes of the search ellipse — the directions of largest and smallest step.
  • Hessian / Newton: an adapted $\mathbf{C}$ approximates the inverse local curvature ($\mathbf{C} \approx \mathbf{H}^{-1}$), so CMA-ES behaves like a gradient-free version of Newton's method — the classical fast optimizer that uses the curvature to rescale its steps (explained just below).
  • Kalman filter: $\mathbf{C}$ is again a "shape of uncertainty" — here, uncertainty about where the optimum lies.

In one paragraph — what is the Hessian $\mathbf{H}$? It is the matrix of second derivatives of $f$; it measures the local curvature, i.e. how fast the slope changes as you move. For an elliptical valley its eigenvectors point along the ellipse's axes and its eigenvalues are the curvatures — a large eigenvalue means a sharply-curving, narrow direction, a small one a gently-curving, long direction. The inverse $\mathbf{H}^{-1}$ is therefore large along the long directions and small along the narrow ones: exactly the shape the search cloud should have.

And what is Newton's method? It is the classical fast optimizer that exploits this curvature. Plain gradient descent just steps along the slope (the gradient), so it zig-zags in a narrow valley. Newton's method instead multiplies the gradient by $\mathbf{H}^{-1}$, which rescales every step by the local curvature — large along the gently-curving directions, small across the sharply-curving ones — and so heads almost straight to the minimum. Learning $\mathbf{C} \approx \mathbf{H}^{-1}$ gives CMA-ES this very same step-rescaling, but without any gradient or derivative: it reads the shape off the ranking of the sampled points alone (a "second-order" behaviour achieved by a black-box method).

And conjugate directions? Two directions are conjugate (with respect to the curvature) if optimizing along one does not undo the progress already made along the other. Searching along such non-interfering directions avoids the slow zig-zag that plain gradient descent shows in a narrow valley; CMA-ES's step-size path (CSA) steers the search toward them for the same reason.

For the full derivations see Hessian-matrix and conjugate-directions.

Basic ES fixes $\mathbf{C} = \mathbf{I}$ and only adapts $\mathbf{m}$ and $\sigma$. CMA-ES adds the missing ingredient — it learns $\mathbf{C}$ — which is why it is the state-of-the-art black-box optimizer.

The Cost of Learning $\mathbf{C}$ — and Leaner Alternatives

Learning the full covariance matrix is powerful, but it is not free. Since $\mathbf{C}$ is a symmetric $n \times n$ matrix, it has $\tfrac{n(n+1)}{2} = \mathcal{O}(n^2)$ free parameters. Storing it costs $\mathcal{O}(n^2)$ memory, and — more importantly — learning that many numbers reliably takes on the order of $\mathcal{O}(n^2)$ function evaluations. For small or moderate dimension $n$ this is well spent; in high dimensions it becomes prohibitive.

The remedy is to restrict the shape of $\mathbf{C}$ to fewer parameters, trading generality for speed:

  • Diagonal covariance — keep only the $n$ diagonal entries of $\mathbf{C}$ (one variance per coordinate; $\mathcal{O}(n)$ parameters). The search ellipsoid can then stretch along the coordinate axes but not rotate. This learns roughly $n$ times faster and is the right choice for separable or nearly-separable problems — those where the variables are only weakly coupled, so the promising directions already line up with the coordinate axes. (This is the idea behind separable variants such as sep-CMA-ES.)

  • Low-rank + diagonal covariance — represent $\mathbf{C}$ as a diagonal plus a few $k \ll n$ dominant directions ($\mathcal{O}(n\,k)$ parameters). This captures the most important correlated directions while ignoring the rest. It suits very high-dimensional problems with a low effective dimensionality — where the difficulty is concentrated in a handful of coupled directions rather than spread across all $\binom{n}{2}$ variable pairs. (This is the idea behind limited-memory variants such as LM-CMA and VkD-CMA.)

Rule of thumb: use the full $\mathbf{C}$ for moderate $n$, or whenever correlations are strong or unknown (it is the robust default and cannot be hurt by correlations); use a diagonal model for separable / weakly-correlated problems; use a low-rank + diagonal model in very high dimension when only a few directions truly matter.

Summary: From Basic ES to CMA-ES

Principle Basic Evolution Strategy CMA-ES (The Next Step)
Mutation Circular: Uses an Identity Matrix. All directions are explored with equal probability. Ellipsoidal: Learns a Covariance Matrix ($C$) to "stretch" and "rotate" the search toward the goal.
Recombination Weighted Mean: The center of the next generation is the average of the survivors. Strategic Mean: The mean update is used to calculate the "Evolution Path" and the new matrix.
Step-Size Control 1/5th Success Rule: Reactive adjustment based on the ratio of "better" offspring. Cumulative Step Adaptation (CSA): Analyzes the search path to detect "sprinting" vs. "zig-zagging."
Selection Rank-based $(\mu/\mu_w, \lambda)$: Only the fitness rank matters, not the absolute values. Same Logic: Remains a robust, gradient-free black-box optimizer.