0
# Logging and Analysis
1
2
Comprehensive data collection, visualization, and analysis tools for monitoring and understanding CMA-ES optimization behavior and performance.
3
4
## Data Logging with CMADataLogger
5
6
The CMADataLogger class provides comprehensive data collection during optimization with automatic file management and data persistence.
7
8
## Core Logging Functions
9
10
```python { .api }
11
# Main logging and plotting functions available in cma module
12
import cma
13
14
def disp(iteration=None):
15
"""
16
Display current optimization state and progress.
17
18
Parameters:
19
-----------
20
iteration : int, optional
21
Iteration number to display (default: current).
22
"""
23
pass
24
25
def plot(name_prefix='outcmaes', **kwargs):
26
"""
27
Plot CMA-ES data from logged files.
28
29
Parameters:
30
-----------
31
name_prefix : str, optional
32
File prefix for data files to plot (default 'outcmaes').
33
**kwargs : dict
34
Additional plotting options.
35
"""
36
pass
37
38
def plot_zip(name_prefix='outcmaes', **kwargs):
39
"""
40
Plot multiple CMA-ES runs from zipped data files.
41
42
Parameters:
43
-----------
44
name_prefix : str, optional
45
File prefix pattern for multiple runs.
46
"""
47
pass
48
```
49
50
### CMADataLogger Class
51
52
```python { .api }
53
class CMADataLogger:
54
"""
55
Data logger for CMAEvolutionStrategy with automatic file management.
56
57
Logs optimization data to files and provides loading, visualization,
58
and analysis capabilities. Data is written to disk in real-time and
59
can be reloaded for post-processing and analysis.
60
"""
61
62
default_prefix = 'outcmaes/' # Default file prefix
63
64
def __init__(self, name_prefix=None, modulo=1, append=False, expensive_modulo=1):
65
"""
66
Initialize CMA-ES data logger.
67
68
Parameters:
69
-----------
70
name_prefix : str, optional
71
File name prefix for all data files. If None, uses default 'outcmaes/'.
72
Can be a directory path ending with '/' for organized storage.
73
74
modulo : int, optional
75
Log data every modulo iterations (default 1 = every iteration).
76
77
append : bool, optional
78
Whether to append to existing files (default False = overwrite).
79
80
expensive_modulo : int, optional
81
Frequency for expensive operations like eigendecomposition logging
82
(default 1). Set higher for large dimensions to reduce overhead.
83
84
Examples:
85
---------
86
>>> import cma
87
>>>
88
>>> # Basic logging
89
>>> logger = cma.CMADataLogger()
90
>>> es = cma.CMAEvolutionStrategy([0, 0, 0], 0.5)
91
>>> logger.register(es) # Connect logger to optimizer
92
>>>
93
>>> while not es.stop():
94
... solutions = es.ask()
95
... fitness_values = [sum(x**2) for x in solutions]
96
... es.tell(solutions, fitness_values)
97
... logger.add() # Log current state
98
>>>
99
>>> # Custom file prefix
100
>>> logger = cma.CMADataLogger('my_experiment/run_001')
101
>>>
102
>>> # Reduced logging frequency for large problems
103
>>> logger = cma.CMADataLogger(modulo=10, expensive_modulo=50)
104
"""
105
pass
106
107
def register(self, es, append=None, modulo=None):
108
"""
109
Register evolution strategy for automatic logging.
110
111
Parameters:
112
-----------
113
es : CMAEvolutionStrategy
114
Evolution strategy instance to log.
115
116
append : bool, optional
117
Override constructor append setting.
118
119
modulo : int, optional
120
Override constructor modulo setting.
121
122
Returns:
123
--------
124
CMADataLogger
125
Self for method chaining.
126
127
Examples:
128
---------
129
>>> import cma
130
>>>
131
>>> es = cma.CMAEvolutionStrategy([0, 0, 0], 0.5)
132
>>> logger = cma.CMADataLogger().register(es)
133
>>>
134
>>> # Method chaining
135
>>> logger = cma.CMADataLogger('experiment1/').register(es, modulo=5)
136
"""
137
pass
138
139
def add(self, es=None, more_data=[], modulo=None):
140
"""
141
Add current evolution strategy state to log.
142
143
Parameters:
144
-----------
145
es : CMAEvolutionStrategy, optional
146
Evolution strategy to log. If None, uses registered ES.
147
148
more_data : list, optional
149
Additional data columns to append to fit file.
150
151
modulo : int, optional
152
Override default logging frequency.
153
154
Examples:
155
---------
156
>>> import cma
157
>>>
158
>>> es = cma.CMAEvolutionStrategy([0, 0, 0], 0.5)
159
>>> logger = cma.CMADataLogger().register(es)
160
>>>
161
>>> while not es.stop():
162
... solutions = es.ask()
163
... fitness_values = [sum(x**2) for x in solutions]
164
... es.tell(solutions, fitness_values)
165
...
166
... # Log with additional data
167
... custom_metric = es.sigma * es.result.evaluations
168
... logger.add(more_data=[custom_metric])
169
"""
170
pass
171
172
def load(self, filenameprefix=None):
173
"""
174
Load logged data from files.
175
176
Parameters:
177
-----------
178
filenameprefix : str, optional
179
File prefix to load. If None, uses instance prefix.
180
181
Returns:
182
--------
183
CMADataLogger
184
Self with loaded data in attributes.
185
186
Examples:
187
---------
188
>>> import cma
189
>>>
190
>>> # Load previously logged data
191
>>> logger = cma.CMADataLogger().load()
192
>>>
193
>>> # Load specific experiment
194
>>> logger = cma.CMADataLogger().load('experiment1/run_005')
195
>>>
196
>>> # Access loaded data
197
>>> print(f"Iterations: {len(logger.f)}")
198
>>> print(f"Final fitness: {logger.f[-1, 5]}")
199
>>> print(f"Final mean: {logger.xmean[-1]}")
200
"""
201
pass
202
203
def save_to(self, filenameprefix):
204
"""
205
Save current data to different file location.
206
207
Parameters:
208
-----------
209
filenameprefix : str
210
New file prefix for saving data.
211
"""
212
pass
213
214
@property
215
def data(self):
216
"""
217
Dictionary of loaded data arrays.
218
219
Returns:
220
--------
221
dict
222
Data dictionary with keys: 'xmean', 'xrecent', 'std', 'f', 'D', 'corrspec'
223
corresponding to mean solutions, recent best, standard deviations,
224
fitness values, axis lengths, and correlation spectrum.
225
"""
226
pass
227
```
228
229
### Logged Data Structure
230
231
```python { .api }
232
# Data files and their contents
233
data_files = {
234
'fit': """
235
Fitness and evaluation data (outcmaesfit.dat):
236
Columns: [iteration, evaluations, sigma, axis_ratio, best_fitness,
237
median_fitness, worst_fitness, further_data...]
238
239
Example access:
240
>>> logger = cma.CMADataLogger().load()
241
>>> iterations = logger.f[:, 0]
242
>>> evaluations = logger.f[:, 1]
243
>>> best_fitness = logger.f[:, 4]
244
>>> median_fitness = logger.f[:, 5]
245
""",
246
247
'xmean': """
248
Mean solution evolution (outcmaesxmean.dat):
249
Columns: [iteration, evaluations, sigma, axis_ratio, mean_x1, mean_x2, ...]
250
251
Example access:
252
>>> logger = cma.CMADataLogger().load()
253
>>> mean_solutions = logger.xmean[:, 4:] # Skip iteration info
254
>>> final_mean = logger.xmean[-1, 4:] # Final mean solution
255
""",
256
257
'xrecentbest': """
258
Recent best solutions (outcmaesxrecentbest.dat):
259
Columns: [iteration, evaluations, sigma, axis_ratio, x1, x2, ...]
260
261
Example access:
262
>>> logger = cma.CMADataLogger().load()
263
>>> recent_best = logger.xrecent[:, 4:] # Recent best solutions
264
>>> best_solution = logger.xrecent[-1, 4:] # Final best solution
265
""",
266
267
'stddev': """
268
Standard deviations (outcmaesstddev.dat):
269
Columns: [iteration, evaluations, sigma, max_std/min_std, std1, std2, ...]
270
271
Example access:
272
>>> logger = cma.CMADataLogger().load()
273
>>> std_devs = logger.std[:, 4:] # Standard deviations per coordinate
274
>>> condition_estimate = logger.std[:, 3] # Max/min std ratio
275
""",
276
277
'axlen': """
278
Principal axis lengths (outcmaesaxlen.dat):
279
Columns: [iteration, evaluations, sigma, condition, len1, len2, ...]
280
Principal axes ordered by length (largest first).
281
282
Example access:
283
>>> logger = cma.CMADataLogger().load()
284
>>> axis_lengths = logger.D[:, 4:] # Principal axis lengths
285
>>> condition_numbers = logger.D[:, 3] # Condition numbers
286
""",
287
288
'axlencorr': """
289
Correlation between consecutive axis length vectors (outcmaesaxlencorr.dat):
290
Indicates stability of principal directions.
291
"""
292
}
293
294
# Example: Comprehensive data access
295
def access_logged_data_example():
296
"""Example of accessing all types of logged data."""
297
298
import cma
299
import numpy as np
300
301
# Run optimization with logging
302
es = cma.CMAEvolutionStrategy(5 * [0.5], 0.3)
303
logger = cma.CMADataLogger('example_run/').register(es)
304
305
while not es.stop():
306
solutions = es.ask()
307
fitness_values = [sum((x - np.array([1, 2, 3, 4, 5]))**2) for x in solutions]
308
es.tell(solutions, fitness_values)
309
logger.add()
310
311
if es.countiter % 50 == 0:
312
es.disp()
313
314
# Reload and analyze data
315
logger.load()
316
317
# Fitness evolution
318
iterations = logger.f[:, 0]
319
evaluations = logger.f[:, 1]
320
best_fitness = logger.f[:, 4]
321
median_fitness = logger.f[:, 5]
322
323
print(f"Optimization completed in {iterations[-1]} iterations")
324
print(f"Total evaluations: {evaluations[-1]}")
325
print(f"Final best fitness: {best_fitness[-1]:.2e}")
326
327
# Solution evolution
328
mean_evolution = logger.xmean[:, 4:]
329
final_mean = mean_evolution[-1]
330
target_solution = np.array([1, 2, 3, 4, 5])
331
332
print(f"Final mean solution: {final_mean}")
333
print(f"Distance to target: {np.linalg.norm(final_mean - target_solution):.4f}")
334
335
# Step-size and conditioning evolution
336
sigmas = logger.f[:, 2]
337
axis_ratios = logger.f[:, 3]
338
339
print(f"Initial sigma: {sigmas[0]:.4f}")
340
print(f"Final sigma: {sigmas[-1]:.4f}")
341
print(f"Final axis ratio (condition): {axis_ratios[-1]:.2e}")
342
343
# Standard deviation evolution per coordinate
344
std_evolution = logger.std[:, 4:]
345
final_stds = std_evolution[-1]
346
347
print(f"Final standard deviations: {final_stds}")
348
349
return logger
350
351
# access_logged_data_example()
352
```
353
354
## Visualization and Plotting
355
356
Comprehensive plotting capabilities for understanding optimization dynamics and diagnosing convergence issues.
357
358
### Main Plotting Functions
359
360
```python { .api }
361
def plot(filenameprefix=None, fig=None, iteridx=None, **kwargs):
362
"""
363
Plot logged CMA-ES data with comprehensive visualization.
364
365
Parameters:
366
-----------
367
filenameprefix : str, optional
368
File prefix of logged data. If None, uses default 'outcmaes'.
369
370
fig : matplotlib.figure.Figure, optional
371
Figure to plot into. If None, creates new figure.
372
373
iteridx : slice or array-like, optional
374
Iteration indices to plot (e.g., slice(100, 500) for iterations 100-500).
375
376
**kwargs : dict
377
Additional plotting options:
378
- iabscissa : int (0=iteration, 1=evaluation count for x-axis)
379
- plot_mean : bool (whether to plot mean solution evolution)
380
- foffset : float (offset for fitness values to handle zero/negative values)
381
- x_opt : array-like (known optimum for reference)
382
- fontsize : int (font size for labels)
383
- xsemilog : bool (semi-log x-axis)
384
385
Returns:
386
--------
387
matplotlib.figure.Figure
388
Figure with CMA-ES diagnostic plots.
389
390
Examples:
391
---------
392
>>> import cma
393
>>>
394
>>> # Run optimization with default logging
395
>>> x, es = cma.fmin2(cma.ff.sphere, [1, 2, 3], 0.5)
396
>>>
397
>>> # Plot results
398
>>> fig = cma.plot() # Plot data from default location
399
>>>
400
>>> # Plot specific experiment
401
>>> fig = cma.plot('my_experiment/run_001')
402
>>>
403
>>> # Plot subset of iterations
404
>>> fig = cma.plot(iteridx=slice(50, 200)) # Iterations 50-200
405
>>>
406
>>> # Plot with evaluation count on x-axis
407
>>> fig = cma.plot(iabscissa=1)
408
>>>
409
>>> # Plot with known optimum reference
410
>>> fig = cma.plot(x_opt=[0, 0, 0]) # Show distance to [0,0,0]
411
"""
412
pass
413
414
def plot_zip(filenameprefix=None, **kwargs):
415
"""
416
Plot data from compressed (.zip) CMA-ES log files.
417
418
Useful for plotting archived optimization runs without extracting files.
419
420
Parameters:
421
-----------
422
filenameprefix : str, optional
423
Prefix of zip file containing logged data.
424
425
**kwargs : dict
426
Same options as plot() function.
427
428
Examples:
429
---------
430
>>> import cma
431
>>>
432
>>> # Plot from compressed log files
433
>>> fig = cma.plot_zip('archived_runs/experiment_001.zip')
434
"""
435
pass
436
437
def disp(filenameprefix=None):
438
"""
439
Display text summary of logged CMA-ES data.
440
441
Parameters:
442
-----------
443
filenameprefix : str, optional
444
File prefix of logged data to display.
445
446
Examples:
447
---------
448
>>> import cma
449
>>>
450
>>> # Display summary of default logged data
451
>>> cma.disp()
452
>>>
453
>>> # Display specific experiment
454
>>> cma.disp('experiments/run_042')
455
"""
456
pass
457
```
458
459
### CMADataLogger Plotting Methods
460
461
```python { .api }
462
class CMADataLogger:
463
"""Extended plotting capabilities of CMADataLogger."""
464
465
def plot(self, fig=None, iabscissa=0, iteridx=None, **kwargs):
466
"""
467
Create comprehensive diagnostic plots for CMA-ES optimization.
468
469
Generates multi-panel plot showing:
470
1. Fitness evolution (best, median, worst)
471
2. Step-size (sigma) evolution
472
3. Principal axis lengths (eigenvalues of C)
473
4. Standard deviations per coordinate
474
5. Mean solution evolution (optional)
475
6. Recent best solution evolution (optional)
476
477
Parameters:
478
-----------
479
fig : matplotlib.figure.Figure, optional
480
Figure to plot into. Creates new if None.
481
482
iabscissa : int, optional
483
X-axis type: 0=iterations (default), 1=evaluation count.
484
485
iteridx : slice or array, optional
486
Iteration range to plot (e.g., slice(100, 500)).
487
488
**kwargs : dict
489
Additional options:
490
- plot_mean : bool (plot mean evolution, default False)
491
- foffset : float (fitness offset for log scale, default 1e-19)
492
- x_opt : array (known optimum for reference)
493
- fontsize : int (font size, default 7)
494
- xsemilog : bool (semi-log x-axis, default False)
495
- downsample_to : int (max points to plot, default 1e7)
496
497
Examples:
498
---------
499
>>> import cma
500
>>>
501
>>> # Run optimization
502
>>> es = cma.CMAEvolutionStrategy([1, 2, 3], 0.5)
503
>>> logger = cma.CMADataLogger().register(es)
504
>>>
505
>>> while not es.stop():
506
... solutions = es.ask()
507
... fitness_values = [sum(x**2) for x in solutions]
508
... es.tell(solutions, fitness_values)
509
... logger.add()
510
>>>
511
>>> # Create diagnostic plots
512
>>> fig = logger.plot()
513
>>>
514
>>> # Plot with custom options
515
>>> fig = logger.plot(
516
... plot_mean=True, # Show mean evolution
517
... x_opt=[0, 0, 0], # Reference optimum
518
... iabscissa=1, # X-axis: evaluation count
519
... iteridx=slice(50, None) # Skip first 50 iterations
520
... )
521
"""
522
pass
523
524
def disp(self):
525
"""
526
Display text summary of logged optimization data.
527
528
Shows:
529
- Problem dimension and optimization settings
530
- Iteration and evaluation count
531
- Best, worst, and current fitness values
532
- Convergence indicators
533
- Step-size and conditioning information
534
535
Examples:
536
---------
537
>>> logger = cma.CMADataLogger().load()
538
>>> logger.disp()
539
Loaded data from outcmaes* files
540
Dimension: 5
541
Iterations: 150
542
Evaluations: 1350
543
Best fitness: 1.23e-08
544
Final sigma: 0.0045
545
Condition number: 2.34e+03
546
"""
547
pass
548
```
549
550
### Plotting Usage Patterns
551
552
```python { .api }
553
import cma
554
import numpy as np
555
import matplotlib.pyplot as plt
556
557
# Pattern 1: Real-time plotting during optimization
558
def realtime_plotting_example():
559
"""Example of real-time plotting during optimization."""
560
561
es = cma.CMAEvolutionStrategy(5 * [0.5], 0.3, {'verb_disp': 0})
562
logger = cma.CMADataLogger().register(es)
563
564
# Create figure for real-time updates
565
plt.ion() # Interactive mode
566
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
567
568
iteration = 0
569
while not es.stop() and iteration < 200:
570
solutions = es.ask()
571
fitness_values = [sum((x - 1)**2) for x in solutions]
572
es.tell(solutions, fitness_values)
573
logger.add()
574
575
# Update plots every 10 iterations
576
if iteration % 10 == 0:
577
plt.clf() # Clear figure
578
579
# Create updated diagnostic plots
580
logger.plot(fig=fig)
581
plt.draw()
582
plt.pause(0.1) # Brief pause for display
583
584
iteration += 1
585
586
plt.ioff() # Turn off interactive mode
587
plt.show()
588
589
return logger
590
591
# Pattern 2: Custom plotting with logged data
592
def custom_plotting_example():
593
"""Create custom plots using logged CMA-ES data."""
594
595
# Load logged data
596
logger = cma.CMADataLogger().load()
597
598
# Custom multi-panel plot
599
fig, axes = plt.subplots(3, 2, figsize=(12, 10))
600
601
# 1. Fitness convergence
602
axes[0, 0].semilogy(logger.f[:, 0], logger.f[:, 4], 'b-', label='Best')
603
axes[0, 0].semilogy(logger.f[:, 0], logger.f[:, 5], 'r--', label='Median')
604
axes[0, 0].set_xlabel('Iteration')
605
axes[0, 0].set_ylabel('Fitness')
606
axes[0, 0].legend()
607
axes[0, 0].set_title('Fitness Evolution')
608
609
# 2. Step-size evolution
610
axes[0, 1].semilogy(logger.f[:, 0], logger.f[:, 2])
611
axes[0, 1].set_xlabel('Iteration')
612
axes[0, 1].set_ylabel('Sigma')
613
axes[0, 1].set_title('Step-size Evolution')
614
615
# 3. Condition number evolution
616
axes[1, 0].semilogy(logger.f[:, 0], logger.f[:, 3])
617
axes[1, 0].set_xlabel('Iteration')
618
axes[1, 0].set_ylabel('Axis Ratio')
619
axes[1, 0].set_title('Condition Number')
620
621
# 4. Standard deviations by coordinate
622
for i in range(min(5, logger.std.shape[1] - 4)): # Plot first 5 coordinates
623
axes[1, 1].semilogy(logger.std[:, 0], logger.std[:, 4 + i],
624
label=f'x[{i}]')
625
axes[1, 1].set_xlabel('Iteration')
626
axes[1, 1].set_ylabel('Standard Deviation')
627
axes[1, 1].legend()
628
axes[1, 1].set_title('Standard Deviations')
629
630
# 5. Principal axis lengths (if available)
631
if hasattr(logger, 'D') and logger.D is not None:
632
for i in range(min(3, logger.D.shape[1] - 4)):
633
axes[2, 0].semilogy(logger.D[:, 0], logger.D[:, 4 + i],
634
label=f'Axis {i+1}')
635
axes[2, 0].set_xlabel('Iteration')
636
axes[2, 0].set_ylabel('Axis Length')
637
axes[2, 0].legend()
638
axes[2, 0].set_title('Principal Axis Lengths')
639
640
# 6. Solution trajectory (2D projection)
641
if logger.xmean.shape[1] >= 6: # At least 2D problem
642
axes[2, 1].plot(logger.xmean[:, 4], logger.xmean[:, 5], 'b-o', markersize=2)
643
axes[2, 1].plot(logger.xmean[0, 4], logger.xmean[0, 5], 'go', markersize=8, label='Start')
644
axes[2, 1].plot(logger.xmean[-1, 4], logger.xmean[-1, 5], 'ro', markersize=8, label='End')
645
axes[2, 1].set_xlabel('x[0]')
646
axes[2, 1].set_ylabel('x[1]')
647
axes[2, 1].legend()
648
axes[2, 1].set_title('Solution Trajectory (2D)')
649
650
plt.tight_layout()
651
plt.show()
652
653
return fig
654
655
# Pattern 3: Comparative plotting of multiple runs
656
def comparative_plotting_example():
657
"""Compare multiple optimization runs."""
658
659
# Simulate multiple runs (in practice, load from different files)
660
run_data = {}
661
662
for run_id, sigma0 in enumerate([0.1, 0.3, 1.0]):
663
logger = cma.CMADataLogger(f'temp_run_{run_id}/')
664
es = cma.CMAEvolutionStrategy(3 * [0.5], sigma0, {'verbose': -9})
665
logger.register(es)
666
667
while not es.stop():
668
solutions = es.ask()
669
fitness_values = [sum(x**2) for x in solutions]
670
es.tell(solutions, fitness_values)
671
logger.add()
672
673
logger.load()
674
run_data[f'sigma_{sigma0}'] = logger
675
676
# Comparative plot
677
plt.figure(figsize=(12, 8))
678
679
# Subplot 1: Fitness comparison
680
plt.subplot(2, 2, 1)
681
for label, logger in run_data.items():
682
plt.semilogy(logger.f[:, 1], logger.f[:, 4], label=label) # evals vs best fitness
683
plt.xlabel('Evaluations')
684
plt.ylabel('Best Fitness')
685
plt.legend()
686
plt.title('Fitness Convergence Comparison')
687
688
# Subplot 2: Step-size comparison
689
plt.subplot(2, 2, 2)
690
for label, logger in run_data.items():
691
plt.semilogy(logger.f[:, 0], logger.f[:, 2], label=label) # iteration vs sigma
692
plt.xlabel('Iteration')
693
plt.ylabel('Sigma')
694
plt.legend()
695
plt.title('Step-size Evolution Comparison')
696
697
# Subplot 3: Convergence speed
698
plt.subplot(2, 2, 3)
699
convergence_evals = []
700
sigma_values = []
701
702
for label, logger in run_data.items():
703
# Find evaluation where fitness < 1e-6
704
converged_idx = np.where(logger.f[:, 4] < 1e-6)[0]
705
if len(converged_idx) > 0:
706
convergence_evals.append(logger.f[converged_idx[0], 1])
707
sigma_values.append(float(label.split('_')[1]))
708
else:
709
convergence_evals.append(logger.f[-1, 1]) # Final evaluation
710
sigma_values.append(float(label.split('_')[1]))
711
712
plt.bar([f'σ={s}' for s in sigma_values], convergence_evals)
713
plt.xlabel('Initial Step-size')
714
plt.ylabel('Evaluations to Convergence')
715
plt.title('Convergence Speed')
716
717
# Subplot 4: Final condition numbers
718
plt.subplot(2, 2, 4)
719
final_conditions = [logger.f[-1, 3] for logger in run_data.values()]
720
plt.bar([f'σ={s}' for s in sigma_values], final_conditions)
721
plt.xlabel('Initial Step-size')
722
plt.ylabel('Final Condition Number')
723
plt.yscale('log')
724
plt.title('Final Conditioning')
725
726
plt.tight_layout()
727
plt.show()
728
729
return run_data
730
731
# Pattern 4: Publication-quality plots
732
def publication_quality_plots():
733
"""Create publication-quality plots from CMA-ES data."""
734
735
# Load data
736
logger = cma.CMADataLogger().load()
737
738
# Set publication style
739
plt.style.use('classic')
740
plt.rcParams.update({
741
'font.size': 12,
742
'font.family': 'serif',
743
'axes.linewidth': 1.5,
744
'axes.grid': True,
745
'grid.alpha': 0.3,
746
'lines.linewidth': 2,
747
'figure.dpi': 300
748
})
749
750
fig, ax = plt.subplots(figsize=(8, 6))
751
752
# Plot fitness evolution with confidence bands (if multiple runs available)
753
iterations = logger.f[:, 0]
754
best_fitness = logger.f[:, 4]
755
median_fitness = logger.f[:, 5]
756
757
ax.semilogy(iterations, best_fitness, 'b-', linewidth=2, label='Best fitness')
758
ax.semilogy(iterations, median_fitness, 'r--', linewidth=2, label='Median fitness')
759
760
ax.set_xlabel('Iteration')
761
ax.set_ylabel('Function value')
762
ax.legend(loc='upper right')
763
ax.grid(True, alpha=0.3)
764
ax.set_title('CMA-ES Convergence on Sphere Function')
765
766
# Add annotations
767
if len(best_fitness) > 0:
768
# Mark significant milestones
769
target_values = [1e-2, 1e-6, 1e-10]
770
for target in target_values:
771
achieved_idx = np.where(best_fitness <= target)[0]
772
if len(achieved_idx) > 0:
773
iter_achieved = iterations[achieved_idx[0]]
774
ax.annotate(f'10^{{{int(np.log10(target))}}} at iter {iter_achieved}',
775
xy=(iter_achieved, target),
776
xytext=(iter_achieved + 20, target * 10),
777
arrowprops=dict(arrowstyle='->', color='gray'))
778
779
plt.tight_layout()
780
plt.savefig('cmaes_convergence.pdf', dpi=300, bbox_inches='tight')
781
plt.show()
782
783
return fig
784
785
# Run plotting examples (comment out as needed)
786
# realtime_plotting_example()
787
# custom_plotting_example()
788
# comparative_plotting_example()
789
# publication_quality_plots()
790
```
791
792
## Analysis and Diagnostics
793
794
Advanced analysis tools for understanding optimization behavior and diagnosing convergence issues.
795
796
### Convergence Analysis
797
798
```python { .api }
799
def convergence_analysis(logger_or_prefix=None):
800
"""
801
Comprehensive convergence analysis of CMA-ES optimization.
802
803
Parameters:
804
-----------
805
logger_or_prefix : CMADataLogger or str, optional
806
Logger instance or file prefix. If None, loads default data.
807
808
Returns:
809
--------
810
dict
811
Analysis results including convergence rates, bottlenecks, and diagnostics.
812
813
Examples:
814
---------
815
>>> import cma
816
>>>
817
>>> # Run optimization
818
>>> x, es = cma.fmin2(cma.ff.elli, [1, 2, 3], 0.5)
819
>>>
820
>>> # Analyze convergence
821
>>> analysis = convergence_analysis()
822
>>>
823
>>> print(f"Linear convergence rate: {analysis['linear_rate']:.3f}")
824
>>> print(f"Bottleneck phase: {analysis['bottleneck_phase']}")
825
>>> print(f"Final condition: {analysis['final_condition']:.2e}")
826
"""
827
828
# Load data if needed
829
if isinstance(logger_or_prefix, str):
830
logger = cma.CMADataLogger().load(logger_or_prefix)
831
elif logger_or_prefix is None:
832
logger = cma.CMADataLogger().load()
833
else:
834
logger = logger_or_prefix
835
836
analysis = {}
837
838
# Basic statistics
839
analysis['total_iterations'] = int(logger.f[-1, 0])
840
analysis['total_evaluations'] = int(logger.f[-1, 1])
841
analysis['initial_fitness'] = logger.f[0, 4]
842
analysis['final_fitness'] = logger.f[-1, 4]
843
analysis['fitness_improvement'] = np.log10(analysis['initial_fitness'] / analysis['final_fitness'])
844
845
# Convergence rate analysis
846
log_fitness = np.log(logger.f[:, 4])
847
iterations = logger.f[:, 0]
848
849
# Linear convergence rate (slope of log(fitness) vs iteration)
850
if len(log_fitness) > 10:
851
# Use latter half for stable convergence rate
852
start_idx = len(log_fitness) // 2
853
coeffs = np.polyfit(iterations[start_idx:], log_fitness[start_idx:], 1)
854
analysis['linear_rate'] = -coeffs[0] # Negative because we want decrease
855
else:
856
analysis['linear_rate'] = 0
857
858
# Identify convergence phases
859
fitness_diff = np.diff(log_fitness)
860
861
# Find where improvement slows down significantly
862
slow_improvement = fitness_diff > -1e-3 # Very slow improvement
863
if np.any(slow_improvement):
864
analysis['bottleneck_start'] = int(iterations[np.where(slow_improvement)[0][0]])
865
else:
866
analysis['bottleneck_start'] = None
867
868
# Step-size analysis
869
sigmas = logger.f[:, 2]
870
analysis['initial_sigma'] = sigmas[0]
871
analysis['final_sigma'] = sigmas[-1]
872
analysis['sigma_reduction'] = sigmas[0] / sigmas[-1]
873
874
# Conditioning analysis
875
axis_ratios = logger.f[:, 3]
876
analysis['initial_condition'] = axis_ratios[0]
877
analysis['final_condition'] = axis_ratios[-1]
878
analysis['max_condition'] = np.max(axis_ratios)
879
880
# Stagnation detection
881
fitness_window = 20 # Window for stagnation check
882
if len(logger.f) > fitness_window:
883
recent_fitness = logger.f[-fitness_window:, 4]
884
fitness_variation = (np.max(recent_fitness) - np.min(recent_fitness)) / np.mean(recent_fitness)
885
analysis['recent_stagnation'] = fitness_variation < 1e-12
886
else:
887
analysis['recent_stagnation'] = False
888
889
# Efficiency metrics
890
analysis['evaluations_per_iteration'] = analysis['total_evaluations'] / analysis['total_iterations']
891
if analysis['fitness_improvement'] > 0:
892
analysis['evals_per_order_magnitude'] = analysis['total_evaluations'] / analysis['fitness_improvement']
893
else:
894
analysis['evals_per_order_magnitude'] = float('inf')
895
896
return analysis
897
898
def diagnostic_summary(logger_or_prefix=None):
899
"""
900
Generate diagnostic summary for CMA-ES optimization.
901
902
Identifies common issues and provides recommendations.
903
"""
904
905
analysis = convergence_analysis(logger_or_prefix)
906
907
print("CMA-ES Diagnostic Summary")
908
print("=" * 40)
909
910
# Basic performance
911
print(f"Total iterations: {analysis['total_iterations']}")
912
print(f"Total evaluations: {analysis['total_evaluations']}")
913
print(f"Fitness improvement: {analysis['fitness_improvement']:.1f} orders of magnitude")
914
print(f"Convergence rate: {analysis['linear_rate']:.3f} (log units per iteration)")
915
916
# Efficiency assessment
917
print(f"\nEfficiency Metrics:")
918
print(f" Evaluations per iteration: {analysis['evaluations_per_iteration']:.1f}")
919
if analysis['evals_per_order_magnitude'] < float('inf'):
920
print(f" Evaluations per order of magnitude: {analysis['evals_per_order_magnitude']:.0f}")
921
922
# Conditioning assessment
923
print(f"\nConditioning Analysis:")
924
print(f" Initial condition number: {analysis['initial_condition']:.2e}")
925
print(f" Final condition number: {analysis['final_condition']:.2e}")
926
print(f" Maximum condition number: {analysis['max_condition']:.2e}")
927
928
# Issue detection and recommendations
929
print(f"\nDiagnostic Findings:")
930
931
issues = []
932
recommendations = []
933
934
# Check for poor conditioning
935
if analysis['final_condition'] > 1e12:
936
issues.append("Very high condition number (> 1e12)")
937
recommendations.append("Consider coordinate scaling or preconditioning")
938
939
# Check for stagnation
940
if analysis['recent_stagnation']:
941
issues.append("Recent fitness stagnation detected")
942
recommendations.append("May need restart or different termination criteria")
943
944
# Check convergence rate
945
if analysis['linear_rate'] < 0.01:
946
issues.append("Slow convergence rate (< 0.01)")
947
recommendations.append("Consider larger population or different step-size")
948
949
# Check step-size reduction
950
if analysis['sigma_reduction'] > 1e6:
951
issues.append("Excessive step-size reduction")
952
recommendations.append("Initial step-size may have been too large")
953
954
# Check efficiency
955
expected_evals = analysis['total_iterations'] * (4 + 3 * np.log(5)) # Rough estimate for 5D
956
if analysis['total_evaluations'] > 2 * expected_evals:
957
issues.append("Higher than expected evaluation count")
958
recommendations.append("Check for expensive function evaluations or large population")
959
960
if not issues:
961
print(" No significant issues detected - optimization appears healthy")
962
else:
963
for i, issue in enumerate(issues):
964
print(f" Issue {i+1}: {issue}")
965
966
print(f"\nRecommendations:")
967
for i, rec in enumerate(recommendations):
968
print(f" {i+1}. {rec}")
969
970
return analysis
971
972
# Example usage
973
def run_diagnostic_example():
974
"""Example of comprehensive diagnostic analysis."""
975
976
import cma
977
978
# Run optimization with logging
979
def objective(x):
980
# Ill-conditioned ellipsoid
981
scales = [10**(i/2) for i in range(len(x))]
982
return sum(s * xi**2 for s, xi in zip(scales, x))
983
984
x, es = cma.fmin2(objective, 5 * [1], 0.5,
985
options={'maxfevals': 5000, 'verbose': -1})
986
987
# Run diagnostics
988
analysis = diagnostic_summary()
989
990
# Custom analysis
991
print(f"\nCustom Analysis:")
992
logger = cma.CMADataLogger().load()
993
994
# Check for premature convergence
995
final_sigma = logger.f[-1, 2]
996
dimension = 5
997
if final_sigma < 1e-12:
998
print(" Warning: Very small final step-size - possible premature convergence")
999
1000
# Check population diversity
1001
if hasattr(logger, 'std') and logger.std is not None:
1002
final_stds = logger.std[-1, 4:]
1003
std_ratio = np.max(final_stds) / np.min(final_stds)
1004
print(f" Final coordinate std ratio: {std_ratio:.2e}")
1005
1006
if std_ratio > 1e6:
1007
print(" Warning: Very different coordinate scales - consider rescaling")
1008
1009
return analysis
1010
1011
# run_diagnostic_example()
1012
```
1013
1014
### Performance Metrics and Benchmarking
1015
1016
```python { .api }
1017
def performance_metrics(results_list):
1018
"""
1019
Compute standard performance metrics for optimization results.
1020
1021
Parameters:
1022
-----------
1023
results_list : list
1024
List of (x_best, es) tuples from multiple optimization runs.
1025
1026
Returns:
1027
--------
1028
dict
1029
Performance metrics including success rates, efficiency measures.
1030
"""
1031
1032
metrics = {
1033
'success_rate': 0,
1034
'mean_evaluations': 0,
1035
'median_evaluations': 0,
1036
'mean_final_fitness': 0,
1037
'evaluation_counts': [],
1038
'final_fitness_values': [],
1039
'successful_runs': 0,
1040
'total_runs': len(results_list)
1041
}
1042
1043
success_threshold = 1e-8
1044
1045
for x_best, es in results_list:
1046
final_fitness = es.result.fbest
1047
evaluations = es.result.evaluations
1048
1049
metrics['evaluation_counts'].append(evaluations)
1050
metrics['final_fitness_values'].append(final_fitness)
1051
1052
if final_fitness < success_threshold:
1053
metrics['successful_runs'] += 1
1054
1055
# Compute derived metrics
1056
metrics['success_rate'] = metrics['successful_runs'] / metrics['total_runs']
1057
metrics['mean_evaluations'] = np.mean(metrics['evaluation_counts'])
1058
metrics['median_evaluations'] = np.median(metrics['evaluation_counts'])
1059
metrics['mean_final_fitness'] = np.mean(metrics['final_fitness_values'])
1060
1061
# Success-conditional metrics
1062
successful_evals = [evals for evals, (_, es) in zip(metrics['evaluation_counts'], results_list)
1063
if es.result.fbest < success_threshold]
1064
1065
if successful_evals:
1066
metrics['mean_successful_evaluations'] = np.mean(successful_evals)
1067
metrics['median_successful_evaluations'] = np.median(successful_evals)
1068
else:
1069
metrics['mean_successful_evaluations'] = None
1070
metrics['median_successful_evaluations'] = None
1071
1072
return metrics
1073
1074
def benchmark_suite_analysis():
1075
"""Run CMA-ES on standard benchmark suite and analyze performance."""
1076
1077
import cma
1078
1079
# Test functions
1080
test_functions = {
1081
'sphere': lambda x: sum(x**2),
1082
'ellipsoid': lambda x: sum(10**(6*i/(len(x)-1)) * xi**2 for i, xi in enumerate(x)),
1083
'rosenbrock': lambda x: sum(100*(x[i+1] - x[i]**2)**2 + (1 - x[i])**2 for i in range(len(x)-1)),
1084
'rastrigin': lambda x: sum(xi**2 - 10*np.cos(2*np.pi*xi) + 10 for xi in x)
1085
}
1086
1087
dimensions = [2, 5, 10]
1088
repetitions = 5
1089
1090
results = {}
1091
1092
for func_name, func in test_functions.items():
1093
results[func_name] = {}
1094
1095
for dim in dimensions:
1096
print(f"Testing {func_name} in {dim}D...")
1097
1098
run_results = []
1099
1100
for rep in range(repetitions):
1101
try:
1102
x, es = cma.fmin2(func, dim * [0.5], 0.3,
1103
options={'maxfevals': 1000 * dim**2, 'verbose': -9})
1104
run_results.append((x, es))
1105
except Exception as e:
1106
print(f" Run {rep} failed: {e}")
1107
1108
if run_results:
1109
metrics = performance_metrics(run_results)
1110
results[func_name][dim] = metrics
1111
1112
# Print summary
1113
print("\nBenchmark Results Summary")
1114
print("=" * 50)
1115
1116
for func_name in results:
1117
print(f"\nFunction: {func_name}")
1118
print("Dim Success% Mean Evals Median Evals Mean Final")
1119
print("-" * 55)
1120
1121
for dim in sorted(results[func_name].keys()):
1122
m = results[func_name][dim]
1123
print(f"{dim:3d} {m['success_rate']:5.1%} {m['mean_evaluations']:8.0f} "
1124
f"{m['median_evaluations']:8.0f} {m['mean_final_fitness']:8.2e}")
1125
1126
return results
1127
1128
# benchmark_suite_analysis()
1129
```
1130
1131
This comprehensive logging and analysis documentation provides:
1132
1133
1. **CMADataLogger** - Complete data logging capabilities with file management
1134
2. **Data Structure** - Detailed explanation of logged data files and access patterns
1135
3. **Visualization** - Comprehensive plotting functions and customization options
1136
4. **Real-time Monitoring** - Live plotting and monitoring during optimization
1137
5. **Analysis Tools** - Convergence analysis, diagnostics, and performance metrics
1138
6. **Benchmarking** - Tools for systematic performance evaluation
1139
1140
The documentation enables users to fully monitor, analyze, and understand CMA-ES optimization behavior for both research and practical applications.