Covariance Matrix Adaptation Evolution Strategy (CMA-ES)
A gradient-free optimizer that keeps reshaping a Gaussian cloud of tries. It slides the cloud toward the winners and stretches it along directions that keep paying off.
Why Does This Exist?
Some problems hand you no gradient. The simulator will not differentiate, the score lands once per episode, or the surface jumps. Stochastic gradient descent has nothing to chew on there, and even policy gradient methods want a gradient signal you may not trust.
Plain evolution fills the gap with a fixed cloud: sample around a mean, keep the best, move on. Round clouds waste samples on stretched problems. Picture one dial a hundred times touchier than the next, or a thin curved valley. Too wide across the valley, too timid along it. For the general spread question, see exploration vs exploitation and evolution strategies.
CMA-ES learns the cloud instead of hand-setting it. Each generation it relearns the center, the shape, and the stride from the winners. This page teaches that loop.
Think of It Like This
A rescue crew in a foggy valley
A crew hunts a lost hiker from a base camp. Each night searchers fan out and report back.
The camp is the mean. It moves toward the weighted average of the best reports, trusting the strongest signal most.
The search pattern is the covariance. When good news keeps coming from the northeast, tomorrow's pattern stretches northeast instead of staying round.
The lantern range is the step size. Nights that pull one way mean stride farther. Nights that zigzag mean tighten up.
Where it stops: real searchers chat and dodge cliffs. These samples are lone Gaussian draws with no memory and no obstacles.
How It Actually Works
One distribution rules every generation:
Here is the mean, the current best guess at the center. The scalar is the step size, one global stride. The matrix is the covariance, whose axes set the ellipse directions and whose eigenvalues set their squared lengths. Each draw is standard normal, reshaped by and scaled by into a candidate .
Each round samples candidates, scores them with fitness (lower better here), ranks them best first, and keeps the top with fixed weights summing to 1. Only the ranking matters, never the raw scores.
Worked example
Start at with and . Three samples return ranked:
- , weight
- , weight
- , weight
New mean is the weighted mix . First coordinate: . Second: . So .
Selection mass is , so this round counts as about two and a half independent winners.
How shape and stride follow
Covariance blends three terms: most of the old , a rank-one stretch along the multi-round path , and a rank- stretch along this round's winning steps. One lucky round cannot yank it because the learning rates and stay small and keep positive-definite.
Step size watches a second path . Marches in one direction grow it past its random baseline near in two dimensions, so grows and strides lengthen. Zigzags shrink it and the cloud fine-tunes.
Ranking-only choice buys three invariances: shifting the problem changes nothing, rotating it costs the same since learns orientation, and any increasing rescale of behaves identically. The bill is samples: populations grow like and full learning costs on the order of in dimension .
Code
The weighted mean step in plain Python:
samples: list[tuple[float, float]] = [(1.0, 0.5), (0.2, 1.2), (-0.8, -0.3)]weights: list[float] = [0.5, 0.3, 0.2]
mean_x: float = sum(w * x for w, (x, _) in zip(weights, samples))mean_y: float = sum(w * y for w, (_, y) in zip(weights, samples))mu_eff: float = 1.0 / sum(w * w for w in weights)
print(f"{mean_x:.2f} {mean_y:.2f}") # -> 0.40 0.55print(f"{mu_eff:.2f}") # -> 2.63Both lines match the hand arithmetic. Swap the weights and watch the mean chase the heaviest try.
Watch Out For
Covariance collapse freezes the search
Symptom: fitness stalls early while and the ellipse axes shrink toward zero, and restarts land in the same spot. A tiny starting on flat ground is the usual trigger: chance clusters teach to stay tiny.
Fix: start near a quarter of your expected range and floor it. If the longest axis outgrows the shortest past about , restart with fresh covariance.
Step size blows up on noisy scores
Symptom: on few-episode rollouts, keeps growing while the true mean barely moves. Noise reshuffles ranks each round and the path test reads churn as progress.
Fix: score each candidate over more episodes or widen the population so ranks average the noise out. Fix the evaluation budget first, never hand-tune to compensate.
The Quick Version
- Candidates come from one Gaussian and the mean moves to the weighted winners, here from weights .
- Covariance pairs a rank- stretch along this round with a rank-one stretch along the multi-round path.
- Step size grows on steady marches and shrinks on zigzags against a random baseline near in two dimensions.
- Rank-only selection gives translation, rotation, and rescale invariance for free.
- No gradients needed, which suits black-box simulators, but full shape learning costs on the order of samples.