Skip to content

Commit 2c3c411

Browse files
Merge pull request #18 from Evolutionary-Algorithms-On-Click/customize-de
Customize differential evolution parameters and functions
2 parents ce559d1 + 8d77991 commit 2c3c411

File tree

1 file changed

+150
-18
lines changed

1 file changed

+150
-18
lines changed

modules/ea.go

Lines changed: 150 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ func (ea *EA) validate() error {
6161

6262
func (ea *EA) imports() string {
6363
return strings.Join([]string{
64-
"import random",
64+
"import random, os",
6565
"from deap import base, creator, tools, algorithms",
6666
"import numpy",
6767
"import matplotlib.pyplot as plt",
6868
"from functools import reduce",
6969
"from scoop import futures",
7070
"from deap import benchmarks",
71+
"from itertools import chain",
7172
}, "\n")
7273
}
7374

@@ -111,11 +112,16 @@ func (ea *EA) initialGenerator() string {
111112
case "initRepeat":
112113
return fmt.Sprintf("toolbox.register(\"individual\", tools.%s, creator.Individual, toolbox.attr, %d)\n", ea.PopulationFunction, ea.IndividualSize) + fmt.Sprintf("toolbox.register(\"population\", tools.%s, list, toolbox.individual)\n", ea.PopulationFunction)
113114
default:
114-
return ea.PopulationFunction
115+
return fmt.Sprintf("toolbox.register(\"individual\", tools.%s, creator.Individual, toolbox.attr, %d)\n", "initRepeat", ea.IndividualSize) + fmt.Sprintf("toolbox.register(\"population\", tools.%s, list, toolbox.individual)\n", "initRepeat")
115116
}
116117
}
117118

118119
func (ea *EA) mutationFunction() string {
120+
// TODO: Remove this later on.
121+
if ea.Algorithm == "de" {
122+
return fmt.Sprintf("toolbox.register(\"mutate\", mutDE, f=%f)\n", ea.Indpb)
123+
}
124+
119125
// TODO: Add support for more mutation functions.
120126
switch ea.MutationFunction {
121127
case "mutFlipBit":
@@ -167,7 +173,7 @@ func (ea *EA) plots() string {
167173
plots += "\tplt.xlabel(\"Generation\")\n"
168174
plots += "\tplt.ylabel(\"Fitness\")\n"
169175
plots += "\tplt.legend(loc=\"lower right\")\n"
170-
plots += "\tplt.savefig(f\"fitness_plot.png\", dpi=300)\n"
176+
plots += "\tplt.savefig(f\"{rootPath}/fitness_plot.png\", dpi=300)\n"
171177
plots += "\tplt.close()\n"
172178
plots += "\n\n"
173179

@@ -179,15 +185,34 @@ func (ea *EA) plots() string {
179185
plots += "\tplt.ylabel(\"Fitness Change\")\n"
180186
plots += "\tplt.title(\"Effect of Mutation and Crossover on Fitness\")\n"
181187
plots += "\tplt.legend()\n"
182-
plots += "\tplt.savefig(f\"mutation_crossover_effect.png\", dpi=300)\n"
188+
plots += "\tplt.savefig(f\"{rootPath}/mutation_crossover_effect.png\", dpi=300)\n"
183189
plots += "\tplt.close()\n"
184190
return plots
185191
}
186192

193+
func (ea *EA) deCrossOverFunctions() string {
194+
return strings.Join([]string{
195+
"def cxBinomial(x, y, cr):",
196+
"\tsize = len(x)",
197+
"\tindex = random.randrange(size)",
198+
"\tfor i in range(size):",
199+
"\t\tif i == index or random.random() < cr:",
200+
"\t\t\tx[i] = y[i]",
201+
"\treturn x",
202+
"\n",
203+
"def cxExponential(x, y, cr):",
204+
"\tsize = len(x)",
205+
"\tindex = random.randrange(size)",
206+
"\tfor i in chain(range(index, size), range(0, index)):",
207+
"\t\tx[i] = y[i]",
208+
"\t\tif random.random() < cr:",
209+
"\t\t\tbreak",
210+
"\treturn x",
211+
}, "\n")
212+
}
213+
187214
func (ea *EA) differentialEvolution() string {
188215
return strings.Join([]string{
189-
fmt.Sprintf("\tCR = %f", ea.CrossOverRate),
190-
fmt.Sprintf("F = %f", ea.ScalingFactor),
191216
"\n",
192217
"logbook = tools.Logbook()",
193218
"logbook.header = 'gen', 'evals', 'std', 'min', 'avg', 'max'",
@@ -198,24 +223,101 @@ func (ea *EA) differentialEvolution() string {
198223
"logbook.record(gen=0, evals=len(pop), **record)",
199224
"print(logbook.stream)",
200225
"for g in range(1, generations):",
201-
"\tfor k, agent in enumerate(pop):",
202-
"\t\ta,b,c = toolbox.select(pop, 3)",
226+
"\tchildren = []",
227+
"\tfor agent in pop:",
228+
"\t\ta, b, c = [toolbox.clone(ind) for ind in toolbox.select(pop, 3)]",
229+
"\t\tx = toolbox.clone(agent)",
203230
"\t\ty = toolbox.clone(agent)",
204-
"\t\tindex = random.randrange(N)",
205-
"\t\tfor i, value in enumerate(agent):",
206-
"\t\t\tif i == index or random.random() < CR:",
207-
"\t\t\t\ty[i] = a[i] + F*(b[i]-c[i])",
208-
"\t\ty.fitness.values = toolbox.evaluate(y)",
209-
"\t\tif y.fitness > agent.fitness:",
210-
"\t\t\tpop[k] = y",
231+
"\t\ty = toolbox.mutate(y, a, b, c)",
232+
"\t\tz = toolbox.mate(x, y)",
233+
"\t\tdel z.fitness.values",
234+
"\t\tchildren.append(z)",
235+
"\n",
236+
"\tfitnesses = toolbox.map(toolbox.evaluate, children)",
237+
"\tfor (i, ind), fit in zip(enumerate(children), fitnesses):",
238+
"\t\tind.fitness.values = fit",
239+
"\t\tif ind.fitness > pop[i].fitness:",
240+
"\t\t\tpop[i] = ind",
241+
"\n",
211242
"\thof.update(pop)",
212243
"\trecord = stats.compile(pop)",
213244
"\tlogbook.record(gen=g, evals=len(pop), **record)",
214245
"\tprint(logbook.stream)",
215-
"\n",
216246
}, "\n\t")
217247
}
218248

249+
func (ea *EA) deMutationFunction() string {
250+
switch ea.MutationFunction {
251+
case "DE/rand/1":
252+
return strings.Join([]string{
253+
"def mutDE(y, a, b, c, f):",
254+
"\tsize = len(y)",
255+
"\tfor i in range(len(y)):",
256+
"\t\ty[i] = a[i] + f*(b[i]-c[i])",
257+
"\treturn y",
258+
}, "\n")
259+
case "DE/rand/2":
260+
return strings.Join([]string{
261+
"def mutDE_rand2(y, a, b, c, d, e, f):",
262+
"\tsize = len(y)",
263+
"\tfor i in range(size):",
264+
"\t\ty[i] = a[i] + f * (b[i] - c[i]) + f * (d[i] - e[i])",
265+
"\treturn y",
266+
}, "\n")
267+
case "DE/best/1":
268+
return strings.Join([]string{
269+
"def mutDE_best1(y, best, b, c, f):",
270+
"\tsize = len(y)",
271+
"\tfor i in range(size):",
272+
"\t\ty[i] = best[i] + f * (b[i] - c[i])",
273+
"\treturn y",
274+
}, "\n")
275+
case "DE/best/2":
276+
return strings.Join([]string{
277+
"def mutDE_best2(y, best, b, c, d, e, f):",
278+
"\tsize = len(y)",
279+
"\tfor i in range(size):",
280+
"\t\ty[i] = best[i] + f * (b[i] - c[i]) + f * (d[i] - e[i])",
281+
"\treturn y",
282+
}, "\n")
283+
case "DE/current-to-best/1":
284+
return strings.Join([]string{
285+
"def mutDE_current_to_best1(y, x, best, b, c, f):",
286+
"\tsize = len(y)",
287+
"\tfor i in range(size):",
288+
"\t\ty[i] = x[i] + f * (best[i] - x[i]) + f * (b[i] - c[i])",
289+
"\treturn y",
290+
}, "\n")
291+
case "DE/current-to-rand/1":
292+
return strings.Join([]string{
293+
"def mutDE_current_to_rand1(y, x, a, b, c, f):",
294+
"\tsize = len(y)",
295+
"\tK = random.uniform(0, 1) # Random number in [0, 1]",
296+
"\tfor i in range(size):",
297+
"\t\ty[i] = x[i] + K * (a[i] - x[i]) + f * (b[i] - c[i])",
298+
"\treturn y",
299+
}, "\n")
300+
case "DE/rand-to-best/1":
301+
return strings.Join([]string{
302+
"def mutDE_rand_to_best1(y, a, best, b, c, f):",
303+
"\tsize = len(y)",
304+
"\tfor i in range(size):",
305+
"\t\ty[i] = a[i] + f * (best[i] - a[i]) + f * (b[i] - c[i])",
306+
"\treturn y",
307+
}, "\n")
308+
default:
309+
return strings.Join([]string{
310+
"def mutDE(y, a, b, c, f):",
311+
"\tsize = len(y)",
312+
"\tfor i in range(len(y)):",
313+
"\t\ty[i] = a[i] + f*(b[i]-c[i])",
314+
"\treturn y",
315+
}, "\n")
316+
317+
}
318+
319+
}
320+
219321
func (ea *EA) Code() (string, error) {
220322
if err := ea.validate(); err != nil {
221323
return "", err
@@ -224,6 +326,19 @@ func (ea *EA) Code() (string, error) {
224326
var code string
225327
code += ea.imports() + "\n\n"
226328
code += ea.evalFunction() + "\n\n"
329+
330+
if ea.Algorithm == "de" {
331+
code += strings.Join([]string{
332+
"def mutDE(y, a, b, c, f):",
333+
"\tsize = len(y)",
334+
"\tfor i in range(len(y)):",
335+
"\t\ty[i] = a[i] + f*(b[i]-c[i])",
336+
"\treturn y",
337+
}, "\n") + "\n\n"
338+
code += ea.deMutationFunction() + "\n\n"
339+
}
340+
341+
code += ea.deCrossOverFunctions() + "\n\n"
227342
code += ea.CustomPop + "\n"
228343
code += ea.CustomMutation + "\n"
229344
code += ea.CustomSelection + "\n\n"
@@ -237,7 +352,14 @@ func (ea *EA) Code() (string, error) {
237352
code += ea.initialGenerator() + "\n"
238353
code += fmt.Sprintf("toolbox.register(\"evaluate\", %s)\n", ea.EvaluationFunction)
239354
code += ea.mutationFunction() + "\n"
240-
code += fmt.Sprintf("toolbox.register(\"mate\", tools.%s)\n", ea.CrossoverFunction)
355+
356+
if ea.Algorithm == "de" {
357+
code += fmt.Sprintf("CR = %f\n", ea.CrossOverRate)
358+
code += fmt.Sprintf("F = %f\n", ea.ScalingFactor)
359+
code += fmt.Sprintf("toolbox.register(\"mate\", %s, cr=CR)\n", ea.CrossoverFunction)
360+
} else {
361+
code += fmt.Sprintf("toolbox.register(\"mate\", tools.%s)\n", ea.CrossoverFunction)
362+
}
241363
code += ea.selectionFunction() + "\n"
242364
code += "\ntoolbox.register(\"map\", futures.map)\n\n"
243365

@@ -261,7 +383,17 @@ func (ea *EA) Code() (string, error) {
261383
code += ea.callAlgo() + "\n"
262384
}
263385

264-
code += "\tprint(f'Best individual is: {hof[0]}\\nwith fitness: {hof[0].fitness}')"
386+
code += "\n\trootPath = os.path.dirname(os.path.abspath(__file__))\n"
387+
code += "\twith open(f\"{rootPath}/logbook.txt\", \"w\") as f:\n"
388+
code += "\t\tf.write(str(logbook))\n"
389+
code += "\n"
390+
391+
// Write best individual to file.
392+
code += "\tout_file = open(f\"{rootPath}/best.txt\", \"w\")\n"
393+
code += "\tout_file.write(f\"Best individual fitness: {hof[0].fitness.values}\\n\")\n"
394+
code += "\n"
395+
code += "\tout_file.write(f\"Best individual: {hof[0]}\\n\")\n"
396+
code += "\tout_file.close()\n"
265397
code += "\n\n"
266398
code += ea.plots()
267399
code += "\n\n"

0 commit comments

Comments
 (0)