1 """Differential Evolution Optimization
2
3 :Author: Robert Kern
4
5 Copyright 2005 by Robert Kern.
6 """
7
8 import numpy as np
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
57 """Minimize a function using differential evolution.
58
59 Constructors
60 ------------
61 DiffEvolver(func, pop0, args=(), crossover_rate=0.5, scale=None,
62 strategy=('rand', 2, 'bin'), eps=1e-6)
63 func -- function to minimize
64 pop0 -- sequence of initial vectors
65 args -- additional arguments to apply to func
66 crossover_rate -- crossover probability [0..1] usually 0.5 or so
67 scale -- scaling factor to apply to differences [0..1] usually > 0.5
68 if None, then calculated from pop0 using a heuristic
69 strategy -- tuple specifying the differencing/crossover strategy
70 The first element is one of 'rand', 'best', 'rand-to-best' to specify
71 how to obtain an initial trial vector.
72 The second element is either 1 or 2 (or only 1 for 'rand-to-best') to
73 specify the number of difference vectors to add to the initial trial.
74 The third element is (currently) 'bin' to specify binomial crossover.
75 eps -- if the maximum and minimum function values of a given generation are
76 with eps of each other, convergence has been achieved.
77 prng -- a RandomState instance. By default, this is the global
78 numpy.random instance.
79
80 DiffEvolver.frombounds(func, lbound, ubound, npop, crossover_rate=0.5,
81 scale=None, strategy=('rand', 2, 'bin'), eps=1e-6)
82 Randomly initialize the population within given rectangular bounds.
83 lbound -- lower bound vector
84 ubound -- upper bound vector
85 npop -- size of population
86
87 Public Methods
88 --------------
89 solve(newgens=100)
90 Run the minimizer for newgens more generations. Return the best parameter
91 vector from the whole run.
92
93 Public Members
94 --------------
95 best_value -- lowest function value in the history
96 best_vector -- minimizing vector
97 best_val_history -- list of best_value's for each generation
98 best_vec_history -- list of best_vector's for each generation
99 population -- current population
100 pop_values -- respective function values for each of the current population
101 generations -- number of generations already computed
102 func, args, crossover_rate, scale, strategy, eps -- from constructor
103 """
104 - def __init__(self, func, pop0, args=(), crossover_rate=0.5, scale=None,
105 strategy=('rand', 2, 'bin'), eps=1e-6, prng=np.random):
106 self.func = func
107 self.population = np.array(pop0)
108 self.npop, self.ndim = self.population.shape
109 self.args = args
110 self.crossover_rate = crossover_rate
111 self.strategy = strategy
112 self.eps = eps
113
114 self.pop_values = [self.func(m, *args) for m in self.population]
115 bestidx = np.argmin(self.pop_values)
116 self.best_vector = self.population[bestidx]
117 self.best_value = self.pop_values[bestidx]
118
119 if scale is None:
120 self.scale = self.calculate_scale()
121 else:
122 self.scale = scale
123
124 self.generations = 0
125 self.best_val_history = []
126 self.best_vec_history = []
127
128 self.jump_table = {
129 ('rand', 1, 'bin'): (self.choose_rand, self.diff1, self.bin_crossover),
130 ('rand', 2, 'bin'): (self.choose_rand, self.diff2, self.bin_crossover),
131 ('best', 1, 'bin'): (self.choose_best, self.diff1, self.bin_crossover),
132 ('best', 2, 'bin'): (self.choose_best, self.diff2, self.bin_crossover),
133 ('rand-to-best', 1, 'bin'):
134 (self.choose_rand_to_best, self.diff1, self.bin_crossover),
135 }
136
138 self.best_val_history = []
139 self.best_vec_history = []
140 self.generations = 0
141 self.pop_values = [self.func(m, *self.args) for m in self.population]
142
143 - def frombounds(cls, func, lbound, ubound, npop, crossover_rate=0.5,
144 scale=None, strategy=('rand', 2, 'bin'), eps=1e-6, prng=np.random):
145 lbound = np.asarray(lbound)
146 ubound = np.asarray(ubound)
147 pop0 = prng.uniform(lbound, ubound, size=(npop, len(lbound)))
148 return cls(func, pop0, crossover_rate=crossover_rate, scale=scale,
149 strategy=strategy, eps=eps)
150 frombounds = classmethod(frombounds)
151
153 rat = abs(max(self.pop_values)/self.best_value)
154 rat = min(rat, 1./rat)
155 return max(0.3, 1.-rat)
156
158 mask = self.prng.rand(self.ndim) < self.crossover_rate
159 return np.where(mask, newgene, oldgene)
160
162 possibilities = range(self.npop)
163 possibilities.remove(candidate)
164 return self.prng.permutation(possibilities)[:nsamples]
165
166 - def diff1(self, candidate):
167 i1, i2 = self.select_samples(candidate, 2)
168 return self.scale * (self.population[i1] - self.population[i2])
169
170 - def diff2(self, candidate):
171 i1, i2, i3, i4 = self.select_samples(candidate, 4)
172 return self.scale * (self.population[i1] - self.population[i2] +
173 self.population[i3] - self.population[i4])
174
176 return self.best_vector
177
179 i = self.select_samples(candidate, 1)[0]
180 return self.population[i]
181
183 return ((1-self.scale) * self.population[candidate] +
184 self.scale * self.best_vector)
185
187 chooser, differ, crosser = self.jump_table[self.strategy]
188 trial = crosser(self.population[candidate],
189 chooser(candidate) + differ(candidate))
190 return trial
191
193 return max(self.pop_values) - min(self.pop_values) <= self.eps
194
195 - def solve(self, newgens=100):
196 """
197 Run for newgens more generations.
198
199 Return best parameter vector from the entire run.
200 """
201 for gen in xrange(self.generations+1, self.generations+newgens+1):
202
203
204 trials = [self.get_trial(c) for c in range(self.npop)]
205 values = map(self.func,trials)
206 for candidate, trial_value in enumerate(values):
207 if value < self.pop_values[candidate]:
208 self.population[candidate] = trials[candidate]
209 self.pop_values[candidate] = trial_value
210 if trial_value < self.best_value:
211 self.best_vector = trial
212 self.best_value = trial_value
213 self.best_val_history.append(self.best_value)
214 self.best_vec_history.append(self.best_vector)
215 if self.converged():
216 break
217 self.generations = gen
218 return self.best_vector
219