1
2
3
4 import numpy as np
5 import cma
6 from cma import interfaces
7 import os
8 import matplotlib.pyplot as plt
9 import ast
10 import warnings
11
13 """data logger for class `CMAEvolutionStrategy`.
14
15 The logger is identified by its name prefix and (over-)writes or
16 reads according data files. Therefore, the logger must be
17 considered as *global* variable with unpredictable side effects,
18 if two loggers with the same name and on the same working folder
19 are used at the same time.
20
21 Examples
22 ========
23 ::
24
25 import cma
26 es = cma.CMAEvolutionStrategy(...)
27 logger = cma.CMADataLogger().register(es)
28 while not es.stop():
29 ...
30 logger.add() # add can also take an argument
31
32 logger.plot() # or a short cut can be used:
33 cma.plot() # plot data from logger with default name
34
35 logger2 = cma.CMADataLogger('just_another_filename_prefix').load()
36 logger2.plot()
37 logger2.disp()
38
39 import cma
40 from matplotlib.pylab import *
41 res = cma.fmin(cma.ff.sphere, rand(10), 1e-0)
42 logger = res[-1] # the CMADataLogger
43 logger.load() # by "default" data are on disk
44 semilogy(logger.f[:,0], logger.f[:,5]) # plot f versus iteration, see file header
45 cma.s.figshow()
46
47 Details
48 =======
49 After loading data, the logger has the attributes `xmean`, `xrecent`,
50 `std`, `f`, `D` and `corrspec` corresponding to ``xmean``,
51 ``xrecentbest``, ``stddev``, ``fit``, ``axlen`` and ``axlencorr``
52 filename trails.
53
54 :See: `disp` (), `plot` ()
55 """
56 default_prefix = 'outsofomore' + os.sep
57
58
59
60
62 """initialize logging of data from a `CMAEvolutionStrategy`
63 instance, default ``modulo=1`` means logging with each call
64
65 """
66
67
68
69
70
71 if name_prefix is None:
72 name_prefix = SofomoreDataLogger.default_prefix
73 self.name_prefix = os.path.abspath(os.path.join(*os.path.split(name_prefix)))
74 if name_prefix is not None and name_prefix.endswith((os.sep, '/')):
75 self.name_prefix = self.name_prefix + os.sep
76 self.file_names = ('hypervolume', 'hypervolume_archive', 'len_archive', 'ratio_active_kernel',
77 'ratio_nondom_incumb', 'ratio_nondom_offsp_incumb',
78 'median_sigmas', 'median_axis_ratios', 'median_min_stds',
79 'median_max_stds', 'median_stds')
80 self.modulo = modulo
81 """how often to record data, allows calling `add` without args"""
82 self.append = append
83 """append to previous data"""
84 self.counter = 0
85 """number of calls to `add`"""
86 self.last_iteration = 0
87 self.registered = False
88 self.persistent_communication_dict = cma.utilities.utils.DictFromTagsInString()
89
90 - def register(self, es, append=None, modulo=None):
91 """register a `Sofomore` instance for logging,
92 ``append=True`` appends to previous data logged under the same name,
93 by default previous data are overwritten.
94
95 """
96
97
98
99
100
101
102 self.es = es
103 if append is not None:
104 self.append = append
105 if modulo is not None:
106 self.modulo = modulo
107 self.registered = True
108 return self
109
111 """reset logger, overwrite original files, `modulo`: log only every modulo call"""
112 if modulo is not None:
113 self.modulo = modulo
114 try:
115 es = self.es
116 except AttributeError:
117 pass
118 raise AttributeError('call register() before initialize()')
119
120 self.counter = 0
121 self.last_iteration = 0
122 if self.modulo <= 0:
123 return self
124
125
126 if os.path.dirname(self.name_prefix):
127 try:
128 os.makedirs(os.path.dirname(self.name_prefix))
129 except OSError:
130 pass
131
132
133
134 fn = self.name_prefix + 'median_sigmas.dat'
135 try:
136 with open(fn, 'w') as f:
137 f.write('% # columns="iteration, evaluation, median sigmas, ' +
138 ', ' +
139 '\n')
140 except (IOError, OSError):
141 print('could not open file ' + fn)
142
143 fn = self.name_prefix + 'median_axis_ratios.dat'
144 try:
145 with open(fn, 'w') as f:
146 f.write('% # columns="iteration, evaluation, median axis ratios, ' +
147 ', ' +
148 '\n')
149 except (IOError, OSError):
150 print('could not open file ' + fn)
151
152 fn = self.name_prefix + 'median_min_stds.dat'
153 try:
154 with open(fn, 'w') as f:
155 f.write('% # columns="iteration, evaluation, median min stds, ' +
156 ', ' +
157 '\n')
158 except (IOError, OSError):
159 print('could not open file ' + fn)
160
161 fn = self.name_prefix + 'median_max_stds.dat'
162 try:
163 with open(fn, 'w') as f:
164 f.write('% # columns="iteration, evaluation, median max stds, ' +
165 ', ' +
166 '\n')
167 except (IOError, OSError):
168 print('could not open file ' + fn)
169
170 fn = self.name_prefix + 'median_stds.dat'
171 try:
172 with open(fn, 'w') as f:
173 f.write('% # columns="iteration, evaluation, median stds, ' +
174 '\n')
175 except (IOError, OSError):
176 print('could not open file ' + fn)
177
178 fn = self.name_prefix + 'hypervolume.dat'
179 try:
180 with open(fn, 'w') as f:
181 f.write('% # columns="iteration, evaluation, hypervolume, ' +
182 ', ' +
183 '\n')
184 except (IOError, OSError):
185 print('could not open file ' + fn)
186
187 fn = self.name_prefix + 'hypervolume_archive.dat'
188 try:
189 with open(fn, 'w') as f:
190 f.write('% # columns="iteration, evaluation, hypervolume of archive' +
191 '\n')
192 except (IOError, OSError):
193 print('could not open/write file ' + fn)
194
195
196 fn = self.name_prefix + 'len_archive.dat'
197 try:
198 with open(fn, 'w') as f:
199 f.write('% # columns="iter, evals, length archive" ' +
200 ', ' +
201 '\n')
202 except (IOError, OSError):
203 print('could not open/write file ' + fn)
204
205 fn = self.name_prefix + 'ratio_inactive_kernels.dat'
206 try:
207 with open(fn, 'w') as f:
208 f.write('% # columns="iteration, evaluation, ratio active kernels, ' +
209 '\n')
210 except (IOError, OSError):
211 print('could not open/write file ' + fn)
212
213 fn = self.name_prefix + 'ratio_nondom_incumb.dat'
214 try:
215 with open(fn, 'w') as f:
216 f.write('% # columns="iteration, evaluation, ratio nondominated incumbents, ' +
217 '\n')
218 except (IOError, OSError):
219 print('could not open/write file ' + fn)
220
221
222 fn = self.name_prefix + 'ratio_nondom_offsp_incumb.dat'
223 try:
224 with open(fn, 'w') as f:
225 f.write('% # columns="iter, evals, first quartile nondom ' +
226 'offspring and incumbent, median nondom offspring and ' +
227 'incumbent, last quartile nondom offspring and incumbent' +
228 ', ' +
229 '\n')
230 except (IOError, OSError):
231 print('could not open/write file ' + fn)
232
233 return self
234
235
236 - def add(self, es=None, more_data=(), modulo=None):
237 """append some logging data from `Sofomore` class instance `es`,
238 if ``number_of_times_called % modulo`` equals to zero, never if ``modulo==0``.
239
240 ``more_data`` is a list of additional data to be recorded where each
241 data entry must have the same length.
242
243 When used for a different optimizer class, this function can be
244 (easily?) adapted by changing the assignments under INTERFACE
245 in the implemention.
246
247 """
248 mod = modulo if modulo is not None else self.modulo
249 self.counter += 1
250 if mod == 0 or (self.counter > 3 and (self.counter - 1) % mod):
251 return
252 if es is None:
253 try:
254 es = self.es
255 except AttributeError :
256 raise AttributeError('call `add` with argument `es` or ``register(es)`` before ``add()``')
257 elif not self.registered:
258 self.register(es)
259
260 if self.counter == 1 and not self.append and self.modulo != 0:
261 self.initialize()
262 self.counter = 1
263
264
265
266
267
268 evals = es.countevals
269 iteration = es.countiter
270 hypervolume = float(es.pareto_front_cut.hypervolume)
271 hypervolume_archive = 0.0
272 len_archive = 0
273 if es.isarchive:
274 hypervolume_archive = float(es.archive.hypervolume)
275 len_archive = len(es.archive)
276 ratio_inactive = 1 - len(es._active_indices) / len(es)
277 ratio_nondom_incumbent = len(es.pareto_front_cut)/len(es)
278
279 for i in range(len(es.offspring)):
280 idx = es.offspring[i][0]
281 kernel = es.kernels[idx]
282
283 temp_archive = es.NDA(kernel._last_offspring_f_values, es.reference_point)
284 temp_archive.add(kernel.objective_values)
285 es._ratio_nondom_offspring_incumbent[idx] = len(temp_archive) / (
286 1 + len(kernel._last_offspring_f_values) )
287
288
289 first_quartile_ratio_offspring_incumbent = np.percentile(es._ratio_nondom_offspring_incumbent, 25);
290 median_ratio_offspring_incumbent = np.percentile(es._ratio_nondom_offspring_incumbent, 50);
291 last_quartile_ratio_offspring_incumbent = np.percentile(es._ratio_nondom_offspring_incumbent, 75);
292
293
294 median_axis_ratios = np.median([kernel.D.max() / kernel.D.min() \
295 if not kernel.opts['CMA_diagonal'] or kernel.countiter > kernel.opts['CMA_diagonal']
296 else max(kernel.sigma_vec*1) / min(kernel.sigma_vec*1) for kernel in es.kernels])
297 median_sigmas = np.median([kernel.sigma for kernel in es.kernels])
298 median_min_stds = np.median([kernel.sigma * min(kernel.sigma_vec * kernel.dC**0.5) \
299 for kernel in es.kernels])
300 median_max_stds = np.median([kernel.sigma * max(kernel.sigma_vec * kernel.dC**0.5) \
301 for kernel in es.kernels])
302 median_stds = self.es.median_stds
303
304
305
306
307 try:
308
309 fn = self.name_prefix + 'median_axis_ratios.dat'
310 with open(fn, 'a') as f:
311 f.write(str(iteration) + ' '
312 + str(evals) + ' '
313 + str(median_axis_ratios)
314 + '\n')
315
316
317 fn = self.name_prefix + 'median_sigmas.dat'
318 with open(fn, 'a') as f:
319 f.write(str(iteration) + ' '
320 + str(evals) + ' '
321 + str(median_sigmas)
322 + '\n')
323
324
325 fn = self.name_prefix + 'median_min_stds.dat'
326 with open(fn, 'a') as f:
327 f.write(str(iteration) + ' '
328 + str(evals) + ' '
329 + str(median_min_stds)
330 + '\n')
331
332
333 fn = self.name_prefix + 'median_max_stds.dat'
334 with open(fn, 'a') as f:
335 f.write(str(iteration) + ' '
336 + str(evals) + ' '
337 + str(median_max_stds)
338 + '\n')
339
340
341 fn = self.name_prefix + 'median_stds.dat'
342 with open(fn, 'a') as f:
343 f.write(str(iteration) + ' '
344 + str(evals) + ' '
345 + str(median_stds)
346 + '\n')
347
348 if iteration > self.last_iteration:
349 fn = self.name_prefix + 'hypervolume.dat'
350 with open(fn, 'a') as f:
351 f.write(str(iteration) + ' '
352 + str(evals) + ' '
353 + str(hypervolume) + ' '
354 + '\n')
355
356
357 if iteration > self.last_iteration:
358 fn = self.name_prefix + 'hypervolume_archive.dat'
359 with open(fn, 'a') as f:
360 f.write(str(iteration) + ' '
361 + str(evals) + ' '
362 + str(hypervolume_archive)
363 + '\n')
364
365
366 if iteration > self.last_iteration:
367 fn = self.name_prefix + 'len_archive.dat'
368 with open(fn, 'a') as f:
369 f.write(str(iteration) + ' '
370 + str(evals) + ' '
371 + str(len_archive)
372 + '\n')
373
374 if iteration > self.last_iteration:
375 fn = self.name_prefix + 'ratio_inactive_kernels.dat'
376 with open(fn, 'a') as f:
377 f.write(str(iteration) + ' '
378 + str(evals) + ' '
379 + str(ratio_inactive)
380 + '\n')
381
382 if iteration > self.last_iteration:
383 fn = self.name_prefix + 'ratio_nondom_incumb.dat'
384 with open(fn, 'a') as f:
385 f.write(str(iteration) + ' '
386 + str(evals) + ' '
387 + str(ratio_nondom_incumbent)
388 + '\n')
389
390 if iteration > self.last_iteration:
391 fn = self.name_prefix + 'ratio_nondom_offsp_incumb.dat'
392 with open(fn, 'a') as f:
393 f.write(str(iteration) + ' '
394 + str(evals) + ' '
395 + str(first_quartile_ratio_offspring_incumbent) + ' '
396 + str(median_ratio_offspring_incumbent) + ' '
397 + str(last_quartile_ratio_offspring_incumbent)
398 + '\n')
399
400
401 except (IOError, OSError):
402 pass
403
404 self.last_iteration = iteration
405
406
407 - def load(self, filenames):
408 """
409 """
410 iteration = []
411 countevals = []
412 if isinstance(filenames, str):
413 filenames = [filenames]
414 res = []
415 for i in range(len(filenames)):
416 filename = filenames[i]
417 with open(filename) as f:
418 tab = [line.rstrip() for line in f.readlines()[1:]]
419
420 maxsplit = 2 if filename[-15:] == 'median_stds.dat' else -1
421 newtab = [list(map(ast.literal_eval,line.split(maxsplit = maxsplit))) for line in tab]
422 length = len(newtab[0])
423 for k in range(2, length):
424 res += [np.array([line[k] for line in newtab])]
425
426 if i == 0:
427 iteration = np.array([line[0] for line in newtab])
428 countevals = np.array([line[1] for line in newtab])
429
430 return iteration, countevals, res
431
432 - def plot(self, filename, x_iteration = 0):
433 """
434 """
435 iteration, countevals, res = self.load(filename)
436 for i in range(len(res)):
437 if not x_iteration:
438 plt.plot(countevals, res)
439 else:
440 plt.plot(iteration, res)
441
443 """
444 """
445 if aspect is not None:
446 myaxes = plt.gca()
447 try:
448 myaxes.set_aspect(aspect)
449 except:
450 pass
451 moes = self.es
452 try:
453 plt.plot([u[0] for u in moes.archive], [u[1] for u in moes.archive], '.',
454 label = "archive")
455 except:
456 pass
457 plt.plot([u[0] for u in moes.pareto_front_cut], [u[1] for u in moes.pareto_front_cut], 'o',
458 label = "cma-es incumbents")
459 pass
460
462
463 """
464 """
465
466
467
468 from matplotlib import pyplot
469 fn_incumbent = self.name_prefix + 'ratio_nondom_incumb.dat'
470 fn_inactive = self.name_prefix + 'ratio_inactive_kernels.dat'
471 fn_nondom = self.name_prefix + 'ratio_nondom_offsp_incumb.dat'
472 filenames = fn_incumbent, fn_inactive, fn_nondom
473 iteration, countevals, res = self.load(filenames)
474 absciss = countevals if iabscissa else iteration
475 self._enter_plotting()
476
477 self._xlabel(iabscissa)
478 mylabel = ['ratio nondom incumbents', 'ratio inactive kernels',
479 '1st quartile ratio nondom off+incumb',
480 'median ratio nondom off+incumb',
481 '3rd quartile ratio nondom off+incumb']
482 for i in range(5):
483 pyplot.plot(absciss, res[i], label = mylabel[i])
484
485
486
487
488
489 pyplot.grid(True)
490 ax = np.array(pyplot.axis())
491
492 pyplot.axis(ax)
493
494 pyplot.legend()
495
496 self._xlabel(iabscissa)
497 self._finalize_plotting()
498 return self
499
501 """
502 """
503
504
505 from matplotlib import pyplot
506 fn_axis_ratios = self.name_prefix + 'median_axis_ratios.dat'
507 fn_max_stds = self.name_prefix + 'median_max_stds.dat'
508 fn_min_stds = self.name_prefix + 'median_min_stds.dat'
509 fn_sigmas = self.name_prefix + 'median_sigmas.dat'
510 fn_hypervolume = self.name_prefix + 'hypervolume.dat'
511 fn_archive = self.name_prefix + 'hypervolume_archive.dat'
512 fn_len_archive = self.name_prefix + 'len_archive.dat'
513
514
515
516
517
518 filenames_median = (fn_axis_ratios, fn_max_stds, fn_min_stds, fn_sigmas)
519 (iteration_median, countevals_median,
520 res_median) = self.load(filenames_median)
521 absciss_median = countevals_median if iabscissa else iteration_median
522
523 filenames_hypervolume = (fn_hypervolume, fn_archive, fn_len_archive)
524 (iteration_hypervolume, countevals_hypervolume,
525 res_hypervolume) = self.load(filenames_hypervolume)
526 absciss_hypervolume = (countevals_hypervolume if iabscissa
527 else iteration_hypervolume)
528
529 self._enter_plotting()
530
531 self._xlabel(iabscissa)
532 mylabel = ['median axis ratios', 'median max stds',
533 'median min stds', 'median sigmas',
534 'convergence gap', 'archive gap', 'inverse length archive']
535 for i in range(4):
536 pyplot.semilogy(absciss_median, res_median[i], label = mylabel[i])
537
538
539
540
541
542 offset_convergence_gap = self.es.best_hypervolume_pareto_front
543 pyplot.semilogy(absciss_hypervolume, [offset_convergence_gap - u
544 for u in res_hypervolume[0]],
545 label = mylabel[4], nonposy = 'clip')
546 current_archive = res_hypervolume[1]
547 try:
548 offset_archive_gap = current_archive[-1]
549 pyplot.semilogy(absciss_hypervolume,
550 [offset_archive_gap - u for u in current_archive],
551 label = mylabel[5], nonposy = 'clip')
552 except IndexError:
553 warnings.warn("empty archive")
554 pyplot.semilogy(absciss_hypervolume, [1/u for u in res_hypervolume[2]],
555 label = mylabel[6])
556 pyplot.grid(True)
557 ax = np.array(pyplot.axis())
558
559 pyplot.axis(ax)
560
561 pyplot.legend()
562
563 self._xlabel(iabscissa)
564 self._finalize_plotting()
565 return self
566
567
569
570 """
571 """
572
573 from matplotlib import pyplot
574 filename = self.name_prefix + 'median_stds.dat'
575 iteration, countevals, res = self.load(filename)
576 absciss = countevals if iabscissa else iteration
577 self._enter_plotting()
578
579 self._xlabel(iabscissa)
580
581 pyplot.semilogy(absciss, res[0])
582
583
584
585
586
587 pyplot.grid(True)
588 ax = np.array(pyplot.axis())
589
590 pyplot.axis(ax)
591
592
593
594 self._xlabel(iabscissa)
595 pyplot.title("median (sorted) standard deviations in all coordinates")
596 self._finalize_plotting()
597 return self
598
600 """assumes that a figure is open """
601 from matplotlib import pyplot
602
603 self.original_fontsize = pyplot.rcParams['font.size']
604
605 if pyplot.rcParams['font.size'] == pyplot.rcParamsDefault['font.size']:
606 pyplot.rcParams['font.size'] = fontsize
607
608
609 pyplot.ioff()
611 from matplotlib import pyplot
612 pyplot.subplots_adjust(left=0.05, top=0.96, bottom=0.07, right=0.95)
613
614 pyplot.draw()
615 pyplot.ion()
616 pyplot.show()
617 pyplot.rcParams['font.size'] = self.original_fontsize
619 from matplotlib import pyplot
620 pyplot.xlabel('iterations' if iabscissa == 0
621 else 'function evaluations')
622