Differential Evolution › Example
Machine Learning from ScratchAll chapters Differential Evolution › ExampleSection view · Sections in this chapter5 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
We work through half a generation of DE/rand/1/bin by hand on a two-variable problem with a known answer, so that every step of the machinery above becomes a concrete number. The objective is
whose unique minimum is at . The box is , the mutation factor is , and the crossover rate is . Our population of four members, with their objective values, is:
Let us verify one of these values to fix the arithmetic. For we get and , so . Repeating: , , and . So the current best is with .
Take the first target and donors , , . The difference vector is
which is a step of two units left and two units up, a displacement the population happens to contain. Scaling by gives , and adding it to gives the mutant
Now crossover with . Suppose the uniform draws are for the coordinate and for the coordinate, and the forced index is , the coordinate. For : , so we take the mutant value (and it would have been forced anyway). For : and , so we keep the target value . The trial is
Since , the greedy rule accepts and slot 1 becomes . The mutation displacement points left from its base to the mutant . Relative to the target , however, crossover moves rightward to and leaves unchanged. The squared error drops from to , accounting for the entire cost decrease. A donor-difference direction must be read from its base, not from the target.
Now take the second target with donors , , , using generation-0 values for all members. The difference vector is now , scaled to , and the mutant is
With draws and and forced index , the coordinate is inherited from the mutant () and the coordinate is kept from the target (), giving . Its value is
and , so the trial is discarded and survives unchanged. This is the greedy rule doing its job: the rejected trial has , whereas has ; the trial's inherited coordinate of is farther from the optimum than the target's , so changing alone left the trial worse than the target.
Both trials cost an objective evaluation, including the rejected trial. A rejection means this completed crossover trial was worse than its target; it does not isolate the mutation direction as the cause. Both hand calculations use generation-0 donors, including the old value of slot 1 after its replacement has been proposed. That is the deferred convention.
We can check the arithmetic with a helper that takes explicit donor vectors, uniform draws, and the forced-coordinate index. We compute the four initial objective values and two trial values once, then reuse those values for display and plotting. These six hand-example candidate evaluations are separate from the later Rastrigin search budgets.
PythonIn [5]Expand
def f_example(X):
"""Vectorized objective f(x, y) = (x - 1)^2 + (y + 2)^2 for a 2D array of points."""
X = np.atleast_2d(X)
return (X[:, 0] - 1.0) ** 2 + (X[:, 1] + 2.0) ** 2
def de_trial_manual(target, r1, r2, r3, F, CR, j_rand, rand):
"""One DE/rand/1/bin trial with all randomness supplied by the caller (1-based j_rand)."""
mutant = r1 + F * (r2 - r3)
mask = rand < CR
mask[j_rand - 1] = True
trial = np.where(mask, mutant, target)
return mutant, mask, trial
pop0 = np.array(
[
[-3.0, 2.0], # x1
[4.0, 1.0], # x2
[0.0, -4.0], # x3
[2.0, 3.0],
]
) # x4
f0 = f_example(pop0)
F, CR = 0.8, 0.5
v1, m1, u1 = de_trial_manual(
pop0[0], pop0[2], pop0[3], pop0[1], F, CR, 1, np.array([0.3, 0.9])
)
v3, m3, u3 = de_trial_manual(
pop0[2], pop0[0], pop0[1], pop0[3], F, CR, 1, np.array([0.2, 0.8])
)
trial_values = f_example(np.vstack([u1, u3]))
accepted_values = trial_values <= f0[[0, 2]]ConsoleOut [6]Expand
objective values of generation 0: [32. 18. 5. 26.] target i=1 mutant : [-1.6 -2.4] mask : [ True False] trial : [-1.6 2. ] f(trial) : 22.7600 f(target) = 32.0000 -> accept target i=3 mutant : [-1.4 0.4] mask : [ True False] trial : [-1.4 -4. ] f(trial) : 9.7600 f(target) = 5.0000 -> reject
The displayed values match the hand calculation: the first trial has cost and is accepted against ; the second trial has cost and is rejected against . If values disagree, check the arithmetic, formulas, indexing, and code together. Agreement on these two updates is a focused consistency check, not a proof for every input. The masks show which coordinates are selected from the mutant.
The printed values already give us the coordinates of both updates; plotting them makes the selection outcome visible. The fixed example uses hand-supplied random draws, so every point is the deterministic result of the hand-chosen donors and crossover draws.
The preparation cell collects the existing targets, mutants, trials, and stored selection outcomes for plotting. It introduces no new random draws or objective evaluations.
VisualizationOut [8]Expand

Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!