1# -*- coding: utf-8 -*-
2"""CMA-ES (evolution strategy), the main sub-module of `cma` providing
3in particular `CMAOptions`, `CMAEvolutionStrategy`, and `fmin`
4"""
5
6# TODO (mainly done): remove separable CMA within the code (keep as sampler only)
7# TODO (low): implement a (deep enough) copy-constructor for class
8#       CMAEvolutionStrategy to repeat the same step in different
9#       configurations for online-adaptation of meta parameters
10# TODO (complex): reconsider geno-pheno transformation. Can it be a
11#       separate module that operates inbetween optimizer and objective?
12#       Can we still propagate a repair of solutions to the optimizer?
13#       A repair-only internal geno-pheno transformation is less
14#       problematic, given the repair is idempotent. In any case, consider
15#       passing a repair function in the interface instead.
16#       How about gradients (should be fine)?
17#       Must be *thoroughly* explored before to switch, in particular the
18#       order of application of repair and other transformations, as the
19#       internal repair can only operate on the internal representation.
20# TODO: split tell into a variable transformation part and the "pure"
21#       functionality
22#       usecase: es.tell_geno(X, [func(es.pheno(x)) for x in X])
23#       genotypic repair is not part of tell_geno
24# TODO: self.opts['mindx'] is checked without sigma_vec, which is a little
25#       inconcise. Cheap solution: project sigma_vec on smallest eigenvector?
26# TODO: class _CMAStopDict implementation looks way too complicated,
27#       design generically from scratch?
28# TODO: separate display and logging options, those CMAEvolutionStrategy
29#       instances don't use themselves (probably all?)
30# TODO: check scitools.easyviz and how big the adaptation would be
31# TODO: separate initialize==reset_state from __init__
32# TODO: keep best ten solutions
33# TODO: implement constraints handling
34# TODO: eigh(): thorough testing would not hurt
35# TODO: (partly done) apply style guide
36# WON'T FIX ANYTIME SOON (done within fmin): implement bipop in a separate
37#       algorithm as meta portfolio algorithm of IPOP and a local restart
38#       option to be implemented
39#       in fmin (e.g. option restart_mode in [IPOP, local])
40# DONE: extend function unitdoctest, or use unittest?
41# DONE: copy_always optional parameter does not make much sense,
42#       as one can always copy the input argument first. Similar,
43#       copy_if_changed should be keep_arg_unchanged or just copy
44# DONE: expand/generalize to dynamically read "signals" from a file
45#       see import ConfigParser, DictFromTagsInString,
46#       function read_properties, or myproperties.py (to be called after
47#       tell()), signals_filename, if given, is parsed in stop()
48# DONE: switch to np.loadtxt
49#
50# typical parameters in scipy.optimize: disp, xtol, ftol, maxiter, maxfun,
51#         callback=None
52#         maxfev, diag (A sequency of N positive entries that serve as
53#                 scale factors for the variables.)
54#           full_output -- non-zero to return all optional outputs.
55#   If xtol < 0.0, xtol is set to sqrt(machine_precision)
56#    'infot -- a dictionary of optional outputs with the keys:
57#                      'nfev': the number of function calls...
58#
59#    see eg fmin_powell
60# typical returns
61#        x, f, dictionary d
62#        (xopt, {fopt, gopt, Hopt, func_calls, grad_calls, warnflag},
63#         <allvecs>)
64#
65
66# changes:
67# 20/04/xx: no negative weights for injected solutions
68# 16/10/xx: versatile options are read from signals_filename
69#           RecombinationWeights refined and work without numpy
70#           new options: recombination_weights, timeout,
71#           integer_variable with basic integer handling
72#           step size parameters removed from CMAEvolutionStrategy class
73#           ComposedFunction class implements function composition
74# 16/10/02: copy_always parameter is gone everywhere, use
75#           np.array(., copy=True)
76# 16/xx/xx: revised doctests with doctest: +ELLIPSIS option, test call(s)
77#           moved all test related to test.py, is quite clean now
78#           "python -m cma.test" is how it works now
79# 16/xx/xx: cleaning up, all kind of larger changes.
80# 16/xx/xx: single file cma.py broken into pieces such that cma has now
81#           become a package.
82# 15/02/xx: (v1.2) sampling from the distribution sampling refactorized
83#           in class Sampler which also does the (natural gradient)
84#           update. New AdaptiveDecoding class for sigma_vec.
85# 15/01/26: bug fix in multiplyC with sep/diagonal option
86# 15/01/20: larger condition numbers for C realized by using tf_pheno
87#           of GenoPheno attribute gp.
88# 15/01/19: injection method, first implementation, short injections
89#           and long injections with good fitness need to be addressed yet
90# 15/01/xx: _prepare_injection_directions to simplify/centralize injected
91#           solutions from mirroring and TPA
92# 14/12/26: bug fix in correlation_matrix computation if np.diag is a view
93# 14/12/06: meta_parameters now only as annotations in ## comments
94# 14/12/03: unified use of base class constructor call, now always
95#         super(ThisClass, self).__init__(args_for_base_class_constructor)
96# 14/11/29: termination via "stop now" in file cmaes_signals.par
97# 14/11/28: bug fix initialization of C took place before setting the
98#           seed. Now in some dimensions (e.g. 10) results are (still) not
99#           determistic due to np.linalg.eigh, in some dimensions (<9, 12)
100#           they seem to be deterministic.
101# 14/11/23: bipop option integration, contributed by Petr Baudis
102# 14/09/30: initial_elitism option added to fmin
103# 14/08/1x: developing fitness wrappers in FFWrappers class
104# 14/08/xx: return value of OOOptimizer.optimize changed to self.
105#           CMAOptions now need to uniquely match an *initial substring*
106#           only (via method corrected_key).
107#           Bug fix in CMAEvolutionStrategy.stop: termination conditions
108#           are now recomputed iff check and self.countiter > 0.
109#           Doc corrected that self.gp.geno _is_ applied to x0
110#           Vaste reorganization/modularization/improvements of plotting
111# 14/08/01: bug fix to guaranty pos. def. in active CMA
112# 14/06/04: gradient of f can now be used with fmin and/or ask
113# 14/05/11: global rcParams['font.size'] not permanently changed anymore,
114#           a little nicer annotations for the plots
115# 14/05/07: added method result_pretty to pretty print optimization result
116# 14/05/06: associated show() everywhere with ion() which should solve the
117#           blocked terminal problem
118# 14/05/05: all instances of "unicode" removed (was incompatible to 3.x)
119# 14/05/05: replaced type(x) == y with isinstance(x, y), reorganized the
120#           comments before the code starts
121# 14/05/xx: change the order of kwargs of OOOptimizer.optimize,
122#           remove prepare method in AdaptSigma classes, various changes/cleaning
123# 14/03/01: bug fix BoundaryHandlerBase.has_bounds didn't check lower bounds correctly
124#           bug fix in BoundPenalty.repair len(bounds[0]) was used instead of len(bounds[1])
125#           bug fix in GenoPheno.pheno, where x was not copied when only boundary-repair was applied
126# 14/02/27: bug fixed when BoundPenalty was combined with fixed variables.
127# 13/xx/xx: step-size adaptation becomes a class derived from CMAAdaptSigmaBase,
128#           to make testing different adaptation rules (much) easier
129# 12/12/14: separated CMAOptions and arguments to fmin
130# 12/10/25: removed useless check_points from fmin interface
131# 12/10/17: bug fix printing number of infeasible samples, moved not-in-use methods
132#           timesCroot and divCroot to the right class
133# 12/10/16 (0.92.00): various changes commit: bug bound[0] -> bounds[0], more_to_write fixed,
134#   sigma_vec introduced, restart from elitist, trace normalization, max(mu,popsize/2)
135#   is used for weight calculation.
136# 12/07/23: (bug:) BoundPenalty.update respects now genotype-phenotype transformation
137# 12/07/21: convert value True for noisehandling into 1 making the output compatible
138# 12/01/30: class Solution and more old stuff removed r3101
139# 12/01/29: class Solution is depreciated, GenoPheno and SolutionDict do the job (v0.91.00, r3100)
140# 12/01/06: CMA_eigenmethod option now takes a function (integer still works)
141# 11/09/30: flat fitness termination checks also history length
142# 11/09/30: elitist option (using method clip_or_fit_solutions)
143# 11/09/xx: method clip_or_fit_solutions for check_points option for all sorts of
144#           injected or modified solutions and even reliable adaptive encoding
145# 11/08/19: fixed: scaling and typical_x type clashes 1 vs array(1) vs ones(dim) vs dim * [1]
146# 11/07/25: fixed: fmin wrote first and last line even with verb_log==0
147#           fixed: method settableOptionsList, also renamed to versatileOptions
148#           default seed depends on time now
149# 11/07/xx (0.9.92): added: active CMA, selective mirrored sampling, noise/uncertainty handling
150#           fixed: output argument ordering in fmin, print now only used as function
151#           removed: parallel option in fmin
152# 11/07/01: another try to get rid of the memory leak by replacing self.unrepaired = self[:]
153# 11/07/01: major clean-up and reworking of abstract base classes and of the documentation,
154#           also the return value of fmin changed and attribute stop is now a method.
155# 11/04/22: bug-fix: option fixed_variables in combination with scaling
156# 11/04/21: stopdict is not a copy anymore
157# 11/04/15: option fixed_variables implemented
158# 11/03/23: bug-fix boundary update was computed even without boundaries
159# 11/03/12: bug-fix of variable annotation in plots
160# 11/02/05: work around a memory leak in numpy
161# 11/02/05: plotting routines improved
162# 10/10/17: cleaning up, now version 0.9.30
163# 10/10/17: bug-fix: return values of fmin now use phenotyp (relevant
164#           if input scaling_of_variables is given)
165# 08/10/01: option evalparallel introduced,
166#           bug-fix for scaling being a vector
167# 08/09/26: option CMAseparable becomes CMA_diagonal
168# 08/10/18: some names change, test functions go into a class
169# 08/10/24: more refactorizing
170# 10/03/09: upper bound np.exp(min(1,...)) for step-size control
171
172from __future__ import (absolute_import, division, print_function,
173                        )  # unicode_literals, with_statement)
174# from builtins import ...
175from .utilities.python3for2 import range  # redefine range in Python 2
176
177import sys
178import os
179import time  # not really essential
180import warnings  # catch numpy warnings
181import ast  # for literal_eval
182try:
183    import collections  # not available in Python 2.5
184except ImportError:
185    pass
186import math
187import numpy as np
188# arange, cos, size, eye, inf, dot, floor, outer, zeros, linalg.eigh,
189# sort, argsort, random, ones,...
190from numpy import inf, array
191# to access the built-in sum fct:  ``__builtins__.sum`` or ``del sum``
192# removes the imported sum and recovers the shadowed build-in
193
194# import logging
195# logging.basicConfig(level=logging.INFO)  # only works before logging is used
196# logging.info('message')  # prints INFO:root:message on red background
197# logger = logging.getLogger(__name__)  # should not be done during import
198# logger.info('message')  # prints INFO:cma...:message on red background
199# see https://fangpenlin.com/posts/2012/08/26/good-logging-practice-in-python/
200
201from . import interfaces
202from . import transformations
203from . import optimization_tools as ot
204from . import sampler
205from .utilities import utils as _utils
206from .constraints_handler import BoundNone, BoundPenalty, BoundTransform, AugmentedLagrangian
207from .recombination_weights import RecombinationWeights
208from .logger import CMADataLogger  # , disp, plot
209from .utilities.utils import BlancClass as _BlancClass
210from .utilities.utils import rglen  #, global_verbosity
211from .utilities.utils import pprint
212from .utilities.utils import seval as eval
213from .utilities.utils import SolutionDict as _SolutionDict
214from .utilities.math import Mh
215from .sigma_adaptation import *
216from . import restricted_gaussian_sampler as _rgs
217
218_where = np.nonzero  # to make pypy work, this is how where is used here anyway
219del division, print_function, absolute_import  #, unicode_literals, with_statement
220
221class InjectionWarning(UserWarning):
222    """Injected solutions are not passed to tell as expected"""
223
224# use_archives uses collections
225use_archives = sys.version_info[0] >= 3 or sys.version_info[1] >= 6
226# use_archives = False  # on False some unit tests fail
227"""speed up for very large population size. `use_archives` prevents the
228need for an inverse gp-transformation, relies on collections module,
229not sure what happens if set to ``False``. """
230
231class MetaParameters(object):
232    """collection of many meta parameters.
233
234    Meta parameters are either annotated constants or refer to
235    options from `CMAOptions` or are arguments to `fmin` or to the
236    `NoiseHandler` class constructor.
237
238    `MetaParameters` take only effect if the source code is modified by
239    a meta parameter weaver module searching for ## meta_parameters....
240    and modifying the next line.
241
242    Details
243    -------
244    This code contains a single class instance `meta_parameters`
245
246    Some interfaces rely on parameters being either `int` or
247    `float` only. More sophisticated choices are implemented via
248    ``choice_value = {1: 'this', 2: 'or that'}[int_param_value]`` here.
249
250    CAVEAT
251    ------
252    `meta_parameters` should not be used to determine default
253    arguments, because these are assigned only once and for all during
254    module import.
255
256    """
257    def __init__(self):
258        """assign settings to be used"""
259        self.sigma0 = None  ## [~0.01, ~10]  # no default available
260
261        # learning rates and back-ward time horizons
262        self.CMA_cmean = 1.0  ## [~0.1, ~10]  #
263        self.c1_multiplier = 1.0  ## [~1e-4, ~20] l
264        self.cmu_multiplier = 2.0  ## [~1e-4, ~30] l  # zero means off
265        self.CMA_active = 1.0  ## [~1e-4, ~10] l  # 0 means off, was CMA_activefac
266        self.cc_multiplier = 1.0  ## [~0.01, ~20] l
267        self.cs_multiplier = 1.0 ## [~0.01, ~10] l  # learning rate for cs
268        self.CSA_dampfac = 1.0  ## [~0.01, ~10]
269        self.CMA_dampsvec_fac = None  ## [~0.01, ~100]  # def=np.Inf or 0.5, not clear whether this is a log parameter
270        self.CMA_dampsvec_fade = 0.1  ## [0, ~2]
271
272        # exponents for learning rates
273        self.c1_exponent = 2.0  ## [~1.25, 2]
274        self.cmu_exponent = 2.0  ## [~1.25, 2]
275        self.cact_exponent = 1.5  ## [~1.25, 2]
276        self.cc_exponent = 1.0  ## [~0.25, ~1.25]
277        self.cs_exponent = 1.0  ## [~0.25, ~1.75]  # upper bound depends on CSA_clip_length_value
278
279        # selection related parameters
280        self.lambda_exponent = 0.0  ## [0, ~2.5]  # usually <= 2, used by adding N**lambda_exponent to popsize-1
281        self.CMA_elitist = 0  ## [0, 2] i  # a choice variable
282        self.CMA_mirrors = 0.0  ## [0, 0.5)  # values <0.5 are interpreted as fraction, values >1 as numbers (rounded), otherwise about 0.16 is used',
283
284        # sampling strategies
285        # self.CMA_sample_on_sphere_surface = 0  ## [0, 1] i  # boolean
286        self.mean_shift_line_samples = 0  ## [0, 1] i  # boolean
287        self.pc_line_samples = 0  ## [0, 1] i  # boolean
288
289        # step-size adapation related parameters
290        self.CSA_damp_mueff_exponent = 0.5  ## [~0.25, ~1.5]  # zero would mean no dependency of damping on mueff, useful with CSA_disregard_length option',
291        self.CSA_disregard_length = 0  ## [0, 1] i
292        self.CSA_squared = 0  ## [0, 1] i
293        self.CSA_clip_length_value = None  ## [0, ~20]  # None reflects inf
294
295        # noise handling
296        self.noise_reeval_multiplier = 1.0  ## [0.2, 4]  # usually 2 offspring are reevaluated
297        self.noise_choose_reeval = 1  ## [1, 3] i  # which ones to reevaluate
298        self.noise_theta = 0.5  ## [~0.05, ~0.9]
299        self.noise_alphasigma = 2.0  ## [0, 10]
300        self.noise_alphaevals = 2.0  ## [0, 10]
301        self.noise_alphaevalsdown_exponent = -0.25  ## [-1.5, 0]
302        self.noise_aggregate = None  ## [1, 2] i  # None and 0 == default or user option choice, 1 == median, 2 == mean
303        # TODO: more noise handling options (maxreevals...)
304
305        # restarts
306        self.restarts = 0  ## [0, ~30]  # but depends on popsize inc
307        self.restart_from_best = 0  ## [0, 1] i  # bool
308        self.incpopsize = 2.0  ## [~1, ~5]
309
310        # termination conditions (for restarts)
311        self.maxiter_multiplier = 1.0  ## [~0.01, ~100] l
312        self.mindx = 0.0  ## [1e-17, ~1e-3] l  #v minimal std in any direction, cave interference with tol*',
313        self.minstd = 0.0  ## [1e-17, ~1e-3] l  #v minimal std in any coordinate direction, cave interference with tol*',
314        self.maxstd = None  ## [~1, ~1e9] l  #v maximal std in any coordinate direction, default is inf',
315        self.tolfacupx = 1e3  ## [~10, ~1e9] l  #v termination when step-size increases by tolfacupx (diverges). That is, the initial step-size was chosen far too small and better solutions were found far away from the initial solution x0',
316        self.tolupsigma = 1e20  ## [~100, ~1e99] l  #v sigma/sigma0 > tolupsigma * max(sqrt(eivenvals(C))) indicates "creeping behavior" with usually minor improvements',
317        self.tolx = 1e-11  ## [1e-17, ~1e-3] l  #v termination criterion: tolerance in x-changes',
318        self.tolfun = 1e-11  ## [1e-17, ~1e-3] l  #v termination criterion: tolerance in function value, quite useful',
319        self.tolfunrel = 0  ## [1e-17, ~1e-2] l  #v termination criterion: relative tolerance in function value',
320        self.tolfunhist = 1e-12  ## [1e-17, ~1e-3] l  #v termination criterion: tolerance in function value history',
321        self.tolstagnation_multiplier = 1.0  ## [0.01, ~100]  # ': 'int(100 + 100 * N**1.5 / popsize)  #v termination if no improvement over tolstagnation iterations',
322
323        # abandoned:
324        # self.noise_change_sigma_exponent = 1.0  ## [0, 2]
325        # self.noise_epsilon = 1e-7  ## [0, ~1e-2] l  #
326        # self.maxfevals = None  ## [1, ~1e11] l  # is not a performance parameter
327        # self.lambda_log_multiplier = 3  ## [0, ~10]
328        # self.lambda_multiplier = 0  ## (0, ~10]
329
330meta_parameters = MetaParameters()
331
332def is_feasible(x, f):
333    """default to check feasibility of f-values.
334
335    Used for rejection sampling in method `ask_and_eval`.
336
337    :See also: CMAOptions, ``CMAOptions('feas')``.
338    """
339    return f is not None and not np.isnan(f)
340
341
342if use_archives:
343
344    class _CMASolutionDict(_SolutionDict):
345        def __init__(self, *args, **kwargs):
346            # _SolutionDict.__init__(self, *args, **kwargs)
347            super(_CMASolutionDict, self).__init__(*args, **kwargs)
348            self.last_solution_index = 0
349
350        # TODO: insert takes 30% of the overall CPU time, mostly in def key()
351        #       with about 15% of the overall CPU time
352        def insert(self, key, geno=None, iteration=None, fitness=None,
353                   value=None, cma_norm=None):
354            """insert an entry with key ``key`` and value
355            ``value if value is not None else {'geno':key}`` and
356            ``self[key]['kwarg'] = kwarg if kwarg is not None`` for the further kwargs.
357
358            """
359            # archive returned solutions, first clean up archive
360            if iteration is not None and iteration > self.last_iteration and (iteration % 10) < 1:
361                self.truncate(300, iteration - 3)
362            elif value is not None and value.get('iteration'):
363                iteration = value['iteration']
364                if (iteration % 10) < 1:
365                    self.truncate(300, iteration - 3)
366
367            self.last_solution_index += 1
368            if value is not None:
369                try:
370                    iteration = value['iteration']
371                except:
372                    pass
373            if iteration is not None:
374                if iteration > self.last_iteration:
375                    self.last_solution_index = 0
376                self.last_iteration = iteration
377            else:
378                iteration = self.last_iteration + 0.5  # a hack to get a somewhat reasonable value
379            if value is not None:
380                self[key] = value
381            else:
382                self[key] = {'pheno': key}
383            if geno is not None:
384                self[key]['geno'] = geno
385            if iteration is not None:
386                self[key]['iteration'] = iteration
387            if fitness is not None:
388                self[key]['fitness'] = fitness
389            if cma_norm is not None:
390                self[key]['cma_norm'] = cma_norm
391            return self[key]
392
393else:  # if not use_archives:
394    class _CMASolutionDict(dict):
395        """a hack to get most code examples running"""
396        def insert(self, *args, **kwargs):
397            pass
398        def get(self, key):
399            return None
400        def __getitem__(self, key):
401            return None
402        def __setitem__(self, key, value):
403            pass
404
405# ____________________________________________________________
406# ____________________________________________________________
407# check out built-in package abc: class ABCMeta, abstractmethod, abstractproperty...
408# see http://docs.python.org/whatsnew/2.6.html PEP 3119 abstract base classes
409#
410
411_debugging = False  # not in use
412_new_injections = True
413_assertions_quadratic = True  # issue warnings
414_assertions_cubic = True
415_depreciated = True
416
417def cma_default_options_(  # to get keyword completion back
418    # the follow string arguments are evaluated if they do not contain "filename"
419    AdaptSigma='True  # or False or any CMAAdaptSigmaBase class e.g. CMAAdaptSigmaTPA, CMAAdaptSigmaCSA',
420    CMA_active='True  # negative update, conducted after the original update',
421#    CMA_activefac='1  # learning rate multiplier for active update',
422    CMA_active_injected='0  #v weight multiplier for negative weights of injected solutions',
423    CMA_cmean='1  # learning rate for the mean value',
424    CMA_const_trace='False  # normalize trace, 1, True, "arithm", "geom", "aeig", "geig" are valid',
425    CMA_diagonal='0*100*N/popsize**0.5  # nb of iterations with diagonal covariance matrix,'\
426                                        ' True for always',  # TODO 4/ccov_separable?
427    CMA_eigenmethod='np.linalg.eigh  # or cma.utilities.math.eig or pygsl.eigen.eigenvectors',
428    CMA_elitist='False  #v or "initial" or True, elitism likely impairs global search performance',
429    CMA_injections_threshold_keep_len='1  #v keep length if Mahalanobis length is below the given relative threshold',
430    CMA_mirrors='popsize < 6  # values <0.5 are interpreted as fraction, values >1 as numbers (rounded),'\
431                              ' for `True` about 0.16 is used',
432    CMA_mirrormethod='2  # 0=unconditional, 1=selective, 2=selective with delay',
433    CMA_mu='None  # parents selection parameter, default is popsize // 2',
434    CMA_on='1  # multiplier for all covariance matrix updates',
435    # CMA_sample_on_sphere_surface='False  #v replaced with option randn=cma.utilities.math.randhss, all mutation vectors have the same length, currently (with new_sampling) not in effect',
436    CMA_sampler='None  # a class or instance that implements the interface of'\
437                       ' `cma.interfaces.StatisticalModelSamplerWithZeroMeanBaseClass`',
438    CMA_sampler_options='{}  # options passed to `CMA_sampler` class init as keyword arguments',
439    CMA_rankmu='1.0  # multiplier for rank-mu update learning rate of covariance matrix',
440    CMA_rankone='1.0  # multiplier for rank-one update learning rate of covariance matrix',
441    CMA_recombination_weights='None  # a list, see class RecombinationWeights, overwrites CMA_mu and popsize options',
442    CMA_dampsvec_fac='np.Inf  # tentative and subject to changes, 0.5 would be a "default" damping for sigma vector update',
443    CMA_dampsvec_fade='0.1  # tentative fading out parameter for sigma vector update',
444    CMA_teststds='None  # factors for non-isotropic initial distr. of C, mainly for test purpose, see CMA_stds for production',
445    CMA_stds='None  # multipliers for sigma0 in each coordinate, not represented in C, makes scaling_of_variables obsolete',
446    # CMA_AII='False  # not yet tested',
447    CSA_dampfac='1  #v positive multiplier for step-size damping, 0.3 is close to optimal on the sphere',
448    CSA_damp_mueff_exponent='0.5  # zero would mean no dependency of damping on mueff, useful with CSA_disregard_length option',
449    CSA_disregard_length='False  #v True is untested, also changes respective parameters',
450    CSA_clip_length_value='None  #v poorly tested, [0, 0] means const length N**0.5, [-1, 1] allows a variation of +- N/(N+2), etc.',
451    CSA_squared='False  #v use squared length for sigma-adaptation ',
452    BoundaryHandler='BoundTransform  # or BoundPenalty, unused when ``bounds in (None, [None, None])``',
453    bounds='[None, None]  # lower (=bounds[0]) and upper domain boundaries, each a scalar or a list/vector',
454     # , eval_parallel2='not in use {"processes": None, "timeout": 12, "is_feasible": lambda x: True} # distributes function calls to processes processes'
455     # 'callback='None  # function or list of functions called as callback(self) at the end of the iteration (end of tell)', # only necessary in fmin and optimize
456    conditioncov_alleviate='[1e8, 1e12]  # when to alleviate the condition in the coordinates and in main axes',
457    eval_final_mean='True  # evaluate the final mean, which is a favorite return candidate',
458    fixed_variables='None  # dictionary with index-value pairs like {0:1.1, 2:0.1} that are not optimized',
459    ftarget='-inf  #v target function value, minimization',
460    integer_variables='[]  # index list, invokes basic integer handling: prevent std dev to become too small in the given variables',
461    is_feasible='is_feasible  #v a function that computes feasibility, by default lambda x, f: f not in (None, np.NaN)',
462    maxfevals='inf  #v maximum number of function evaluations',
463    maxiter='100 + 150 * (N+3)**2 // popsize**0.5  #v maximum number of iterations',
464    mean_shift_line_samples='False #v sample two new solutions colinear to previous mean shift',
465    mindx='0  #v minimal std in any arbitrary direction, cave interference with tol*',
466    minstd='0  #v minimal std (scalar or vector) in any coordinate direction, cave interference with tol*',
467    maxstd='inf  #v maximal std in any coordinate direction',
468    pc_line_samples='False #v one line sample along the evolution path pc',
469    popsize='4 + 3 * np.log(N)  # population size, AKA lambda, int(popsize) is the number of new solution per iteration',
470    popsize_factor='1  # multiplier for popsize, convenience option to increase default popsize',
471    randn='np.random.randn  #v randn(lam, N) must return an np.array of shape (lam, N), see also cma.utilities.math.randhss',
472    scaling_of_variables='None  # deprecated, rather use fitness_transformations.ScaleCoordinates instead (or possibly CMA_stds). Scale for each variable in that effective_sigma0 = sigma0*scaling. Internally the variables are divided by scaling_of_variables and sigma is unchanged, default is `np.ones(N)`',
473    seed='time  # random number seed for `numpy.random`; `None` and `0` equate to `time`,'\
474                ' `np.nan` means "do nothing", see also option "randn"',
475    signals_filename='cma_signals.in  # read versatile options from this file (use `None` or `""` for no file)'\
476                                      ' which contains a single options dict, e.g. ``{"timeout": 0}`` to stop,'\
477                                      ' string-values are evaluated, e.g. "np.inf" is valid',
478    termination_callback='[]  #v a function or list of functions returning True for termination, called in'\
479                              ' `stop` with `self` as argument, could be abused for side effects',
480    timeout='inf  #v stop if timeout seconds are exceeded, the string "2.5 * 60**2" evaluates to 2 hours and 30 minutes',
481    tolconditioncov='1e14  #v stop if the condition of the covariance matrix is above `tolconditioncov`',
482    tolfacupx='1e3  #v termination when step-size increases by tolfacupx (diverges). That is, the initial'\
483                     ' step-size was chosen far too small and better solutions were found far away from the initial solution x0',
484    tolupsigma='1e20  #v sigma/sigma0 > tolupsigma * max(eivenvals(C)**0.5) indicates "creeping behavior" with usually'\
485                       ' minor improvements',
486    tolflatfitness='1  #v iterations tolerated with flat fitness before termination',
487    tolfun='1e-11  #v termination criterion: tolerance in function value, quite useful',
488    tolfunhist='1e-12  #v termination criterion: tolerance in function value history',
489    tolfunrel='0  #v termination criterion: relative tolerance in function value:'\
490                   ' Delta f current < tolfunrel * (median0 - median_min)',
491    tolstagnation='int(100 + 100 * N**1.5 / popsize)  #v termination if no improvement over tolstagnation iterations',
492    tolx='1e-11  #v termination criterion: tolerance in x-changes',
493    transformation='''None  # depreciated, use cma.fitness_transformations.FitnessTransformation instead.
494            [t0, t1] are two mappings, t0 transforms solutions from CMA-representation to f-representation (tf_pheno),
495            t1 is the (optional) back transformation, see class GenoPheno''',
496    typical_x='None  # used with scaling_of_variables',
497    updatecovwait='None  #v number of iterations without distribution update, name is subject to future changes',  # TODO: rename: iterwaitupdatedistribution?
498    verbose='3  #v verbosity e.g. of initial/final message, -1 is very quiet, -9 maximally quiet, may not be fully implemented',
499    verb_append='0  # initial evaluation counter, if append, do not overwrite output files',
500    verb_disp='100  #v verbosity: display console output every verb_disp iteration',
501    verb_filenameprefix=CMADataLogger.default_prefix + '  # output path and filenames prefix',
502    verb_log='1  #v verbosity: write data to files every verb_log iteration, writing can be'\
503                  ' time critical on fast to evaluate functions',
504    verb_log_expensive='N * (N <= 50)  # allow to execute eigendecomposition for logging every verb_log_expensive iteration,'\
505                                       ' 0 or False for never',
506    verb_plot='0  #v in fmin(): plot() is called every verb_plot iteration',
507    verb_time='True  #v output timings on console',
508    vv='{}  #? versatile set or dictionary for hacking purposes, value found in self.opts["vv"]'
509    ):
510    """use this function to get keyword completion for `CMAOptions`.
511
512    ``cma.CMAOptions('substr')`` provides even substring search.
513
514    returns default options as a `dict` (not a `cma.CMAOptions` `dict`).
515    """
516    return dict(locals())  # is defined before and used by CMAOptions, so it can't return CMAOptions
517
518cma_default_options = cma_default_options_()  # will later be reassigned as CMAOptions(dict)
519cma_versatile_options = tuple(sorted(k for (k, v) in cma_default_options.items()
520                                     if v.find(' #v ') > 0))
521cma_allowed_options_keys = dict([s.lower(), s] for s in cma_default_options)
522
523class CMAOptions(dict):
524    """a dictionary with the available options and their default values
525    for class `CMAEvolutionStrategy`.
526
527    ``CMAOptions()`` returns a `dict` with all available options and their
528    default values with a comment string.
529
530    ``CMAOptions('verb')`` returns a subset of recognized options that
531    contain 'verb' in there keyword name or (default) value or
532    description.
533
534    ``CMAOptions(opts)`` returns the subset of recognized options in
535    ``dict(opts)``.
536
537    Option values can be "written" in a string and, when passed to `fmin`
538    or `CMAEvolutionStrategy`, are evaluated using "N" and "popsize" as
539    known values for dimension and population size (sample size, number
540    of new solutions per iteration). All default option values are given
541    as such a string.
542
543    Details
544    -------
545    `CMAOptions` entries starting with ``tol`` are termination
546    "tolerances".
547
548    For `tolstagnation`, the median over the first and the second half
549    of at least `tolstagnation` iterations are compared for both, the
550    per-iteration best and per-iteration median function value.
551
552    Example
553    -------
554    ::
555
556        import cma
557        cma.CMAOptions('tol')
558
559    is a shortcut for ``cma.CMAOptions().match('tol')`` that returns all
560    options that contain 'tol' in their name or description.
561
562    To set an option::
563
564        import cma
565        opts = cma.CMAOptions()
566        opts.set('tolfun', 1e-12)
567        opts['tolx'] = 1e-11
568
569    todo: this class is overly complex and should be re-written, possibly
570    with reduced functionality.
571
572    :See also: `fmin` (), `CMAEvolutionStrategy`, `_CMAParameters`
573
574    """
575
576    # @classmethod # self is the class, not the instance
577    # @property
578    # def default(self):
579    #     """returns all options with defaults"""
580    #     return fmin([],[])
581
582    @staticmethod
583    def defaults():
584        """return a dictionary with default option values and description"""
585        return cma_default_options
586        # return dict((str(k), str(v)) for k, v in cma_default_options_().items())
587        # getting rid of the u of u"name" by str(u"name")
588        # return dict(cma_default_options)
589
590    @staticmethod
591    def versatile_options():
592        """return list of options that can be changed at any time (not
593        only be initialized).
594
595        Consider that this list might not be entirely up
596        to date.
597
598        The string ' #v ' in the default value indicates a versatile
599        option that can be changed any time, however a string will not
600        necessarily be evaluated again.
601
602        """
603        return cma_versatile_options
604        # return tuple(sorted(i[0] for i in list(CMAOptions.defaults().items()) if i[1].find(' #v ') > 0))
605    def check(self, options=None):
606        """check for ambiguous keys and move attributes into dict"""
607        self.check_values(options)
608        self.check_attributes(options)
609        self.check_values(options)
610        return self
611    def check_values(self, options=None):
612        corrected_key = CMAOptions().corrected_key  # caveat: infinite recursion
613        validated_keys = []
614        original_keys = []
615        if options is None:
616            options = self
617        for key in options:
618            correct_key = corrected_key(key)
619            if correct_key is None:
620                raise ValueError('%s is not a valid option.\n'
621                                 'Similar valid options are %s\n'
622                                 'Valid options are %s' %
623                                (key, str(list(cma_default_options(key))),
624                                 str(list(cma_default_options))))
625            if correct_key in validated_keys:
626                if key == correct_key:
627                    key = original_keys[validated_keys.index(key)]
628                raise ValueError("%s was not a unique key for %s option"
629                    % (key, correct_key))
630            validated_keys.append(correct_key)
631            original_keys.append(key)
632        return options
633    def check_attributes(self, opts=None):
634        """check for attributes and moves them into the dictionary"""
635        if opts is None:
636            opts = self
637        if 11 < 3:
638            if hasattr(opts, '__dict__'):
639                for key in opts.__dict__:
640                    if key not in self._attributes:
641                        raise ValueError("""
642                        Assign options with ``opts['%s']``
643                        instead of ``opts.%s``
644                        """ % (opts.__dict__.keys()[0],
645                               opts.__dict__.keys()[0]))
646            return self
647        else:
648        # the problem with merge is that ``opts['ftarget'] = new_value``
649        # would be overwritten by the old ``opts.ftarget``.
650        # The solution here is to empty opts.__dict__ after the merge
651            if hasattr(opts, '__dict__'):
652                for key in list(opts.__dict__):
653                    if key in self._attributes:
654                        continue
655                    utils.print_warning(
656                        """
657        An option attribute has been merged into the dictionary,
658        thereby possibly overwriting the dictionary value, and the
659        attribute has been removed. Assign options with
660
661            ``opts['%s'] = value``  # dictionary assignment
662
663        or use
664
665            ``opts.set('%s', value)  # here isinstance(opts, CMAOptions)
666
667        instead of
668
669            ``opts.%s = value``  # attribute assignment
670                        """ % (key, key, key), 'check', 'CMAOptions')
671
672                    opts[key] = opts.__dict__[key]  # getattr(opts, key)
673                    delattr(opts, key)  # is that cosher?
674                    # delattr is necessary to prevent that the attribute
675                    # overwrites the dict entry later again
676            return opts
677
678    def __init__(self, s=None, **kwargs):
679        """return an `CMAOptions` instance.
680
681        Return default options if ``s is None and not kwargs``,
682        or all options whose name or description contains `s`, if
683        `s` is a (search) string (case is disregarded in the match),
684        or with entries from dictionary `s` as options,
685        or with kwargs as options if ``s is None``,
686        in any of the latter cases not complemented with default options
687        or settings.
688
689        Returns: see above.
690
691        Details: as several options start with ``'s'``, ``s=value`` is
692        not valid as an option setting.
693
694        """
695        # if not CMAOptions.defaults:  # this is different from self.defaults!!!
696        #     CMAOptions.defaults = fmin([],[])
697        if s is None and not kwargs:
698            super(CMAOptions, self).__init__(CMAOptions.defaults())  # dict.__init__(self, CMAOptions.defaults()) should be the same
699            # self = CMAOptions.defaults()
700            s = 'nocheck'
701        elif utils.is_str(s) and not s.startswith('unchecked'):
702            super(CMAOptions, self).__init__(CMAOptions().match(s))
703            # we could return here
704            s = 'nocheck'
705        elif isinstance(s, dict):
706            if kwargs:
707                raise ValueError('Dictionary argument must be the only argument')
708            super(CMAOptions, self).__init__(s)
709        elif kwargs and (s is None or s.startswith('unchecked')):
710            super(CMAOptions, self).__init__(kwargs)
711        else:
712            raise ValueError('The first argument must be a string or a dict or a keyword argument or `None`')
713        if not utils.is_str(s) or not s.startswith(('unchecked', 'nocheck')):
714            # was main offender
715            self.check()  # caveat: infinite recursion
716            for key in list(self.keys()):
717                correct_key = self.corrected_key(key)
718                if correct_key not in CMAOptions.defaults():
719                    utils.print_warning('invalid key ``' + str(key) +
720                                   '`` removed', '__init__', 'CMAOptions')
721                    self.pop(key)
722                elif key != correct_key:
723                    self[correct_key] = self.pop(key)
724        # self.evaluated = False  # would become an option entry
725        self._lock_setting = False
726        self._attributes = self.__dict__.copy()  # are not valid keys
727        self._attributes['_attributes'] = len(self._attributes)
728
729    def init(self, dict_or_str, val=None, warn=True):
730        """initialize one or several options.
731
732        Arguments
733        ---------
734            `dict_or_str`
735                a dictionary if ``val is None``, otherwise a key.
736                If `val` is provided `dict_or_str` must be a valid key.
737            `val`
738                value for key
739
740        Details
741        -------
742        Only known keys are accepted. Known keys are in `CMAOptions.defaults()`
743
744        """
745        # dic = dict_or_key if val is None else {dict_or_key:val}
746        self.check(dict_or_str)
747        dic = dict_or_str
748        if val is not None:
749            dic = {dict_or_str:val}
750
751        for key, val in dic.items():
752            key = self.corrected_key(key)
753            if key not in CMAOptions.defaults():
754                # TODO: find a better solution?
755                if warn:
756                    print('Warning in cma.CMAOptions.init(): key ' +
757                        str(key) + ' ignored')
758            else:
759                self[key] = val
760
761        return self
762
763    def set(self, dic, val=None, force=False):
764        """assign versatile options.
765
766        Method `CMAOptions.versatile_options` () gives the versatile
767        options, use `init()` to set the others.
768
769        Arguments
770        ---------
771            `dic`
772                either a dictionary or a key. In the latter
773                case, `val` must be provided
774            `val`
775                value for `key`, approximate match is sufficient
776            `force`
777                force setting of non-versatile options, use with caution
778
779        This method will be most probably used with the ``opts`` attribute of
780        a `CMAEvolutionStrategy` instance.
781
782        """
783        if val is not None:  # dic is a key in this case
784            dic = {dic:val}  # compose a dictionary
785        for key_original, val in list(dict(dic).items()):
786            key = self.corrected_key(key_original)
787            if (not self._lock_setting or
788                key in CMAOptions.versatile_options() or
789                force):
790                self[key] = val
791            else:
792                utils.print_warning('key ' + str(key_original) +
793                      ' ignored (not recognized as versatile)',
794                               'set', 'CMAOptions')
795        return self  # to allow o = CMAOptions(o).set(new)
796
797    def complement(self):
798        """add all missing options with their default values"""
799
800        # add meta-parameters, given options have priority
801        self.check()
802        for key in CMAOptions.defaults():
803            if key not in self:
804                self[key] = CMAOptions.defaults()[key]
805        return self
806
807    @property
808    def settable(self):
809        """return the subset of those options that are settable at any
810        time.
811
812        Settable options are in `versatile_options` (), but the
813        list might be incomplete.
814
815        """
816        return CMAOptions(dict(i for i in list(self.items())
817                                if i[0] in CMAOptions.versatile_options()))
818
819    def __call__(self, key, default=None, loc=None):
820        """evaluate and return the value of option `key` on the fly, or
821        return those options whose name or description contains `key`,
822        case disregarded.
823
824        Details
825        -------
826        Keys that contain `filename` are not evaluated.
827        For ``loc==None``, `self` is used as environment
828        but this does not define ``N``.
829
830        :See: `eval()`, `evalall()`
831
832        """
833        try:
834            val = self[key]
835        except:
836            return self.match(key)
837
838        if loc is None:
839            loc = self  # TODO: this hack is not so useful: popsize could be there, but N is missing
840        try:
841            if utils.is_str(val):
842                val = val.split('#')[0].strip()  # remove comments
843                if key.find('filename') < 0:
844                        # and key.find('mindx') < 0:
845                    val = eval(val, globals(), loc)
846            # invoke default
847            # TODO: val in ... fails with array type, because it is applied element wise!
848            # elif val in (None,(),[],{}) and default is not None:
849            elif val is None and default is not None:
850                val = eval(str(default), globals(), loc)
851        except:
852            pass  # slighly optimistic: the previous is bug-free
853        return val
854
855    def corrected_key(self, key):
856        """return the matching valid key, if ``key.lower()`` is a unique
857        starting sequence to identify the valid key, ``else None``
858
859        """
860        matching_keys = []
861        key = key.lower()  # this was somewhat slow, so it is speed optimized now
862        if key in cma_allowed_options_keys:
863            return cma_allowed_options_keys[key]
864        for allowed_key in cma_allowed_options_keys:
865            if allowed_key.startswith(key):
866                if len(matching_keys) > 0:
867                    return None
868                matching_keys.append(allowed_key)
869        return matching_keys[0] if len(matching_keys) == 1 else None
870
871    def eval(self, key, default=None, loc=None, correct_key=True):
872        """Evaluates and sets the specified option value in
873        environment `loc`. Many options need ``N`` to be defined in
874        `loc`, some need `popsize`.
875
876        Details
877        -------
878        Keys that contain 'filename' are not evaluated.
879        For `loc` is None, the self-dict is used as environment
880
881        :See: `evalall()`, `__call__`
882
883        """
884        # TODO: try: loc['dim'] = loc['N'] etc
885        if correct_key:
886            # in_key = key  # for debugging only
887            key = self.corrected_key(key)
888        self[key] = self(key, default, loc)
889        return self[key]
890
891    def evalall(self, loc=None, defaults=None):
892        """Evaluates all option values in environment `loc`.
893
894        :See: `eval()`
895
896        """
897        self.check()
898        if defaults is None:
899            defaults = cma_default_options_()
900        # TODO: this needs rather the parameter N instead of loc
901        if 'N' in loc:  # TODO: __init__ of CMA can be simplified
902            popsize = self('popsize', defaults['popsize'], loc)
903            for k in list(self.keys()):
904                k = self.corrected_key(k)
905                self.eval(k, defaults[k],
906                          {'N':loc['N'], 'popsize':popsize})
907        self._lock_setting = True
908        return self
909
910    def match(self, s=''):
911        """return all options that match, in the name or the description,
912        with string `s`, case is disregarded.
913
914        Example: ``cma.CMAOptions().match('verb')`` returns the verbosity
915        options.
916
917        """
918        match = s.lower()
919        res = {}
920        for k in sorted(self):
921            s = str(k) + '=\'' + str(self[k]) + '\''
922            if match in s.lower():
923                res[k] = self[k]
924        return CMAOptions(res)
925
926    @property
927    def to_namedtuple(self):
928        """return options as const attributes of the returned object,
929        only useful for inspection. """
930        raise NotImplementedError
931        # return collections.namedtuple('CMAOptionsNamedTuple',
932        #                               self.keys())(**self)
933
934    def from_namedtuple(self, t):
935        """update options from a `collections.namedtuple`.
936        :See also: `to_namedtuple`
937        """
938        return self.update(t._asdict())
939
940    def pprint(self, linebreak=80):
941        for i in sorted(self.items()):
942            s = str(i[0]) + "='" + str(i[1]) + "'"
943            a = s.split(' ')
944
945            # print s in chunks
946            l = ''  # start entire to the left
947            while a:
948                while a and len(l) + len(a[0]) < linebreak:
949                    l += ' ' + a.pop(0)
950                print(l)
951                l = '        '  # tab for subsequent lines
952
953class CMAEvolutionStrategyResult(collections.namedtuple(
954    'CMAEvolutionStrategyResult', [
955        'xbest',
956        'fbest',
957        'evals_best',
958        'evaluations',
959        'iterations',
960        'xfavorite',
961        'stds',
962        'stop',
963    ])):
964    """A results tuple from `CMAEvolutionStrategy` property ``result``.
965
966    This tuple contains in the given position and as attribute
967
968    - 0 ``xbest`` best solution evaluated
969    - 1 ``fbest`` objective function value of best solution
970    - 2 ``evals_best`` evaluation count when ``xbest`` was evaluated
971    - 3 ``evaluations`` evaluations overall done
972    - 4 ``iterations``
973    - 5 ``xfavorite`` distribution mean in "phenotype" space, to be
974      considered as current best estimate of the optimum
975    - 6 ``stds`` effective standard deviations, can be used to
976      compute a lower bound on the expected coordinate-wise distance
977      to the true optimum, which is (very) approximately stds[i] *
978      dimension**0.5 / min(mueff, dimension) / 1.5 / 5 ~ std_i *
979      dimension**0.5 / min(popsize / 2, dimension) / 5, where
980      dimension = CMAEvolutionStrategy.N and mueff =
981      CMAEvolutionStrategy.sp.weights.mueff ~ 0.3 * popsize.
982    - 7 ``stop`` termination conditions in a dictionary
983
984    The penalized best solution of the last completed iteration can be
985    accessed via attribute ``pop_sorted[0]`` of `CMAEvolutionStrategy`
986    and the respective objective function value via ``fit.fit[0]``.
987
988    Details:
989
990    - This class is of purely declarative nature and for providing
991      this docstring. It does not provide any further functionality.
992    - ``list(fit.fit).find(0)`` is the index of the first sampled
993      solution of the last completed iteration in ``pop_sorted``.
994
995    """
996
997cma_default_options = CMAOptions(cma_default_options_())
998
999class _CMAEvolutionStrategyResult(tuple):
1000    """A results tuple from `CMAEvolutionStrategy` property ``result``.
1001
1002    This tuple contains in the given position
1003
1004    - 0 best solution evaluated, ``xbest``
1005    - 1 objective function value of best solution, ``f(xbest)``
1006    - 2 evaluation count when ``xbest`` was evaluated
1007    - 3 evaluations overall done
1008    - 4 iterations
1009    - 5 distribution mean in "phenotype" space, to be considered as
1010      current best estimate of the optimum
1011    - 6 effective standard deviations, give a lower bound on the expected
1012      coordinate-wise distance to the true optimum of (very) approximately
1013      std_i * dimension**0.5 / min(mueff, dimension) / 1.2 / 5
1014      ~ std_i * dimension**0.5 / min(popsize / 0.4, dimension) / 5, where
1015      mueff = CMAEvolutionStrategy.sp.weights.mueff ~ 0.3 * popsize.
1016
1017    The penalized best solution of the last completed iteration can be
1018    accessed via attribute ``pop_sorted[0]`` of `CMAEvolutionStrategy`
1019    and the respective objective function value via ``fit.fit[0]``.
1020
1021    Details:
1022
1023    - This class is of purely declarative nature and for providing this
1024      docstring. It does not provide any further functionality.
1025    - ``list(fit.fit).find(0)`` is the index of the first sampled solution
1026      of the last completed iteration in ``pop_sorted``.
1027
1028"""  # here starts the code: (beating the code folding glitch)
1029    # remark: a tuple is immutable, hence we cannot change it anymore
1030    # in __init__. This would work if we inherited from a `list`.
1031    @staticmethod
1032    def _generate(self):
1033        """return a results tuple of type `CMAEvolutionStrategyResult`.
1034
1035        `_generate` is a surrogate for the ``__init__`` method, which
1036        cannot be used to initialize the immutable `tuple` super class.
1037        """
1038        return _CMAEvolutionStrategyResult(
1039            self.best.get() + (  # (x, f, evals) triple
1040            self.countevals,
1041            self.countiter,
1042            self.gp.pheno(self.mean, into_bounds=self.boundary_handler.repair),
1043            self.gp.scales * self.sigma * self.sigma_vec.scaling *
1044                self.dC**0.5))
1045
1046class CMAEvolutionStrategy(interfaces.OOOptimizer):
1047    """CMA-ES stochastic optimizer class with ask-and-tell interface.
1048
1049    Calling Sequences
1050    =================
1051
1052    - ``es = CMAEvolutionStrategy(x0, sigma0)``
1053
1054    - ``es = CMAEvolutionStrategy(x0, sigma0, opts)``
1055
1056    - ``es = CMAEvolutionStrategy(x0, sigma0).optimize(objective_fct)``
1057
1058    - ::
1059
1060        res = CMAEvolutionStrategy(x0, sigma0,
1061                                opts).optimize(objective_fct).result
1062
1063    Arguments
1064    =========
1065    `x0`
1066        initial solution, starting point. `x0` is given as "phenotype"
1067        which means, if::
1068
1069            opts = {'transformation': [transform, inverse]}
1070
1071        is given and ``inverse is None``, the initial mean is not
1072        consistent with `x0` in that ``transform(mean)`` does not
1073        equal to `x0` unless ``transform(mean)`` equals ``mean``.
1074    `sigma0`
1075        initial standard deviation.  The problem variables should
1076        have been scaled, such that a single standard deviation
1077        on all variables is useful and the optimum is expected to
1078        lie within about `x0` +- ``3*sigma0``. Often one wants to
1079        check for solutions close to the initial point. This allows,
1080        for example, for an easier check of consistency of the
1081        objective function and its interfacing with the optimizer.
1082        In this case, a much smaller `sigma0` is advisable.
1083    `opts`
1084        options, a dictionary with optional settings,
1085        see class `CMAOptions`.
1086
1087    Main interface / usage
1088    ======================
1089    The interface is inherited from the generic `OOOptimizer`
1090    class (see also there). An object instance is generated from::
1091
1092        es = cma.CMAEvolutionStrategy(8 * [0.5], 0.2)
1093
1094    The least verbose interface is via the optimize method::
1095
1096        es.optimize(objective_func)
1097        res = es.result
1098
1099    More verbosely, the optimization is done using the
1100    methods `stop`, `ask`, and `tell`::
1101
1102        while not es.stop():
1103            solutions = es.ask()
1104            es.tell(solutions, [cma.ff.rosen(s) for s in solutions])
1105            es.disp()
1106        es.result_pretty()
1107
1108
1109    where `ask` delivers new candidate solutions and `tell` updates
1110    the `optim` instance by passing the respective function values
1111    (the objective function `cma.ff.rosen` can be replaced by any
1112    properly defined objective function, see `cma.ff` for more
1113    examples).
1114
1115    To change an option, for example a termination condition to
1116    continue the optimization, call::
1117
1118        es.opts.set({'tolfacupx': 1e4})
1119
1120    The class `CMAEvolutionStrategy` also provides::
1121
1122        (solutions, func_values) = es.ask_and_eval(objective_func)
1123
1124    and an entire optimization can also be written like::
1125
1126        while not es.stop():
1127            es.tell(*es.ask_and_eval(objective_func))
1128
1129    Besides for termination criteria, in CMA-ES only the ranks of the
1130    `func_values` are relevant.
1131
1132    Attributes and Properties
1133    =========================
1134    - `inputargs`: passed input arguments
1135    - `inopts`: passed options
1136    - `opts`: actually used options, some of them can be changed any
1137      time via ``opts.set``, see class `CMAOptions`
1138    - `popsize`: population size lambda, number of candidate
1139      solutions returned by `ask` ()
1140    - `logger`: a `CMADataLogger` instance utilized by `optimize`
1141
1142    Examples
1143    ========
1144    Super-short example, with output shown:
1145
1146    >>> import cma
1147    >>> # construct an object instance in 4-D, sigma0=1:
1148    >>> es = cma.CMAEvolutionStrategy(4 * [1], 1, {'seed':234})
1149    ...      # doctest: +ELLIPSIS
1150    (4_w,8)-aCMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=234...)
1151
1152    and optimize the ellipsoid function
1153
1154    >>> es.optimize(cma.ff.elli, verb_disp=1)  # doctest: +ELLIPSIS
1155    Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]
1156        1      8 2.09...
1157    >>> assert len(es.result) == 8
1158    >>> assert es.result[1] < 1e-9
1159
1160    The optimization loop can also be written explicitly:
1161
1162    >>> es = cma.CMAEvolutionStrategy(4 * [1], 1)  # doctest: +ELLIPSIS
1163    (4_w,8)-aCMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=...
1164    >>> while not es.stop():
1165    ...    X = es.ask()
1166    ...    es.tell(X, [cma.ff.elli(x) for x in X])
1167    ...    es.disp()  # doctest: +ELLIPSIS
1168    Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]
1169        1      8 ...
1170
1171    achieving the same result as above.
1172
1173    An example with lower bounds (at zero) and handling infeasible
1174    solutions:
1175
1176    >>> import numpy as np
1177    >>> es = cma.CMAEvolutionStrategy(10 * [0.2], 0.5,
1178    ...         {'bounds': [0, np.inf]})  #doctest: +ELLIPSIS
1179    (5_w,...
1180    >>> while not es.stop():
1181    ...     fit, X = [], []
1182    ...     while len(X) < es.popsize:
1183    ...         curr_fit = None
1184    ...         while curr_fit in (None, np.NaN):
1185    ...             x = es.ask(1)[0]
1186    ...             curr_fit = cma.ff.somenan(x, cma.ff.elli) # might return np.NaN
1187    ...         X.append(x)
1188    ...         fit.append(curr_fit)
1189    ...     es.tell(X, fit)
1190    ...     es.logger.add()
1191    ...     es.disp()  #doctest: +ELLIPSIS
1192    Itera...
1193    >>>
1194    >>> assert es.result[1] < 1e-9
1195    >>> assert es.result[2] < 9000  # by internal termination
1196    >>> # es.logger.plot()  # will plot data
1197    >>> # cma.s.figshow()  # display plot window
1198
1199    An example with user-defined transformation, in this case to realize
1200    a lower bound of 2.
1201
1202    >>> import warnings
1203    >>> with warnings.catch_warnings(record=True) as warns:
1204    ...     es = cma.CMAEvolutionStrategy(5 * [3], 0.1,
1205    ...                 {"transformation": [lambda x: x**2+1.2, None],
1206    ...                  "verbose": -2,})
1207    >>> warns[0].message  # doctest:+ELLIPSIS
1208    UserWarning('in class GenoPheno: user defined transformations have not been tested thoroughly ()'...
1209    >>> warns[1].message  # doctest:+ELLIPSIS
1210    UserWarning('computed initial point...
1211    >>> es.optimize(cma.ff.rosen, verb_disp=0)  #doctest: +ELLIPSIS
1212    <cma...
1213    >>> assert cma.ff.rosen(es.result[0]) < 1e-7 + 5.54781521192
1214    >>> assert es.result[2] < 3300
1215
1216    The inverse transformation is (only) necessary if the `BoundPenalty`
1217    boundary handler is used at the same time.
1218
1219    The `CMAEvolutionStrategy` class also provides a default logger
1220    (cave: files are overwritten when the logger is used with the same
1221    filename prefix):
1222
1223    >>> es = cma.CMAEvolutionStrategy(4 * [0.2], 0.5, {'verb_disp': 0})
1224    >>> es.logger.disp_header()  # annotate the print of disp
1225    Iterat Nfevals  function value    axis ratio maxstd  minstd
1226    >>> while not es.stop():
1227    ...     X = es.ask()
1228    ...     es.tell(X, [cma.ff.sphere(x) for x in X])
1229    ...     es.logger.add()  # log current iteration
1230    ...     es.logger.disp([-1])  # display info for last iteration   #doctest: +ELLIPSIS
1231        1  ...
1232    >>> es.logger.disp_header()
1233    Iterat Nfevals  function value    axis ratio maxstd  minstd
1234    >>> # es.logger.plot() # will make a plot
1235
1236    Example implementing restarts with increasing popsize (IPOP):
1237
1238    >>> bestever = cma.optimization_tools.BestSolution()
1239    >>> for lam in 10 * 2**np.arange(8):  # 10, 20, 40, 80, ..., 10 * 2**7
1240    ...     es = cma.CMAEvolutionStrategy(6 - 8 * np.random.rand(4),  # 4-D
1241    ...                                   5,  # initial std sigma0
1242    ...                                   {'popsize': lam,  # options
1243    ...                                    'verb_append': bestever.evalsall})
1244    ...     # logger = cma.CMADataLogger().register(es, append=bestever.evalsall)
1245    ...     while not es.stop():
1246    ...         X = es.ask()    # get list of new solutions
1247    ...         fit = [cma.ff.rastrigin(x) for x in X]  # evaluate each solution
1248    ...         es.tell(X, fit) # besides for termination only the ranking in fit is used
1249    ...
1250    ...         # display some output
1251    ...         # logger.add()  # add a "data point" to the log, writing in files
1252    ...         es.disp()  # uses option verb_disp with default 100
1253    ...
1254    ...     print('termination:', es.stop())
1255    ...     cma.s.pprint(es.best.__dict__)
1256    ...
1257    ...     bestever.update(es.best)
1258    ...
1259    ...     # show a plot
1260    ...     # logger.plot();
1261    ...     if bestever.f < 1e-8:  # global optimum was hit
1262    ...         break  #doctest: +ELLIPSIS
1263    (5_w,...
1264    >>> assert es.result[1] < 1e-8
1265
1266    On the Rastrigin function, usually after five restarts the global
1267    optimum is located.
1268
1269    Using the `multiprocessing` module, we can evaluate the function in
1270    parallel with a simple modification of the example (however
1271    multiprocessing seems not always reliable):
1272
1273    >>> from cma.fitness_functions import elli  # cannot be an instance method
1274    >>> from cma.optimization_tools import EvalParallel2
1275    >>> es = cma.CMAEvolutionStrategy(22 * [0.0], 1.0, {'maxiter':10})  # doctest:+ELLIPSIS
1276    (6_w,13)-aCMA-ES (mu_w=...
1277    >>> with EvalParallel2(elli, es.popsize + 1) as eval_all:
1278    ...     while not es.stop():
1279    ...         X = es.ask()
1280    ...         es.tell(X, eval_all(X))
1281    ...         es.disp()
1282    ...         # es.logger.add()  # doctest:+ELLIPSIS
1283    Iterat...
1284
1285    The final example shows how to resume:
1286
1287    >>> import pickle
1288    >>>
1289    >>> es0 = cma.CMAEvolutionStrategy(12 * [0.1],  # a new instance, 12-D
1290    ...                                0.12)         # initial std sigma0
1291    ...   #doctest: +ELLIPSIS
1292    (5_w,...
1293    >>> es0.optimize(cma.ff.rosen, iterations=100)  #doctest: +ELLIPSIS
1294    I...
1295    >>> s = es0.pickle_dumps()  # return pickle.dumps(es) with safeguards
1296    >>> # save string s to file like open(filename, 'wb').write(s)
1297    >>> del es0  # let's start fresh
1298    >>> # s = open(filename, 'rb').read()  # load string s from file
1299    >>> es = pickle.loads(s)  # read back es instance from string
1300    >>> # resuming
1301    >>> es.optimize(cma.ff.rosen, verb_disp=200)  #doctest: +ELLIPSIS
1302      200 ...
1303    >>> assert es.result[2] < 15000
1304    >>> assert cma.s.Mh.vequals_approximately(es.result[0], 12 * [1], 1e-5)
1305    >>> assert len(es.result) == 8
1306
1307    Details
1308    =======
1309    The following two enhancements are implemented, the latter is only
1310    turned on by default for very small population sizes.
1311
1312    *Active CMA* is implemented with option ``CMA_active`` and
1313    conducts an update of the covariance matrix with negative weights.
1314    The negative update is implemented, such that positive definiteness
1315    is guarantied. A typical speed up factor (number of f-evaluations)
1316    is between 1.1 and two.
1317
1318    References: Jastrebski and Arnold, Improving evolution strategies
1319    through active covariance matrix adaptation, CEC 2006.
1320    Hansen, The CMA evolution strategy: a tutorial, arXiv 2016.
1321
1322    *Selective mirroring* is implemented with option ``CMA_mirrors``
1323    in the method `get_mirror` and `get_selective_mirrors`.
1324    The method `ask_and_eval` (used by `fmin`) will then sample
1325    selectively mirrored vectors within the iteration
1326    (``CMA_mirrormethod==1``). Otherwise, or if ``CMA_mirromethod==2``,
1327    selective mirrors are injected for the next iteration.
1328    In selective mirroring, only the worst solutions are mirrored. With
1329    the default small number of mirrors, *pairwise selection* (where at
1330    most one of the two mirrors contribute to the update of the
1331    distribution mean) is implicitly guarantied under selective
1332    mirroring and therefore not explicitly implemented.
1333
1334    Update: pairwise selection for injected mirrors is also applied in the
1335    covariance matrix update: for all injected solutions, as for those from
1336    TPA, this is now implemented in that the recombination weights are
1337    constrained to be nonnegative for injected solutions in the covariance
1338    matrix (otherwise recombination weights are anyway nonnegative). This
1339    is a precaution to prevent failure when injected solutions are
1340    systematically bad (see e.g. https://github.com/CMA-ES/pycma/issues/124),
1341    but may not be "optimal" for mirrors.
1342
1343    References: Brockhoff et al, PPSN 2010, Auger et al, GECCO 2011.
1344
1345    :See also: `fmin` (), `OOOptimizer`, `CMAOptions`, `plot` (), `ask` (),
1346        `tell` (), `ask_and_eval` ()
1347
1348"""  # here starts the code: (beating the code folding glitch)
1349    @property  # read only attribute decorator for a method
1350    def popsize(self):
1351        """number of samples by default returned by `ask` ()
1352        """
1353        return self.sp.popsize
1354
1355    # this is not compatible with python2.5:
1356    #     @popsize.setter
1357    #     def popsize(self, p):
1358    #         """popsize cannot be set (this might change in future)
1359    #         """
1360    #         raise RuntimeError("popsize cannot be changed")
1361
1362    def stop(self, check=True, ignore_list=(), check_in_same_iteration=False,
1363             get_value=None):
1364        """return the termination status as dictionary.
1365
1366        With ``check == False``, the termination conditions are not checked
1367        and the status might not reflect the current situation.
1368        ``check_on_same_iteration == False`` (new) does not re-check during
1369        the same iteration. When termination options are manually changed,
1370        it must be set to `True` to advance afterwards.
1371        ``stop().clear()`` removes the currently active termination
1372        conditions.
1373
1374        As a convenience feature, keywords in `ignore_list` are removed from
1375        the conditions.
1376
1377        If `get_value` is set to a condition name (not the empty string),
1378        `stop` does not update the termination dictionary but returns the
1379        measured value that would be compared to the threshold. This only
1380        works for some conditions, like 'tolx'. If the condition name is
1381        not known or cannot be computed, `None` is returned and no warning
1382        is issued.
1383
1384        Testing `get_value` functionality:
1385
1386        >>> import cma
1387        >>> es = cma.CMAEvolutionStrategy(2 * [1], 1e4, {'verbose': -9})
1388        >>> with warnings.catch_warnings(record=True) as w:
1389        ...     es.stop(get_value='tolx')  # triggers zero iteration warning
1390        ...     assert len(w) == 1, [str(wi) for wi in w]
1391        >>> es = es.optimize(cma.ff.sphere, iterations=4)
1392        >>> assert 1e3 < es.stop(get_value='tolx') < 1e4, es.stop(get_value='tolx')
1393        >>> assert es.stop() == {}
1394        >>> assert es.stop(get_value='catch 22') is None
1395
1396    """
1397        if (check and self.countiter > 0 and self.opts['termination_callback'] and
1398                self.opts['termination_callback'] != str(self.opts['termination_callback'])):
1399            self.callbackstop = utils.ListOfCallables(self.opts['termination_callback'])(self)
1400
1401        self._stopdict._get_value = get_value  # a hack to avoid passing arguments down to _add_stop and back
1402        # check_on_same_iteration == False makes como code much faster
1403        res = self._stopdict(self, check_in_same_iteration or get_value or (  # update the stopdict and return a Dict (self)
1404                                   check and self.countiter != self._stopdict.lastiter))
1405        if ignore_list:
1406            for key in ignore_list:
1407                res.pop(key, None)
1408        if get_value:  # deliver _value and reset
1409            res, self._stopdict._value = self._stopdict._value, None
1410        return res
1411
1412    def __init__(self, x0, sigma0, inopts=None):
1413        """see class `CMAEvolutionStrategy`
1414
1415        """
1416        self.inputargs = dict(locals())  # for the record
1417        del self.inputargs['self']  # otherwise the instance self has a cyclic reference
1418        if inopts is None:
1419            inopts = {}
1420        self.inopts = inopts
1421        opts = CMAOptions(inopts).complement()  # CMAOptions() == fmin([],[]) == defaultOptions()
1422        if opts.eval('verbose') is None:
1423            opts['verbose'] = CMAOptions()['verbose']
1424        utils.global_verbosity = global_verbosity = opts.eval('verbose')
1425        if global_verbosity < -8:
1426            opts['verb_disp'] = 0
1427            opts['verb_log'] = 0
1428            opts['verb_plot'] = 0
1429
1430        if 'noise_handling' in opts and opts.eval('noise_handling'):
1431            raise ValueError('noise_handling not available with class CMAEvolutionStrategy, use function fmin')
1432        if 'restarts' in opts and opts.eval('restarts'):
1433            raise ValueError('restarts not available with class CMAEvolutionStrategy, use function fmin')
1434
1435        self._set_x0(x0)  # manage weird shapes, set self.x0
1436        self.N_pheno = len(self.x0)
1437
1438        self.sigma0 = sigma0
1439        if utils.is_str(sigma0):
1440            raise ValueError("sigma0 must be a scalar, a string is no longer permitted")
1441            # self.sigma0 = eval(sigma0)  # like '1./N' or 'np.random.rand(1)[0]+1e-2'
1442        if np.size(self.sigma0) != 1 or np.shape(self.sigma0):
1443            raise ValueError('input argument sigma0 must be (or evaluate to) a scalar')
1444        self.sigma = self.sigma0  # goes to inialize
1445
1446        # extract/expand options
1447        N = self.N_pheno
1448        if utils.is_str(opts['fixed_variables']):
1449            opts['fixed_variables'] = ast.literal_eval(
1450                    opts['fixed_variables'])
1451        assert (isinstance(opts['fixed_variables'], dict)
1452            or opts['fixed_variables'] is None)
1453        if isinstance(opts['fixed_variables'], dict):
1454            N = self.N_pheno - len(opts['fixed_variables'])
1455        opts.evalall(locals())  # using only N
1456        if np.isinf(opts['CMA_diagonal']):
1457            opts['CMA_diagonal'] = True
1458        self.opts = opts
1459        self.randn = opts['randn']
1460        if not utils.is_nan(opts['seed']):
1461            if self.randn is np.random.randn:
1462                if not opts['seed'] or opts['seed'] is time:
1463                    np.random.seed()
1464                    six_decimals = (time.time() - 1e6 * (time.time() // 1e6))
1465                    opts['seed'] = int(1e5 * np.random.rand() + six_decimals
1466                                       + 1e5 * (time.time() % 1))
1467                np.random.seed(opts['seed'])  # a printable seed
1468            elif opts['seed'] not in (None, time):
1469                utils.print_warning("seed=%s will never be used (seed is only used if option 'randn' is np.random.randn)"
1470                                    % str(opts['seed']))
1471        self.gp = transformations.GenoPheno(self.N_pheno,
1472                        opts['scaling_of_variables'],
1473                        opts['typical_x'],
1474                                            opts['fixed_variables'],
1475                        opts['transformation'])
1476
1477        self.boundary_handler = opts['BoundaryHandler']
1478        if isinstance(self.boundary_handler, type):
1479            self.boundary_handler = self.boundary_handler(opts['bounds'])
1480        elif opts['bounds'] not in (None, False, [], [None, None]):
1481            utils.print_warning("""
1482                Option 'bounds' ignored because a BoundaryHandler *instance* was found.
1483                Consider to pass only the desired BoundaryHandler class. """,
1484                                CMAEvolutionStrategy.__init__)
1485        if not self.boundary_handler.has_bounds():
1486            self.boundary_handler = BoundNone()  # just a little faster and well defined
1487        elif not self.boundary_handler.is_in_bounds(self.x0):
1488            if opts['verbose'] >= 0:
1489                utils.print_warning("""
1490            Initial solution is out of the domain boundaries:
1491                x0   = %s
1492                ldom = %s
1493                udom = %s
1494            THIS MIGHT LEAD TO AN EXCEPTION RAISED LATER ON.
1495            """ % (str(self.gp.pheno(self.x0)),
1496                    str(self.boundary_handler.bounds[0]),
1497                    str(self.boundary_handler.bounds[1])),
1498                               '__init__', 'CMAEvolutionStrategy')
1499
1500        # set self.mean to geno(x0)
1501        tf_geno_backup = self.gp.tf_geno
1502        if self.gp.tf_pheno and self.gp.tf_geno is None:
1503            self.gp.tf_geno = lambda x: x  # a hack to avoid an exception
1504            utils.print_warning(
1505                "computed initial point may well be wrong, because no\n"
1506                "inverse for the user provided phenotype transformation "
1507                "was given")
1508        self.mean = self.gp.geno(np.array(self.x0, copy=True),
1509                            from_bounds=self.boundary_handler.inverse,
1510                            copy=False)
1511        self.mean0 = array(self.mean, copy=True)  # relevant for initial injection
1512        self.gp.tf_geno = tf_geno_backup
1513        # without copy_always interface:
1514        # self.mean = self.gp.geno(array(self.x0, copy=True), copy_if_changed=False)
1515        self.N = len(self.mean)
1516        assert N == self.N
1517        # self.fmean = np.NaN  # TODO name should change? prints nan in output files (OK with matlab&octave)
1518        # self.fmean_noise_free = 0.  # for output only
1519
1520        self.sp = _CMAParameters(N, opts, verbose=opts['verbose'] > 0)
1521        self.sp0 = self.sp  # looks useless, as it is not a copy
1522
1523        def instantiate_adapt_sigma(adapt_sigma, self):
1524            """return instantiated sigma adaptation object"""
1525            if adapt_sigma is None:
1526                utils.print_warning(
1527                    "Value `None` for option 'AdaptSigma' is ambiguous and\n"
1528                    "hence deprecated. AdaptSigma can be set to `True` or\n"
1529                    "`False` or a class or class instance which inherited from\n"
1530                    "`cma.sigma_adaptation.CMAAdaptSigmaBase`")
1531                adapt_sigma = CMAAdaptSigmaCSA
1532            elif adapt_sigma is True:
1533                if self.opts['CMA_diagonal'] is True and self.N > 299:
1534                    adapt_sigma = CMAAdaptSigmaTPA
1535                else:
1536                    adapt_sigma = CMAAdaptSigmaCSA
1537            elif adapt_sigma is False:
1538                adapt_sigma = CMAAdaptSigmaNone()
1539            if isinstance(adapt_sigma, type):  # is a class?
1540                # then we want the instance
1541                adapt_sigma = adapt_sigma(dimension=self.N, popsize=self.sp.popsize)
1542            return adapt_sigma
1543        self.adapt_sigma = instantiate_adapt_sigma(opts['AdaptSigma'], self)
1544
1545        self.mean_shift_samples = True if (isinstance(self.adapt_sigma, CMAAdaptSigmaTPA) or
1546            opts['mean_shift_line_samples']) else False
1547
1548        def eval_vector(in_, opts, N, default_value=1.0):
1549            """return `default_value` as scalar or `in_` after removing
1550            fixed variables if ``len(in_) == N``
1551            """
1552            res = default_value
1553            if in_ is not None:
1554                if np.size(in_) == 1:  # return scalar value
1555                    try:
1556                        res = float(in_[0])
1557                    except TypeError:
1558                        res = float(in_)
1559                elif opts['fixed_variables'] and np.size(in_) > N:
1560                    res = array([in_[i] for i in range(len(in_))
1561                                      if i not in opts['fixed_variables']],
1562                                dtype=float)
1563                    if len(res) != N:
1564                        utils.print_warning(
1565                            "resulting len %d != N = %d" % (len(res), N),
1566                            'eval_vector', iteration=self.countiter)
1567                else:
1568                    res = array(in_, dtype=float)
1569                if np.size(res) not in (1, N):
1570                    raise ValueError(
1571                        "CMA_stds option must have dimension %d "
1572                        "instead of %d" % (N, np.size(res)))
1573            return res
1574
1575        opts['minstd'] = eval_vector(opts['minstd'], opts, N, 0)
1576        opts['maxstd'] = eval_vector(opts['maxstd'], opts, N, np.inf)
1577
1578        # iiinteger handling, currently very basic:
1579        # CAVEAT: integer indices may give unexpected results if fixed_variables is used
1580        if len(opts['integer_variables']) and opts['fixed_variables']:
1581            utils.print_warning(
1582                "CAVEAT: fixed_variables change the meaning of "
1583                "integer_variables indices")
1584        # 1) prepare minstd to be a vector
1585        if (len(opts['integer_variables']) and
1586                np.isscalar(opts['minstd'])):
1587            opts['minstd'] = N * [opts['minstd']]
1588        # 2) set minstd to 1 / (2 Nint + 1),
1589        #    the setting 2 / (2 Nint + 1) already prevents convergence
1590        for i in opts['integer_variables']:
1591            if -N <= i < N:
1592                opts['minstd'][i] = max((opts['minstd'][i],
1593                    1 / (2 * len(opts['integer_variables']) + 1)))
1594            else:
1595                utils.print_warning(
1596                    """integer index %d not in range of dimension %d""" %
1597                        (i, N))
1598
1599        # initialization of state variables
1600        self.countiter = 0
1601        self.countevals = max((0, opts['verb_append'])) \
1602            if not isinstance(opts['verb_append'], bool) else 0
1603        self.pc = np.zeros(N)
1604        self.pc_neg = np.zeros(N)
1605        if 1 < 3:  # new version with class
1606            self.sigma_vec0 = eval_vector(self.opts['CMA_stds'], opts, N)
1607            self.sigma_vec = transformations.DiagonalDecoding(self.sigma_vec0)
1608            if np.isfinite(self.opts['CMA_dampsvec_fac']):
1609                self.sigma_vec *= np.ones(N)  # make sure to get a vector
1610        else:
1611            self.sigma_vec = eval_vector(self.opts['CMA_stds'], opts, N)
1612            if np.isfinite(self.opts['CMA_dampsvec_fac']):
1613                self.sigma_vec *= np.ones(N)  # make sure to get a vector
1614            self.sigma_vec0 = self.sigma_vec if np.isscalar(self.sigma_vec) \
1615                                            else self.sigma_vec.copy()
1616        stds = eval_vector(self.opts['CMA_teststds'], opts, N)
1617        if self.opts['CMA_diagonal']:  # is True or > 0
1618            # linear time and space complexity
1619            self.sigma_vec = transformations.DiagonalDecoding(stds * np.ones(N))
1620            self.sm = sampler.GaussStandardConstant(N, randn=self.opts['randn'])
1621            self._updateBDfromSM(self.sm)
1622            if self.opts['CMA_diagonal'] is True:
1623                self.sp.weights.finalize_negative_weights(N,
1624                                                      self.sp.c1_sep,
1625                                                      self.sp.cmu_sep,
1626                                                      pos_def=False)
1627            elif self.opts['CMA_diagonal'] == 1:
1628                raise ValueError("""Option 'CMA_diagonal' == 1 is disallowed.
1629                Use either `True` or an iteration number > 1 up to which C should be diagonal.
1630                Only `True` has linear memory demand.""")
1631            else:  # would ideally be done when switching
1632                self.sp.weights.finalize_negative_weights(N,
1633                                                      self.sp.c1,
1634                                                      self.sp.cmu)
1635        else:
1636            if 11 < 3:
1637                if hasattr(self.opts['vv'], '__getitem__') and \
1638                        'sweep_ccov' in self.opts['vv']:
1639                    self.opts['CMA_const_trace'] = True
1640            if self.opts['CMA_sampler'] is None:
1641                self.sm = sampler.GaussFullSampler(stds * np.ones(N),
1642                    lazy_update_gap=(
1643                        1. / (self.sp.c1 + self.sp.cmu + 1e-23) / self.N / 10
1644                        if self.opts['updatecovwait'] is None
1645                        else self.opts['updatecovwait']),
1646                    constant_trace=self.opts['CMA_const_trace'],
1647                    randn=self.opts['randn'],
1648                    eigenmethod=self.opts['CMA_eigenmethod'],
1649                    )
1650                p = self.sm.parameters(mueff=self.sp.weights.mueff,
1651                                       lam=self.sp.weights.lambda_)
1652                self.sp.weights.finalize_negative_weights(N, p['c1'], p['cmu'])
1653            elif isinstance(self.opts['CMA_sampler'], type):
1654                try:
1655                    self.sm = self.opts['CMA_sampler'](
1656                                stds * np.ones(N),
1657                                **self.opts['CMA_sampler_options'])
1658                except:
1659                    if max(stds) > min(stds):
1660                        utils.print_warning("different initial standard"
1661                            " deviations are not supported by the current"
1662                            " sampler and hence ignored")
1663                    elif stds[0] != 1:
1664                        utils.print_warning("""ignoring scaling factor %f
1665    for sample distribution""" % stds[0])
1666                    self.sm = self.opts['CMA_sampler'](N,
1667                                **self.opts['CMA_sampler_options'])
1668            else:  # CMA_sampler is already initialized as class instance
1669                self.sm = self.opts['CMA_sampler']
1670            if not isinstance(self.sm, interfaces.StatisticalModelSamplerWithZeroMeanBaseClass):
1671                utils.print_warning("""statistical model sampler did
1672    not evaluate to the expected type `%s` but to type `%s`. This is
1673    likely to lead to an exception later on. """ % (
1674                    str(type(interfaces.StatisticalModelSamplerWithZeroMeanBaseClass)),
1675                    str(type(self.sm))))
1676            self._updateBDfromSM(self.sm)
1677        self.dC = self.sm.variances
1678        self.D = self.dC**0.5  # we assume that the initial C is diagonal
1679        self.pop_injection_solutions = []
1680        self.pop_injection_directions = []
1681        self.number_of_solutions_asked = 0
1682        self.number_of_injections_delivered = 0  # used/delivered in asked
1683
1684        # self.gp.pheno adds fixed variables
1685        relative_stds = ((self.gp.pheno(self.mean + self.sigma * self.sigma_vec * self.D)
1686                          - self.gp.pheno(self.mean - self.sigma * self.sigma_vec * self.D)) / 2.0
1687                         / (self.boundary_handler.get_bounds('upper', self.N_pheno)
1688                            - self.boundary_handler.get_bounds('lower', self.N_pheno)))
1689        if np.any(relative_stds > 1):
1690            idx = np.nonzero(relative_stds > 1)[0]
1691            s = ("Initial standard deviation "
1692                 "%s larger than the bounded domain size in variable %s.\n"
1693                 "Consider using option 'CMA_stds', if the bounded "
1694                 "domain sizes differ significantly. "
1695                 % (("s (sigma0*stds) are", str(idx))
1696                    if len(idx) > 1 else (" (sigma0*stds) is",
1697                                          str(idx[0]))))
1698            raise ValueError(s)
1699        self._flgtelldone = True
1700        self.itereigenupdated = self.countiter
1701        self.count_eigen = 0
1702        self.noiseS = 0  # noise "signal"
1703        self.hsiglist = []
1704
1705        self.sent_solutions = _CMASolutionDict()
1706        self.archive = _CMASolutionDict()
1707        self._injected_solutions_archive = _SolutionDict()
1708        self.best = ot.BestSolution()
1709
1710        self.const = _BlancClass()
1711        self.const.chiN = N**0.5 * (1 - 1. / (4.*N) + 1. / (21.*N**2))  # expectation of norm(randn(N,1))
1712
1713        self.logger = CMADataLogger(opts['verb_filenameprefix'],
1714                                    modulo=opts['verb_log'],
1715                                    expensive_modulo=opts['verb_log_expensive']).register(self)
1716
1717        self._stopdict = _CMAStopDict()
1718        "    attribute for stopping criteria in function stop"
1719        self.callbackstop = ()
1720        "    return values of callbacks, used like ``if any(callbackstop)``"
1721        self.fit = _BlancClass()
1722        self.fit.fit = []  # not really necessary
1723        self.fit.hist = []  # short history of best
1724        self.fit.histbest = []  # long history of best
1725        self.fit.histmedian = []  # long history of median
1726        self.fit.median = None
1727        self.fit.median0 = None
1728        self.fit.median_min = np.inf
1729        self.fit.flatfit_iterations = 0
1730
1731        self.more_to_write = utils.MoreToWrite()  # [1, 1, 1, 1]  #  N*[1]  # needed when writing takes place before setting
1732
1733        # say hello
1734        if opts['verb_disp'] > 0 and opts['verbose'] >= 0:
1735            sweighted = '_w' if self.sp.weights.mu > 1 else ''
1736            smirr = 'mirr%d' % (self.sp.lam_mirr) if self.sp.lam_mirr else ''
1737            print('(%d' % (self.sp.weights.mu) + sweighted + ',%d' % (self.sp.popsize) + smirr +
1738                  ')-' + ('a' if opts['CMA_active'] else '') + 'CMA-ES' +
1739                  ' (mu_w=%2.1f,w_1=%d%%)' % (self.sp.weights.mueff, int(100 * self.sp.weights[0])) +
1740                  ' in dimension %d (seed=%s, %s)' % (N, str(opts['seed']), time.asctime()))  # + func.__name__
1741            if opts['CMA_diagonal'] and self.sp.CMA_on:
1742                s = ''
1743                if opts['CMA_diagonal'] is not True:
1744                    s = ' for '
1745                    if opts['CMA_diagonal'] < np.inf:
1746                        s += str(int(opts['CMA_diagonal']))
1747                    else:
1748                        s += str(np.floor(opts['CMA_diagonal']))
1749                    s += ' iterations'
1750                    s += ' (1/ccov=' + str(round(1. / (self.sp.c1 + self.sp.cmu))) + ')'
1751                print('   Covariance matrix is diagonal' + s)
1752
1753    def _set_x0(self, x0):
1754        """Assign `self.x0` from argument `x0`.
1755
1756        Input `x0` may be a `callable` or a `list` or `numpy.ndarray` of
1757        the desired length.
1758
1759        Below an artificial example is given, where calling `x0`
1760        delivers in the first two calls ``dimension * [5]`` and in
1761        succeeding calls``dimension * [0.01]``. Only the initial value of
1762        0.01 solves the Rastrigin function:
1763
1764        >>> import cma
1765        >>> class X0:
1766        ...     def __init__(self, dimension):
1767        ...         self.irun = 0
1768        ...         self.dimension = dimension
1769        ...     def __call__(self):
1770        ...         """"""
1771        ...         self.irun += 1
1772        ...         return (self.dimension * [5] if self.irun < 3
1773        ...                 else self.dimension * [0.01])
1774        >>> xopt, es = cma.fmin2(cma.ff.rastrigin, X0(3), 0.01,
1775        ...                      {'verbose':-9}, restarts=1)
1776        >>> assert es.result.fbest > 1e-5
1777        >>> xopt, es = cma.fmin2(cma.ff.rastrigin, X0(3), 0.01,
1778        ...                      {'verbose':-9}, restarts=2)
1779        >>> assert es.result.fbest < 1e-5  # third run succeeds due to x0
1780
1781        """
1782        try:
1783            x0 = x0()
1784        except TypeError:
1785            if utils.is_str(x0):
1786                raise ValueError("x0 may be a callable, but a string is no longer permitted")
1787                # x0 = eval(x0)
1788        self.x0 = array(x0, dtype=float, copy=True)  # should not have column or row, is just 1-D
1789        if self.x0.ndim == 2 and 1 in self.x0.shape:
1790            utils.print_warning('input x0 should be a list or 1-D array, trying to flatten ' +
1791                                str(self.x0.shape) + '-array')
1792            if self.x0.shape[0] == 1:
1793                self.x0 = self.x0[0]
1794            elif self.x0.shape[1] == 1:
1795                self.x0 = array([x[0] for x in self.x0])
1796        if self.x0.ndim != 1:
1797            raise ValueError('x0 must be 1-D array')
1798        if len(self.x0) <= 1:
1799            raise ValueError('Could not digest initial solution argument x0=%s.\n'
1800                             'Optimization in 1-D is not supported (code was never tested)'
1801                             % str(self.x0))
1802        try:
1803            self.x0.resize(self.x0.shape[0])  # 1-D array, not really necessary?!
1804        except NotImplementedError:
1805            pass
1806
1807    def _copy_light(self, sigma=None, inopts=None):
1808        """tentative copy of self, versatile (interface and functionalities may change).
1809
1810        `sigma` overwrites the original initial `sigma`.
1811        `inopts` allows to overwrite any of the original options.
1812
1813        This copy may not work as expected depending on the used sampler.
1814
1815        Copy mean and sample distribution parameters and input options. Do
1816        not copy evolution paths, termination status or other state variables.
1817
1818        >>> import cma
1819        >>> es = cma.CMAEvolutionStrategy(3 * [1], 0.1,
1820        ...          {'verbose':-9}).optimize(cma.ff.elli, iterations=10)
1821        >>> es2 = es._copy_light()
1822        >>> assert es2.sigma == es.sigma
1823        >>> assert sum((es.sm.C - es2.sm.C).flat < 1e-12)
1824        >>> es3 = es._copy_light(sigma=10)
1825        >>> assert es3.sigma == es3.sigma0 == 10
1826        >>> es4 = es._copy_light(inopts={'CMA_on': False})
1827        >>> assert es4.sp.c1 == es4.sp.cmu == 0
1828
1829        """
1830        if sigma is None:
1831            sigma = self.sigma
1832        opts = dict(self.inopts)
1833        if inopts is not None:
1834            opts.update(inopts)
1835        es = type(self)(self.mean[:], sigma, opts)
1836        es.sigma_vec = transformations.DiagonalDecoding(self.sigma_vec.scaling)
1837        try: es.sm.C = self.sm.C.copy()
1838        except: warnings.warn("self.sm.C.copy failed")
1839        es.sm.update_now(-1)  # make B and D consistent with C
1840        es._updateBDfromSM()
1841        return es
1842
1843    # ____________________________________________________________
1844    # ____________________________________________________________
1845    def ask(self, number=None, xmean=None, sigma_fac=1,
1846            gradf=None, args=(), **kwargs):
1847        """get/sample new candidate solutions.
1848
1849        Solutions are sampled from a multi-variate
1850        normal distribution and transformed to f-representation
1851        (phenotype) to be evaluated.
1852
1853        Arguments
1854        ---------
1855            `number`
1856                number of returned solutions, by default the
1857                population size ``popsize`` (AKA ``lambda``).
1858            `xmean`
1859                distribution mean, phenotyp?
1860            `sigma_fac`
1861                multiplier for internal sample width (standard
1862                deviation)
1863            `gradf`
1864                gradient, ``len(gradf(x)) == len(x)``, if
1865                ``gradf is not None`` the third solution in the
1866                returned list is "sampled" in supposedly Newton
1867                direction ``np.dot(C, gradf(xmean, *args))``.
1868            `args`
1869                additional arguments passed to gradf
1870
1871        Return
1872        ------
1873        A list of N-dimensional candidate solutions to be evaluated
1874
1875        Example
1876        -------
1877        >>> import cma
1878        >>> es = cma.CMAEvolutionStrategy([0,0,0,0], 0.3)  #doctest: +ELLIPSIS
1879        (4_w,...
1880        >>> while not es.stop() and es.best.f > 1e-6:
1881        ...     X = es.ask()  # get list of new solutions
1882        ...     fit = [cma.ff.rosen(x) for x in X]  # call fct with each solution
1883        ...     es.tell(X, fit)  # feed values
1884
1885        :See: `ask_and_eval`, `ask_geno`, `tell`
1886    """
1887        assert self.countiter >= 0
1888        if kwargs:
1889            utils.print_warning("""Optional argument%s \n\n  %s\n\nignored""" % (
1890                                    '(s)' if len(kwargs) > 1 else '', str(kwargs)),
1891                                "ask", "CMAEvolutionStrategy",
1892                                self.countiter, maxwarns=1)
1893        if self.countiter == 0:
1894            self.timer = utils.ElapsedWCTime()
1895        else:
1896            self.timer.tic
1897        pop_geno = self.ask_geno(number, xmean, sigma_fac)
1898
1899        # N,lambda=20,200: overall CPU 7s vs 5s == 40% overhead, even without bounds!
1900        #                  new data: 11.5s vs 9.5s == 20%
1901        # TODO: check here, whether this is necessary?
1902        # return [self.gp.pheno(x, copy=False, into_bounds=self.boundary_handler.repair) for x in pop]  # probably fine
1903        # return [Solution(self.gp.pheno(x, copy=False), copy=False) for x in pop]  # here comes the memory leak, now solved
1904        pop_pheno = [self.gp.pheno(x, copy=True,
1905                                into_bounds=self.boundary_handler.repair)
1906                     for x in pop_geno]
1907
1908        if gradf is not None:
1909            if not isinstance(self.sm, sampler.GaussFullSampler):
1910                utils.print_warning("Gradient injection may fail, because\n"
1911                                    "sampler attributes `B` and `D` are not present",
1912                                    "ask", "CMAEvolutionStrategy",
1913                                    self.countiter, maxwarns=1)
1914            try:
1915                # see Hansen (2011), Injecting external solutions into CMA-ES
1916                if not self.gp.islinear:
1917                    utils.print_warning("""
1918                    using the gradient (option ``gradf``) with a non-linear
1919                    coordinate-wise transformation (option ``transformation``)
1920                    has never been tested.""")
1921                    # TODO: check this out
1922                def grad_numerical_of_coordinate_map(x, map, epsilon=None):
1923                    """map is a coordinate-wise independent map, return
1924                    the estimated diagonal of the Jacobian.
1925                    """
1926                    eps = 1e-8 * (1 + abs(x)) if epsilon is None else epsilon
1927                    return (map(x + eps) - map(x - eps)) / (2 * eps)
1928                def grad_numerical_sym(x, func, epsilon=None):
1929                    """return symmetric numerical gradient of func : R^n -> R.
1930                    """
1931                    eps = 1e-8 * (1 + abs(x)) if epsilon is None else epsilon
1932                    grad = np.zeros(len(x))
1933                    ei = np.zeros(len(x))  # float is 1.6 times faster than int
1934                    for i in rglen(x):
1935                        ei[i] = eps[i]
1936                        grad[i] = (func(x + ei) - func(x - ei)) / (2*eps[i])
1937                        ei[i] = 0
1938                    return grad
1939                try:
1940                    if self.last_iteration_with_gradient == self.countiter:
1941                        utils.print_warning('gradient is used several times in ' +
1942                                'this iteration', iteration=self.countiter,
1943                                    verbose=self.opts['verbose'])
1944                    self.last_iteration_with_gradient = self.countiter
1945                except AttributeError:
1946                    pass
1947                index_for_gradient = min((2, len(pop_pheno)-1))
1948                if xmean is None:
1949                    xmean = self.mean
1950                xpheno = self.gp.pheno(xmean, copy=True,
1951                                    into_bounds=self.boundary_handler.repair)
1952                grad_at_mean = gradf(xpheno, *args)
1953                # lift gradient into geno-space
1954                if not self.gp.isidentity or (self.boundary_handler is not None
1955                        and self.boundary_handler.has_bounds()):
1956                    boundary_repair = None
1957                    gradpen = 0
1958                    if isinstance(self.boundary_handler, BoundTransform):
1959                        boundary_repair = self.boundary_handler.repair
1960                    elif isinstance(self.boundary_handler,
1961                                    BoundPenalty):
1962                        fpenalty = lambda x: self.boundary_handler.__call__(
1963                            x, _SolutionDict({tuple(x): {'geno': x}}), self.gp)
1964                        gradpen = grad_numerical_sym(
1965                            xmean, fpenalty)
1966                    elif self.boundary_handler is None or \
1967                            isinstance(self.boundary_handler,
1968                                       BoundNone):
1969                        pass
1970                    else:
1971                        raise NotImplementedError(
1972                            "unknown boundary handling method" +
1973                            str(self.boundary_handler) +
1974                            " when using gradf")
1975                    gradgp = grad_numerical_of_coordinate_map(
1976                        xmean,
1977                        lambda x: self.gp.pheno(x, copy=True,
1978                                into_bounds=boundary_repair))
1979                    grad_at_mean = grad_at_mean * gradgp + gradpen
1980
1981                # TODO: frozen variables brake the code (e.g. at grad of map)
1982                if len(grad_at_mean) != self.N or self.opts['fixed_variables']:
1983                    NotImplementedError("""
1984                    gradient with fixed variables is not (yet) implemented,
1985                    implement a simple transformation of the objective instead""")
1986                v = self.sm.D * np.dot(self.sm.B.T, self.sigma_vec * grad_at_mean)
1987                # newton_direction = sv * B * D * D * B^T * sv * gradient = sv * B * D * v
1988                # v = D^-1 * B^T * sv^-1 * newton_direction = D * B^T * sv * gradient
1989                q = sum(v**2)
1990                if q:
1991                    # Newton direction
1992                    pop_geno[index_for_gradient] = xmean - self.sigma \
1993                                * (self.N / q)**0.5 \
1994                                * (self.sigma_vec * np.dot(self.sm.B, self.sm.D * v))
1995                    if 11 < 3 and self.opts['vv']:
1996                        # gradient direction
1997                        q = sum((np.dot(self.sm.B.T, self.sigma_vec**-1 * grad_at_mean) / self.sm.D)**2)
1998                        pop_geno[index_for_gradient] = xmean - self.sigma \
1999                                        * (self.N / q)**0.5 * grad_at_mean \
2000                            if q else xmean
2001                else:
2002                    pop_geno[index_for_gradient] = xmean
2003                    utils.print_warning('gradient zero observed',
2004                                        iteration=self.countiter)
2005                # test "pure" gradient:
2006                # pop_geno[index_for_gradient] = -0.52 * grad_at_mean
2007                pop_pheno[index_for_gradient] = self.gp.pheno(
2008                    pop_geno[index_for_gradient], copy=True,
2009                    into_bounds=self.boundary_handler.repair)
2010                if 11 < 3:
2011                    print("x/m", pop_pheno[index_for_gradient] / self.mean)
2012                    print("  x-m=",
2013                          pop_pheno[index_for_gradient] - self.mean)
2014                    print("    g=", grad_at_mean)
2015                    print("      (x-m-g)/||g||=", (pop_pheno[index_for_gradient] - self.mean - grad_at_mean) / sum(grad_at_mean**2)**0.5
2016                          )
2017            except AttributeError:
2018                warnings.warn("Gradient injection failed presumably due\n"
2019                              "to missing attribute ``self.sm.B or self.sm.D``")
2020
2021        # insert solutions, this could also (better?) be done in self.gp.pheno
2022        for i in rglen((pop_geno)):
2023            self.sent_solutions.insert(pop_pheno[i], geno=pop_geno[i],
2024                                       iteration=self.countiter)
2025        ### iiinteger handling could come here
2026        return pop_pheno
2027
2028    # ____________________________________________________________
2029    # ____________________________________________________________
2030    def ask_geno(self, number=None, xmean=None, sigma_fac=1):
2031        """get new candidate solutions in genotyp.
2032
2033        Solutions are sampled from a multi-variate normal distribution.
2034
2035        Arguments are
2036            `number`
2037                number of returned solutions, by default the
2038                population size `popsize` (AKA lambda).
2039            `xmean`
2040                distribution mean
2041            `sigma_fac`
2042                multiplier for internal sample width (standard
2043                deviation)
2044
2045        `ask_geno` returns a list of N-dimensional candidate solutions
2046        in genotyp representation and is called by `ask`.
2047
2048        Details: updates the sample distribution if needed and might
2049        change the geno-pheno transformation during this update.
2050
2051        :See: `ask`, `ask_and_eval`
2052    """
2053        # TODO: return one line samples depending on a switch
2054        #       loosely akin to the mean_shift_samples part of
2055        #       _prepare_injection_directions
2056        if number is None or number < 1:
2057            number = self.sp.popsize
2058        if self.number_of_solutions_asked == 0:
2059            self.number_of_injections = (
2060                len(self.pop_injection_directions) +
2061                len(self.pop_injection_solutions))
2062
2063            # update distribution, might change self.mean
2064
2065        # if not self.opts['tolconditioncov'] or not np.isfinite(self.opts['tolconditioncov']):
2066        if self.opts['conditioncov_alleviate']:
2067            self.alleviate_conditioning_in_coordinates(self.opts['conditioncov_alleviate'][0])
2068            self.alleviate_conditioning(self.opts['conditioncov_alleviate'][-1])
2069
2070        xmean_arg = xmean
2071        if xmean is None:
2072            xmean = self.mean
2073        else:
2074            try:
2075                xmean = self.archive[xmean]['geno']
2076                # noise handling after call of tell
2077            except KeyError:
2078                try:
2079                    xmean = self.sent_solutions[xmean]['geno']
2080                    # noise handling before calling tell
2081                except KeyError:
2082                    pass
2083
2084        if 11 < 3:
2085            if self.opts['CMA_AII']:
2086                if self.countiter == 0:
2087                    # self.aii = AII(self.x0, self.sigma0)
2088                    pass
2089                self._flgtelldone = False
2090                pop = self.aii.ask(number)
2091                return pop
2092
2093        sigma = sigma_fac * self.sigma
2094
2095        # update parameters for sampling the distribution
2096        #        fac  0      1      10
2097        # 150-D cigar:
2098        #           50749  50464   50787
2099        # 200-D elli:               == 6.9
2100        #                  99900   101160
2101        #                 100995   103275 == 2% loss
2102        # 100-D elli:               == 6.9
2103        #                 363052   369325  < 2% loss
2104        #                 365075   365755
2105
2106        # sample distribution
2107        if self._flgtelldone:  # could be done in tell()!?
2108            self._flgtelldone = False
2109            self.ary = []
2110
2111        # check injections from pop_injection_directions
2112        arinj = []
2113        # a hack: do not use injection when only a single solution is asked for or a solution with a specific mean
2114        if number > 1 and (xmean_arg is None or Mh.vequals_approximately(xmean_arg, self.mean)):
2115            if self.countiter < 4 and \
2116                    len(self.pop_injection_directions) > self.popsize - 2:
2117                utils.print_warning('  %d special injected samples with popsize %d, '
2118                                    % (len(self.pop_injection_directions), self.popsize)
2119                                    + "popsize %d will be used" % (len(self.pop_injection_directions) + 2)
2120                                    + (" and the warning is suppressed in the following" if self.countiter == 3 else ""))
2121            # directions must come first because of mean_shift_samples/TPA
2122            while self.pop_injection_directions:
2123                if len(arinj) >= number:
2124                    break
2125                # TODO: if len(arinj) > number, ask doesn't fulfill the contract
2126                y = self.pop_injection_directions.pop(0)
2127                # sigma_vec _is_ taken into account here
2128                # this may be done again in tell
2129                if self.mahalanobis_norm(y) > self.N**0.5 * self.opts['CMA_injections_threshold_keep_len']:
2130                    nominator = self._random_rescaling_factor_to_mahalanobis_size(y)
2131                else:
2132                    nominator = 1
2133                y *= nominator / self.sigma
2134                arinj.append(y)
2135            while self.pop_injection_solutions:
2136                arinj.append((self.pop_injection_solutions.pop(0) - self.mean) / self.sigma)
2137            if self.mean_shift_samples and self.countiter > 1:
2138                # TPA is implemented by injection of the Delta mean
2139                if len(arinj) < 2:
2140                    raise RuntimeError(
2141                        "Mean shift samples are expected but missing.\n"
2142                        "This happens if, for example, `ask` is called"
2143                        "  more than once, without calling `tell`\n"
2144                        "(because the first call removes the samples from"
2145                        " the injection list).\n"
2146                        "`cma.sigma_adaptation.CMAAdaptSigmaTPA`"
2147                        " step-size adaptation generates mean shift\n"
2148                        "samples and relies on them. \n"
2149                        "Using ``ask(1)`` for any subsequent calls of"
2150                        " `ask` works OK and TPA works if the\n"
2151                        "first two samples from the"
2152                        " first call are retained as first samples when"
2153                        " calling `tell`. \n"
2154                        "EXAMPLE: \n"
2155                        "    X = es.ask()\n"
2156                        "    X.append(es.ask(1)[0])\n"
2157                        "    ...\n"
2158                        "    es.tell(X, ...)"
2159                    )
2160                # for TPA, set both vectors to the same length and don't
2161                # ever keep the original length
2162                arinj[0] *= self._random_rescaling_factor_to_mahalanobis_size(arinj[0]) / self.sigma
2163                arinj[1] *= (np.sum(arinj[0]**2) / np.sum(arinj[1]**2))**0.5
2164                if not Mh.vequals_approximately(arinj[0], -arinj[1]):
2165                    utils.print_warning(
2166                        "mean_shift_samples, but the first two solutions"
2167                        " are not mirrors.",
2168                        "ask_geno", "CMAEvolutionStrategy",
2169                        self.countiter)
2170                    # arinj[1] /= sum(arinj[0]**2)**0.5 / s1  # revert change
2171            self.number_of_injections_delivered += len(arinj)
2172            assert (self.countiter < 2 or not self.mean_shift_samples
2173                    or self.number_of_injections_delivered >= 2)
2174
2175        Niid = number - len(arinj) # each row is a solution
2176        # compute ary
2177        if Niid >= 0:  # should better be true
2178            ary = self.sigma_vec * np.asarray(self.sm.sample(Niid))
2179            self._updateBDfromSM(self.sm)  # sm.sample invoked lazy update
2180            # unconditional mirroring
2181            if self.sp.lam_mirr and self.opts['CMA_mirrormethod'] == 0:
2182                for i in range(Mh.sround(self.sp.lam_mirr * number / self.popsize)):
2183                    if 2 * (i + 1) > len(ary):
2184                        utils.print_warning("fewer mirrors generated than given in parameter setting (%d<%d)"
2185                                            % (i, self.sp.lam_mirr),
2186                                       "ask_geno", "CMAEvolutionStrategy",
2187                                            iteration=self.countiter,
2188                                            maxwarns=4)
2189                        break
2190                    ary[-1 - 2 * i] = -ary[-2 - 2 * i]
2191            if len(arinj):
2192                ary = np.vstack((arinj, ary))
2193        else:
2194            ary = array(arinj)
2195            assert number == len(arinj)
2196
2197        if (self.opts['verbose'] > 4 and self.countiter < 3 and len(arinj) and
2198                self.adapt_sigma is not CMAAdaptSigmaTPA):
2199            utils.print_message('   %d pre-injected solutions will be used (popsize=%d)' %
2200                                (len(arinj), len(ary)))
2201
2202        pop = xmean + sigma * ary
2203        for i, x in enumerate(pop[:len(arinj)]):
2204            self._injected_solutions_archive[x] = {
2205                'iteration': self.countiter,  # values are currently never used
2206                'index': i,
2207                'counter': len(self._injected_solutions_archive)
2208                }
2209            # pprint(dict(self._injected_solutions_archive))
2210        self.evaluations_per_f_value = 1
2211        self.ary = ary
2212        self.number_of_solutions_asked += len(pop)
2213        return pop
2214
2215    def random_rescale_to_mahalanobis(self, x):
2216        """change `x` like for injection, all on genotypic level"""
2217        x = x - self.mean  # -= fails if dtypes don't agree
2218        if any(x):  # let's not divide by zero
2219            x *= sum(self.randn(1, len(x))[0]**2)**0.5 / self.mahalanobis_norm(x)
2220        x += self.mean
2221        return x
2222    def _random_rescaling_factor_to_mahalanobis_size(self, y):
2223        """``self.mean + self._random_rescaling_factor_to_mahalanobis_size(y) * y``
2224        is guarantied to appear like from the sample distribution.
2225        """
2226        if len(y) != self.N:
2227            raise ValueError('len(y)=%d != %d=dimension' % (len(y), self.N))
2228        if not any(y):
2229            utils.print_warning("input was all-zeros, which is probably a bug",
2230                           "_random_rescaling_factor_to_mahalanobis_size",
2231                                iteration=self.countiter)
2232            return 1.0
2233        return np.sum(self.randn(1, len(y))[0]**2)**0.5 / self.mahalanobis_norm(y)
2234
2235
2236    def get_mirror(self, x, preserve_length=False):
2237        """return ``pheno(self.mean - (geno(x) - self.mean))``.
2238
2239        >>> import numpy as np, cma
2240        >>> es = cma.CMAEvolutionStrategy(np.random.randn(3), 1)  #doctest: +ELLIPSIS
2241        (3_w,...
2242        >>> x = np.random.randn(3)
2243        >>> assert cma.utilities.math.Mh.vequals_approximately(es.mean - (x - es.mean), es.get_mirror(x, preserve_length=True))
2244        >>> x = es.ask(1)[0]
2245        >>> vals = (es.get_mirror(x) - es.mean) / (x - es.mean)
2246        >>> assert cma.utilities.math.Mh.equals_approximately(sum(vals), len(vals) * vals[0])
2247
2248        TODO: this implementation is yet experimental.
2249
2250        TODO: this implementation includes geno-pheno transformation,
2251        however in general GP-transformation should be separated from
2252        specific code.
2253
2254        Selectively mirrored sampling improves to a moderate extend but
2255        overadditively with active CMA for quite understandable reasons.
2256
2257        Optimal number of mirrors are suprisingly small: 1,2,3 for
2258        maxlam=7,13,20 where 3,6,10 are the respective maximal possible
2259        mirrors that must be clearly suboptimal.
2260
2261        """
2262        try:
2263            dx = self.sent_solutions[x]['geno'] - self.mean
2264        except:  # can only happen with injected solutions?!
2265            dx = self.gp.geno(x, from_bounds=self.boundary_handler.inverse,
2266                              copy=True) - self.mean
2267
2268        if not preserve_length:
2269            # dx *= sum(self.randn(1, self.N)[0]**2)**0.5 / self.mahalanobis_norm(dx)
2270            dx *= self._random_rescaling_factor_to_mahalanobis_size(dx)
2271        x = self.mean - dx
2272        y = self.gp.pheno(x, into_bounds=self.boundary_handler.repair)
2273        # old measure: costs 25% in CPU performance with N,lambda=20,200
2274        self.sent_solutions.insert(y, geno=x, iteration=self.countiter)
2275        return y
2276
2277    # ____________________________________________________________
2278    # ____________________________________________________________
2279    #
2280    def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma_fac=1,
2281                     evaluations=1, aggregation=np.median, kappa=1, parallel_mode=False):
2282        """sample `number` solutions and evaluate them on `func`.
2283
2284        Each solution ``s`` is resampled until
2285        ``self.is_feasible(s, func(s)) is True``.
2286
2287        Arguments
2288        ---------
2289        `func`:
2290            objective function, ``func(x)`` accepts a `numpy.ndarray`
2291            and returns a scalar ``if not parallel_mode``. Else returns a
2292            `list` of scalars from a `list` of `numpy.ndarray`.
2293        `args`:
2294            additional parameters for `func`
2295        `gradf`:
2296            gradient of objective function, ``g = gradf(x, *args)``
2297            must satisfy ``len(g) == len(x)``
2298        `number`:
2299            number of solutions to be sampled, by default
2300            population size ``popsize`` (AKA lambda)
2301        `xmean`:
2302            mean for sampling the solutions, by default ``self.mean``.
2303        `sigma_fac`:
2304            multiplier for sampling width, standard deviation, for example
2305            to get a small perturbation of solution `xmean`
2306        `evaluations`:
2307            number of evaluations for each sampled solution
2308        `aggregation`:
2309            function that aggregates `evaluations` values to
2310            as single value.
2311        `kappa`:
2312            multiplier used for the evaluation of the solutions, in
2313            that ``func(m + kappa*(x - m))`` is the f-value for ``x``.
2314
2315        Return
2316        ------
2317        ``(X, fit)``, where
2318
2319        - `X`: list of solutions
2320        - `fit`: list of respective function values
2321
2322        Details
2323        -------
2324        While ``not self.is_feasible(x, func(x))`` new solutions are
2325        sampled. By default
2326        ``self.is_feasible == cma.feasible == lambda x, f: f not in (None, np.NaN)``.
2327        The argument to `func` can be freely modified within `func`.
2328
2329        Depending on the ``CMA_mirrors`` option, some solutions are not
2330        sampled independently but as mirrors of other bad solutions. This
2331        is a simple derandomization that can save 10-30% of the
2332        evaluations in particular with small populations, for example on
2333        the cigar function.
2334
2335        Example
2336        -------
2337        >>> import cma
2338        >>> x0, sigma0 = 8 * [10], 1  # 8-D
2339        >>> es = cma.CMAEvolutionStrategy(x0, sigma0)  #doctest: +ELLIPSIS
2340        (5_w,...
2341        >>> while not es.stop():
2342        ...     X, fit = es.ask_and_eval(cma.ff.elli)  # handles NaN with resampling
2343        ...     es.tell(X, fit)  # pass on fitness values
2344        ...     es.disp(20) # print every 20-th iteration  #doctest: +ELLIPSIS
2345        Iterat #Fevals...
2346        >>> print('terminated on ' + str(es.stop()))  #doctest: +ELLIPSIS
2347        terminated on ...
2348
2349        A single iteration step can be expressed in one line, such that
2350        an entire optimization after initialization becomes::
2351
2352            while not es.stop():
2353                es.tell(*es.ask_and_eval(cma.ff.elli))
2354
2355        """
2356        # initialize
2357        popsize = self.sp.popsize
2358        if number is not None:
2359            popsize = int(number)
2360
2361        if self.opts['CMA_mirrormethod'] == 1:  # direct selective mirrors
2362            nmirrors = Mh.sround(self.sp.lam_mirr * popsize / self.sp.popsize)
2363            self._mirrormethod1_done = self.countiter
2364        else:
2365            # method==0 unconditional mirrors are done in ask_geno
2366            # method==2 delayed selective mirrors are done via injection
2367            nmirrors = 0
2368        assert nmirrors <= popsize // 2
2369        self.mirrors_idx = np.arange(nmirrors)  # might never be used
2370        is_feasible = self.opts['is_feasible']
2371
2372        # do the work
2373        fit = []  # or np.NaN * np.empty(number)
2374        X_first = self.ask(popsize, xmean=xmean, gradf=gradf, args=args)
2375        if xmean is None:
2376            xmean = self.mean  # might have changed in self.ask
2377        X = []
2378        if parallel_mode:
2379            if hasattr(func, 'evaluations'):
2380                evals0 = func.evaluations
2381            fit_first = func(X_first, *args)
2382            # the rest is only book keeping and warnings spitting
2383            if hasattr(func, 'evaluations'):
2384                self.countevals += func.evaluations - evals0 - self.popsize  # why not .sp.popsize ?
2385            if nmirrors and self.opts['CMA_mirrormethod'] > 0 and self.countiter < 2:
2386                utils.print_warning(
2387                    "selective mirrors will not work in parallel mode",
2388                    "ask_and_eval", "CMAEvolutionStrategy")
2389            if evaluations > 1 and self.countiter < 2:
2390                utils.print_warning(
2391                    "aggregating evaluations will not work in parallel mode",
2392                    "ask_and_eval", "CMAEvolutionStrategy")
2393        else:
2394            fit_first = len(X_first) * [None]
2395        for k in range(popsize):
2396            x, f = X_first.pop(0), fit_first.pop(0)
2397            rejected = -1
2398            while f is None or not is_feasible(x, f):  # rejection sampling
2399                if parallel_mode:
2400                    utils.print_warning(
2401                        "rejection sampling will not work in parallel mode"
2402                        " unless the parallel_objective makes a distinction\n"
2403                        "between called with a numpy array vs a list (of"
2404                        " numpy arrays) as first argument.",
2405                        "ask_and_eval", "CMAEvolutionStrategy")
2406                rejected += 1
2407                if rejected:  # resample
2408                    x = self.ask(1, xmean, sigma_fac)[0]
2409                elif k >= popsize - nmirrors:  # selective mirrors
2410                    if k == popsize - nmirrors:
2411                        self.mirrors_idx = np.argsort(fit)[-1:-1 - nmirrors:-1]
2412                    x = self.get_mirror(X[self.mirrors_idx[popsize - 1 - k]])
2413
2414                # constraints handling test hardwired ccccccccccc
2415
2416                length_normalizer = 1
2417                # zzzzzzzzzzzzzzzzzzzzzzzzz
2418                if 11 < 3:
2419                    # for some unclear reason, this normalization does not work as expected: the step-size
2420                    # becomes sometimes too large and overall the mean might diverge. Is the reason that
2421                    # we observe random fluctuations, because the length is not selection relevant?
2422                    # However sigma-adaptation should mainly work on the correlation, not the length?
2423                    # Or is the reason the deviation of the direction introduced by using the original
2424                    # length, which also can effect the measured correlation?
2425                    # Update: if the length of z in CSA is clipped at chiN+1, it works, but only sometimes?
2426                    length_normalizer = self.N**0.5 / self.mahalanobis_norm(x - xmean)  # self.const.chiN < N**0.5, the constant here is irrelevant (absorbed by kappa)
2427                    # print(self.N**0.5 / self.mahalanobis_norm(x - xmean))
2428                    # self.more_to_write += [length_normalizer * 1e-3, length_normalizer * self.mahalanobis_norm(x - xmean) * 1e2]
2429
2430                f = func(x, *args) if kappa == 1 else \
2431                    func(xmean + kappa * length_normalizer * (x - xmean),
2432                         *args)
2433                if is_feasible(x, f) and evaluations > 1:
2434                    f = aggregation([f] + [(func(x, *args) if kappa == 1 else
2435                                            func(xmean + kappa * length_normalizer * (x - xmean), *args))
2436                                           for _i in range(int(evaluations - 1))])
2437                if (rejected + 1) % 1000 == 0:
2438                    utils.print_warning('  %d solutions rejected (f-value NaN or None) at iteration %d' %
2439                          (rejected, self.countiter))
2440            fit.append(f)
2441            X.append(x)
2442        self.evaluations_per_f_value = int(evaluations)
2443        if any(f is None or np.isnan(f) for f in fit):
2444            idxs = [i for i in range(len(fit))
2445                    if fit[i] is None or np.isnan(fit[i])]
2446            utils.print_warning("f-values %s contain None or NaN at indices %s"
2447                                % (str(fit[:30]) + ('...' if len(fit) > 30 else ''),
2448                                   str(idxs)),
2449                                'ask_and_tell',
2450                                'CMAEvolutionStrategy',
2451                                self.countiter)
2452        return X, fit
2453
2454    def _prepare_injection_directions(self):
2455        """provide genotypic directions for TPA and selective mirroring,
2456        with no specific length normalization, to be used in the
2457        coming iteration.
2458
2459        Details:
2460        This method is called in the end of `tell`. The result is
2461        assigned to ``self.pop_injection_directions`` and used in
2462        `ask_geno`.
2463
2464        """
2465        # self.pop_injection_directions is supposed to be empty here
2466        if self.pop_injection_directions or self.pop_injection_solutions:
2467            raise ValueError("""Found unused injected direction/solutions.
2468                This could be a bug in the calling order/logics or due to
2469                a too small popsize used in `ask()` or when only using
2470                `ask(1)` repeatedly. """)
2471        ary = []
2472        if self.mean_shift_samples:
2473            ary = [self.mean - self.mean_old]
2474            ary.append(self.mean_old - self.mean)  # another copy!
2475            if np.alltrue(ary[-1] == 0.0):
2476                utils.print_warning('zero mean shift encountered',
2477                               '_prepare_injection_directions',
2478                               'CMAEvolutionStrategy', self.countiter)
2479        if self.opts['pc_line_samples']: # caveat: before, two samples were used
2480            ary.append(self.pc.copy())
2481        if self.sp.lam_mirr and (
2482                self.opts['CMA_mirrormethod'] == 2 or (
2483                    self.opts['CMA_mirrormethod'] == 1 and ( # replacement for direct selective mirrors
2484                        not hasattr(self, '_mirrormethod1_done') or
2485                        self._mirrormethod1_done < self.countiter - 1))):
2486            i0 = len(ary)
2487            ary += self.get_selective_mirrors()
2488            self._indices_of_selective_mirrors = range(i0, len(ary))
2489        self.pop_injection_directions = ary
2490        return ary
2491
2492    def get_selective_mirrors(self, number=None):
2493        """get mirror genotypic directions from worst solutions.
2494
2495        Details:
2496
2497        To be called after the mean has been updated.
2498
2499        Takes the last ``number=sp.lam_mirr`` entries in the
2500        ``self.pop[self.fit.idx]`` as solutions to be mirrored.
2501
2502        Do not take a mirror if it is suspected to stem from a
2503        previous mirror in order to not go endlessly back and forth.
2504        """
2505        if number is None:
2506            number = self.sp.lam_mirr
2507        if not hasattr(self, '_indices_of_selective_mirrors'):
2508            self._indices_of_selective_mirrors = []
2509        res = []
2510        for i in range(1, number + 1):
2511            if 'all-selective-mirrors' in self.opts['vv'] or self.fit.idx[-i] not in self._indices_of_selective_mirrors:
2512                res.append(self.mean_old - self.pop[self.fit.idx[-i]])
2513        assert len(res) >= number - len(self._indices_of_selective_mirrors)
2514        return res
2515
2516    # ____________________________________________________________
2517    def tell(self, solutions, function_values, check_points=None,
2518             copy=False):
2519        """pass objective function values to prepare for next
2520        iteration. This core procedure of the CMA-ES algorithm updates
2521        all state variables, in particular the two evolution paths, the
2522        distribution mean, the covariance matrix and a step-size.
2523
2524        Arguments
2525        ---------
2526        `solutions`
2527            list or array of candidate solution points (of
2528            type `numpy.ndarray`), most presumably before
2529            delivered by method `ask()` or `ask_and_eval()`.
2530        `function_values`
2531            list or array of objective function values
2532            corresponding to the respective points. Beside for termination
2533            decisions, only the ranking of values in `function_values`
2534            is used.
2535        `check_points`
2536            If ``check_points is None``, only solutions that are not generated
2537            by `ask()` are possibly clipped (recommended). ``False`` does not clip
2538            any solution (not recommended).
2539            If ``True``, clips solutions that realize long steps (i.e. also
2540            those that are unlikely to be generated with `ask()`). `check_points`
2541            can be a list of indices to be checked in solutions.
2542        `copy`
2543            ``solutions`` can be modified in this routine, if ``copy is False``
2544
2545        Details
2546        -------
2547        `tell()` updates the parameters of the multivariate
2548        normal search distribution, namely covariance matrix and
2549        step-size and updates also the attributes ``countiter`` and
2550        ``countevals``. To check the points for consistency is quadratic
2551        in the dimension (like sampling points).
2552
2553        Bugs
2554        ----
2555        The effect of changing the solutions delivered by `ask()`
2556        depends on whether boundary handling is applied. With boundary
2557        handling, modifications are disregarded. This is necessary to
2558        apply the default boundary handling that uses unrepaired
2559        solutions but might change in future.
2560
2561        Example
2562        -------
2563
2564        >>> import cma
2565        >>> func = cma.ff.sphere  # choose objective function
2566        >>> es = cma.CMAEvolutionStrategy(np.random.rand(2) / 3, 1.5)
2567        ... # doctest:+ELLIPSIS
2568        (3_...
2569        >>> while not es.stop():
2570        ...    X = es.ask()
2571        ...    es.tell(X, [func(x) for x in X])
2572        >>> es.result  # result is a `namedtuple` # doctest:+ELLIPSIS
2573        CMAEvolutionStrategyResult(xbest=array([...
2574
2575        :See: class `CMAEvolutionStrategy`, `ask`, `ask_and_eval`, `fmin`
2576    """
2577        if self._flgtelldone:
2578            raise RuntimeError('tell should only be called once per iteration')
2579
2580        lam = len(solutions)
2581        if lam != len(function_values):
2582            raise ValueError('#f-values = %d must equal #solutions = %d'
2583                             % (len(function_values), lam))
2584        if lam + self.sp.lam_mirr < 3:
2585            raise ValueError('population size ' + str(lam) +
2586                             ' is too small with option ' +
2587                             'CMA_mirrors * popsize < 0.5')
2588        if not np.isscalar(function_values[0]):
2589            try:
2590                if np.isscalar(function_values[0][0]):
2591                    if self.countiter <= 1:
2592                        utils.print_warning('''function_values is not a list of scalars,
2593                        the first element equals %s with non-scalar type %s.
2594                        Using now ``[v[0] for v in function_values]`` instead (further warnings are suppressed)'''
2595                                            % (str(function_values[0]), str(type(function_values[0]))))
2596                    function_values = [val[0] for val in function_values]
2597                else:
2598                    raise ValueError('objective function values must be a list of scalars')
2599            except:
2600                utils.print_message("function values=%s" % function_values,
2601                                    method_name='tell', class_name='CMAEvolutionStrategy',
2602                                    verbose=9, iteration=self.countiter)
2603                raise
2604        if any(f is None or np.isnan(f) for f in function_values):
2605            idx_none = [i for i, f in enumerate(function_values) if f is None]
2606            idx_nan = [i for i, f in enumerate(function_values) if f is not None and np.isnan(f)]
2607            m = np.median([f for f in function_values
2608                           if f is not None and not np.isnan(f)])
2609            utils.print_warning("function values with index %s/%s are nan/None and will be set to the median value %s"
2610                                % (str(idx_nan), str(idx_none), str(m)), 'ask',
2611                                'CMAEvolutionStrategy', self.countiter)
2612            for i in idx_nan + idx_none:
2613                function_values[i] = m
2614        if not np.isfinite(function_values).all():
2615            idx = [i for i, f in enumerate(function_values)
2616                   if not np.isfinite(f)]
2617            utils.print_warning("function values with index %s are not finite but %s."
2618                                % (str(idx), str([function_values[i] for i in idx])), 'ask',
2619                                'CMAEvolutionStrategy', self.countiter)
2620        if self.number_of_solutions_asked <= self.number_of_injections:
2621            utils.print_warning("""no independent samples generated because the
2622                number of injected solutions, %d, equals the number of
2623                solutions asked, %d, where %d solutions remain to be injected
2624                """ % (self.number_of_injections,
2625                       self.number_of_solutions_asked,
2626                       len(self.pop_injection_directions) + len(self.pop_injection_solutions)),
2627                "ask_geno", "CMAEvolutionStrategy", self.countiter)
2628        self.number_of_solutions_asked = 0
2629
2630        # ## prepare
2631        N = self.N
2632        sp = self.sp
2633        if 11 < 3 and lam != sp.popsize:  # turned off, because mu should stay constant, still not desastrous
2634            utils.print_warning('population size has changed, recomputing parameters')
2635            self.sp.set(self.opts, lam)  # not really tested
2636        if lam < sp.weights.mu:  # rather decrease cmean instead of having mu > lambda//2
2637            raise ValueError('not enough solutions passed to function tell (mu>lambda)')
2638
2639        self.countiter += 1  # >= 1 now
2640        self.countevals += lam * self.evaluations_per_f_value
2641        self.best.update(solutions,  # caveat: these solutions may be out-of-bounds
2642                         self.sent_solutions, function_values, self.countevals)
2643        flg_diagonal = self.opts['CMA_diagonal'] is True \
2644                       or self.countiter <= self.opts['CMA_diagonal']
2645        if not flg_diagonal and isinstance(self.sm, sampler.GaussStandardConstant):
2646            # switching from diagonal to full covariance learning
2647            self.sm = sampler.GaussFullSampler(N)
2648            self._updateBDfromSM(self.sm)
2649
2650        self._record_rankings(function_values[:2], function_values[2:])  # for analysis
2651
2652        # ## manage fitness
2653        fit = self.fit  # make short cut
2654
2655        # CPU for N,lam=20,200: this takes 10s vs 7s
2656        fit.bndpen = self.boundary_handler.update(function_values, self)(solutions, self.sent_solutions, self.gp)
2657        # for testing:
2658        # fit.bndpen = self.boundary_handler.update(function_values, self)([s.unrepaired for s in solutions])
2659        fit.idx = np.argsort(array(fit.bndpen) + array(function_values))
2660        fit.fit = array(function_values, copy=False)[fit.idx]
2661
2662        # update output data TODO: this is obsolete!? However: need communicate current best x-value?
2663        # old: out['recent_x'] = self.gp.pheno(pop[0])
2664        # self.out['recent_x'] = array(solutions[fit.idx[0]])  # TODO: change in a data structure(?) and use current as identify
2665        # self.out['recent_f'] = fit.fit[0]
2666
2667        # fitness histories
2668        fit.hist.insert(0, fit.fit[0])  # caveat: this may neither be the best nor the best in-bound fitness, TODO
2669        fit.median = (fit.fit[self.popsize // 2] if self.popsize % 2
2670                      else np.mean(fit.fit[self.popsize // 2 - 1: self.popsize // 2 + 1]))
2671        # if len(self.fit.histbest) < 120+30*N/sp.popsize or  # does not help, as tablet in the beginning is the critical counter-case
2672        if ((self.countiter % 5) == 0):  # 20 percent of 1e5 gen.
2673            fit.histbest.insert(0, fit.fit[0])
2674            fit.histmedian.insert(0, fit.median)
2675        if len(fit.histbest) > 2e4:  # 10 + 30*N/sp.popsize:
2676            fit.histbest.pop()
2677            fit.histmedian.pop()
2678        if len(fit.hist) > 10 + 30 * N / sp.popsize:
2679            fit.hist.pop()
2680        if fit.median0 is None:
2681            fit.median0 = fit.median
2682        if fit.median_min > fit.median:
2683            fit.median_min = fit.median
2684
2685        ### line 2665
2686
2687        # TODO: clean up inconsistency when an unrepaired solution is available and used
2688        # now get the genotypes
2689        self.pop_sorted = None
2690        pop = []  # create pop from input argument solutions
2691        for k, s in enumerate(solutions):  # use phenotype before Solution.repair()
2692            if 1 < 3:
2693                pop += [self.gp.geno(s,
2694                            from_bounds=self.boundary_handler.inverse,
2695                            repair=(self.repair_genotype if check_points not in (False, 0, [], ()) else None),
2696                            archive=self.sent_solutions)]  # takes genotype from sent_solutions, if available
2697                try:
2698                    self.archive.insert(s, value=self.sent_solutions.pop(s), fitness=function_values[k])
2699                    # self.sent_solutions.pop(s)
2700                except KeyError:
2701                    pass
2702        # check that TPA mirrors are available
2703        self.pop = pop  # used in check_consistency of CMAAdaptSigmaTPA
2704        self.adapt_sigma.check_consistency(self)
2705
2706        if self.countiter > 1:
2707            self.mean_old_old = self.mean_old
2708        self.mean_old = self.mean
2709        mold = self.mean_old  # just an alias
2710
2711        # check and normalize each x - m
2712        # check_points is a flag (None is default: check non-known solutions) or an index list
2713        # should also a number possible (first check_points points)?
2714        if check_points not in (None, False, 0, [], ()):
2715            # useful in case of injected solutions and/or adaptive encoding, however is automatic with use_sent_solutions
2716            # by default this is not executed
2717            try:
2718                if len(check_points):
2719                    idx = check_points
2720            except:
2721                idx = range(sp.popsize)
2722
2723            for k in idx:
2724                self.repair_genotype(pop[k])
2725
2726        # sort pop for practicability, now pop != self.pop, which is unsorted
2727        pop = np.asarray(pop)[fit.idx]
2728
2729        # prepend best-ever solution to population, in case
2730        # note that pop and fit.fit do not agree anymore in this case
2731        if self.opts['CMA_elitist'] == 'initial':
2732            if not hasattr(self, 'f0'):
2733                utils.print_warning(
2734                    'Set attribute `es.f0` to make initial elitism\n' +
2735                    'available or use cma.fmin.',
2736                    'tell', 'CMAEvolutionStrategy', self.countiter)
2737            elif fit.fit[0] > self.f0:
2738                x_elit = self.mean0.copy()
2739                # self.clip_or_fit_solutions([x_elit], [0]) # just calls repair_genotype
2740                self.random_rescale_to_mahalanobis(x_elit)
2741                pop = array([x_elit] + list(pop), copy=False)
2742                utils.print_message('initial solution injected %f<%f' %
2743                                    (self.f0, fit.fit[0]),
2744                               'tell', 'CMAEvolutionStrategy',
2745                                    self.countiter, verbose=self.opts['verbose'])
2746        elif self.opts['CMA_elitist'] and self.best.f < fit.fit[0]:
2747            if self.best.x_geno is not None:
2748                xp = [self.best.x_geno]
2749                # xp = [self.best.xdict['geno']]
2750                # xp = [self.gp.geno(self.best.x[:])]  # TODO: remove
2751                # print self.mahalanobis_norm(xp[0]-self.mean)
2752            else:
2753                xp = [self.gp.geno(array(self.best.x, copy=True),
2754                                   self.boundary_handler.inverse,
2755                                   copy=False)]
2756                utils.print_warning('genotype for elitist not found', 'tell')
2757            # self.clip_or_fit_solutions(xp, [0])
2758            self.random_rescale_to_mahalanobis(xp[0])
2759            pop = np.asarray([xp[0]] + list(pop))
2760
2761        self.pop_sorted = pop
2762        # compute new mean
2763        self.mean = mold + self.sp.cmean * \
2764                    (np.sum(np.asarray(sp.weights.positive_weights) * pop[0:sp.weights.mu].T, 1) - mold)
2765
2766        # check Delta m (this is not default, but could become at some point)
2767        # CAVE: upper_length=sqrt(2)+2 is too restrictive, test upper_length = sqrt(2*N) thoroughly.
2768        # replaced by repair_geno?
2769        # simple test case injecting self.mean:
2770        # self.mean = 1e-4 * self.sigma * np.random.randn(N)
2771        if 11 < 3 and self.opts['vv'] and check_points:  # CAVEAT: check_points might be an index-list
2772            cmean = self.sp.cmean / min(1, ((self.opts['vv'] * N)**0.5 + 2) / (# abuse of cmean
2773                (self.sp.weights.mueff**0.5 / self.sp.cmean) *
2774                self.mahalanobis_norm(self.mean - mold)))
2775        else:
2776            cmean = self.sp.cmean
2777
2778        # zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
2779        if 11 < 3:
2780            self.more_to_write += [sum(self.mean**2)]
2781        if 11 < 3:  # plot length of mean - mold
2782            self.more_to_write += [self.sp.weights.mueff**0.5 *
2783                sum(((1. / self.D) * np.dot(self.B.T, self.mean - mold))**2)**0.5 /
2784                       self.sigma / N**0.5 / cmean]
2785        ### line 2799
2786
2787        # get learning rate constants
2788        cc = sp.cc
2789        c1 = self.opts['CMA_on'] * self.opts['CMA_rankone'] * self.sm.parameters(
2790            mueff=sp.weights.mueff, lam=sp.weights.lambda_).get('c1', sp.c1)  # mueff and lambda_ should not be necessary here
2791        cmu = self.opts['CMA_on'] * self.opts['CMA_rankmu'] * self.sm.parameters().get('cmu', sp.cmu)
2792        if flg_diagonal:
2793            cc, c1, cmu = sp.cc_sep, sp.c1_sep, sp.cmu_sep
2794
2795        # now the real work can start
2796
2797        # _update_ps must be called before the distribution is changed,
2798        # hsig() calls _update_ps
2799        hsig = self.adapt_sigma.hsig(self)
2800
2801        if 11 < 3:
2802            # hsig = 1
2803            # sp.cc = 4 / (N + 4)
2804            # sp.cs = 4 / (N + 4)
2805            # sp.cc = 1
2806            # sp.damps = 2  #
2807            # sp.CMA_on = False
2808            # c1 = 0  # 2 / ((N + 1.3)**2 + 0 * sp.weights.mu) # 1 / N**2
2809            # cmu = min([1 - c1, cmu])
2810            if self.countiter == 1:
2811                print('parameters modified')
2812        # hsig = sum(self.ps**2) / self.N < 2 + 4./(N+1)
2813        # adjust missing variance due to hsig, in 4-D with damps=1e99 and sig0 small
2814        #       hsig leads to premature convergence of C otherwise
2815        # hsiga = (1-hsig**2) * c1 * cc * (2-cc)  # to be removed in future
2816        c1a = c1 * (1 - (1 - hsig**2) * cc * (2 - cc))  # adjust for variance loss
2817
2818        if 11 < 3:  # diagnostic data
2819            # self.out['hsigcount'] += 1 - hsig
2820            if not hsig:
2821                self.hsiglist.append(self.countiter)
2822        if 11 < 3:  # diagnostic message
2823            if not hsig:
2824                print(str(self.countiter) + ': hsig-stall')
2825        if 11 < 3:  # for testing purpose
2826            hsig = 1  # TODO:
2827            #       put correction term, but how?
2828            if self.countiter == 1:
2829                print('hsig=1')
2830
2831        self.pc = (1 - cc) * self.pc + hsig * (
2832                    (cc * (2 - cc) * self.sp.weights.mueff)**0.5 / self.sigma
2833                        / cmean) * (self.mean - mold) / self.sigma_vec.scaling
2834
2835        # covariance matrix adaptation/udpate
2836        pop_zero = pop - mold
2837        if c1a + cmu > 0:
2838            # TODO: make sure cc is 1 / N**0.5 rather than 1 / N
2839            # TODO: simplify code: split the c1 and cmu update and call self.sm.update twice
2840            #       caveat: for this the decay factor ``c1_times_delta_hsigma - sum(weights)`` should be zero in the second update
2841            sampler_weights = [c1a] + [cmu * w for w in sp.weights]
2842            if len(pop_zero) > len(sp.weights):
2843                sampler_weights = (
2844                        sampler_weights[:1+sp.weights.mu] +
2845                        (len(pop_zero) - len(sp.weights)) * [0] +
2846                        sampler_weights[1+sp.weights.mu:])
2847            if 'inc_cmu_pos' in self.opts['vv']:
2848                sampler_weights = np.asarray(sampler_weights)
2849                sampler_weights[sampler_weights > 0] *= 1 + self.opts['vv']['inc_cmu_pos']
2850            # logger = logging.getLogger(__name__)  # "global" level needs to be DEBUG
2851            # logger.debug("w[0,1]=%f,%f", sampler_weights[0],
2852            #               sampler_weights[1]) if self.countiter < 2 else None
2853            # print(' injected solutions', tuple(self._injected_solutions_archive.values()))
2854            for i, x in enumerate(pop):
2855                try:
2856                    self._injected_solutions_archive.pop(x)
2857                    # self.gp.repaired_solutions.pop(x)
2858                except KeyError:
2859                    pass  # print(i)
2860                else:
2861                    # print(i + 1, '-th weight set to zero')
2862                    # sampler_weights[i + 1] = 0  # weight index 0 is for pc
2863                    sampler_weights[i + 1] *= self.opts['CMA_active_injected']  # weight index 0 is for pc
2864            for s in list(self._injected_solutions_archive):
2865                if self._injected_solutions_archive[s]['iteration'] < self.countiter - 2:
2866                    warnings.warn("""orphanated injected solution %s
2867                        This could be a bug in the calling order/logics or due to
2868                        a too small popsize used in `ask()` or when only using
2869                        `ask(1)` repeatedly. Please check carefully.
2870                        In case this is desired, the warning can be surpressed with
2871                        ``warnings.simplefilter("ignore", cma.evolution_strategy.InjectionWarning)``
2872                        """ % str(self._injected_solutions_archive.pop(s)),
2873                        InjectionWarning)
2874            assert len(sampler_weights) == len(pop_zero) + 1
2875            if flg_diagonal:
2876                self.sigma_vec.update(
2877                    [self.sm.transform_inverse(self.pc)] +
2878                    list(self.sm.transform_inverse(pop_zero /
2879                                        (self.sigma * self.sigma_vec.scaling))),
2880                    array(sampler_weights) / 2)  # TODO: put the 1/2 into update function!?
2881            else:
2882                self.sm.update([(c1 / (c1a + 1e-23))**0.5 * self.pc] +  # c1a * pc**2 gets c1 * pc**2
2883                              list(pop_zero / (self.sigma * self.sigma_vec.scaling)),
2884                              sampler_weights)
2885            if any(np.asarray(self.sm.variances) < 0):
2886                raise RuntimeError("A sampler variance has become negative "
2887                                   "after the update, this must be considered as a bug.\n"
2888                                   "Variances `self.sm.variances`=%s" % str(self.sm.variances))
2889        self._updateBDfromSM(self.sm)
2890
2891        # step-size adaptation, adapt sigma
2892        # in case of TPA, function_values[0] and [1] must reflect samples colinear to xmean - xmean_old
2893        try:
2894            self.sigma *= self.adapt_sigma.update2(self,
2895                                        function_values=function_values)
2896        except (NotImplementedError, AttributeError):
2897            self.adapt_sigma.update(self, function_values=function_values)
2898
2899        if 11 < 3 and self.opts['vv']:
2900            if self.countiter < 2:
2901                print('constant sigma applied')
2902                print(self.opts['vv'])  # N=10,lam=10: 0.8 is optimal
2903            self.sigma = self.opts['vv'] * self.sp.weights.mueff * sum(self.mean**2)**0.5 / N
2904
2905        if any(self.sigma * self.sigma_vec.scaling * self.dC**0.5 <
2906                       np.asarray(self.opts['minstd'])):
2907            self.sigma = max(np.asarray(self.opts['minstd']) /
2908                                (self.sigma_vec * self.dC**0.5))
2909            assert all(self.sigma * self.sigma_vec * self.dC**0.5 >=
2910                       (1-1e-9) * np.asarray(self.opts['minstd']))
2911        elif any(self.sigma * self.sigma_vec.scaling * self.dC**0.5 >
2912                       np.asarray(self.opts['maxstd'])):
2913            self.sigma = min(np.asarray(self.opts['maxstd']) /
2914                             self.sigma_vec * self.dC**0.5)
2915        # g = self.countiter
2916        # N = self.N
2917        # mindx = eval(self.opts['mindx'])
2918        #  if utils.is_str(self.opts['mindx']) else self.opts['mindx']
2919        if self.sigma * min(self.D) < self.opts['mindx']:  # TODO: sigma_vec is missing here
2920            self.sigma = self.opts['mindx'] / min(self.D)
2921
2922        if self.sigma > 1e9 * self.sigma0:
2923            alpha = self.sigma / max(self.sm.variances)**0.5
2924            if alpha > 1:
2925                self.sigma /= alpha**0.5  # adjust only half
2926                self.opts['tolupsigma'] /= alpha**0.5  # to be compared with sigma
2927                self.sm *= alpha
2928                self._updateBDfromSM()
2929
2930        # TODO increase sigma in case of a plateau?
2931
2932        # Uncertainty noise measurement is done on an upper level
2933
2934        # move mean into "feasible preimage", leads to weird behavior on
2935        # 40-D tablet with bound 0.1, not quite explained (constant
2936        # dragging is problematic, but why doesn't it settle), still a bug?
2937        if 11 < 3 and isinstance(self.boundary_handler, BoundTransform) \
2938                and not self.boundary_handler.is_in_bounds(self.mean):
2939            self.mean = array(self.boundary_handler.inverse(
2940                self.boundary_handler.repair(self.mean, copy_if_changed=False),
2941                    copy_if_changed=False), copy=False)
2942        if _new_injections:
2943            self.pop_injection_directions = self._prepare_injection_directions()
2944            if self.opts['verbose'] > 4 and self.countiter < 3 and type(self.adapt_sigma) is not CMAAdaptSigmaTPA and len(self.pop_injection_directions):
2945                utils.print_message('   %d directions prepared for injection %s' %
2946                                    (len(self.pop_injection_directions),
2947                                     "(no more messages will be shown)" if
2948                                     self.countiter == 2 else ""))
2949            self.number_of_injections_delivered = 0
2950        self.pop = []  # remove this in case pop is still needed
2951        # self.pop_sorted = []
2952        self._flgtelldone = True
2953        try:  # shouldn't fail, but let's be nice to code abuse
2954            self.timer.pause()
2955        except AttributeError:
2956            warnings.warn('CMAEvolutionStrategy.tell(countiter=%d): "timer" attribute '
2957                          'not found, probably because `ask` was never called. \n'
2958                          'Timing is likely to work only until `tell` is called (again), '
2959                          'because `tic` will never be called again afterwards.'
2960                          % self.countiter)
2961            self.timer = utils.ElapsedWCTime()
2962
2963        self.more_to_write.check()
2964    # end tell()
2965
2966    def _record_rankings(self, vals, function_values):
2967        "do nothing by default, otherwise assign to `_record_rankings_` after instantiation"
2968    def _record_rankings_(self, vals, function_values):
2969        """compute ranks of `vals` in `function_values` and
2970
2971        in `self.fit.fit` and store the results in `_recorded_rankings`.
2972        The ranking differences between two solutions appear to be similar
2973        in the current and last iteration.
2974        """
2975        vals = list(vals)
2976        r0 = _utils.ranks(vals + list(self.fit.fit))
2977        r1 = _utils.ranks(vals + list(function_values))
2978        self._recorded_rankings = [r0[:2], r1[:2]]
2979        return self._recorded_rankings
2980
2981    def inject(self, solutions, force=None):
2982        """inject list of one or several genotypic solution(s).
2983
2984        This is the preferable way to pass outside proposal solutions
2985        into `CMAEvolutionStrategy`. Passing (bad) solutions directly
2986        via `tell` is likely to fail when ``CMA_active is True`` as by
2987        default.
2988
2989        Unless ``force is True``, the `solutions` are used as direction
2990        relative to the distribution mean to compute a new candidate
2991        solution returned in method `ask_geno` which in turn is used in
2992        method `ask`. Even when ``force is True``, the update in `tell`
2993        takes later care of possibly trimming the update vector.
2994
2995        `inject` is to be called before `ask` or after `tell` and can be
2996        called repeatedly.
2997
2998        >>> import cma
2999        >>> es = cma.CMAEvolutionStrategy(4 * [1], 2)  #doctest: +ELLIPSIS
3000        (4_w,...
3001        >>> while not es.stop():
3002        ...     es.inject([4 * [0.0]])
3003        ...     X = es.ask()
3004        ...     if es.countiter == 0:
3005        ...         assert X[0][0] == X[0][1]  # injected sol. is on the diagonal
3006        ...     es.tell(X, [cma.ff.sphere(x) for x in X])
3007
3008        Details: injected solutions are not used in the "active" update which
3009        would decrease variance in the covariance matrix in this direction.
3010        """
3011        for solution in solutions:
3012            if solution is None:
3013                continue
3014            if len(solution) != self.N:
3015                raise ValueError('method `inject` needs a list or array'
3016                    + (' each el with dimension (`len`) %d' % self.N))
3017            solution = array(solution, copy=False, dtype=float)
3018            if force:
3019                self.pop_injection_solutions.append(solution)
3020            else:
3021                self.pop_injection_directions.append(solution - self.mean)
3022
3023    @property
3024    def result(self):
3025        """return a `CMAEvolutionStrategyResult` `namedtuple`.
3026
3027        :See: `cma.evolution_strategy.CMAEvolutionStrategyResult`
3028            or try ``help(...result)`` on the ``result`` property
3029            of an `CMAEvolutionStrategy` instance or on the
3030            `CMAEvolutionStrategyResult` instance itself.
3031
3032        """
3033        # TODO: how about xcurrent?
3034        # return CMAEvolutionStrategyResult._generate(self)
3035        x, f, evals = self.best.get()
3036        return CMAEvolutionStrategyResult(
3037            x,
3038            f,
3039            evals,
3040            self.countevals,
3041            self.countiter,
3042            self.gp.pheno(self.mean, into_bounds=self.boundary_handler.repair),
3043            self.gp.scales * self.sigma * self.sigma_vec.scaling *
3044                self.dC**0.5,
3045            self.stop()
3046        )
3047
3048    def result_pretty(self, number_of_runs=0, time_str=None,
3049                      fbestever=None):
3050        """pretty print result.
3051
3052        Returns `result` of ``self``.
3053
3054        """
3055        if fbestever is None:
3056            fbestever = self.best.f
3057        s = (' after %i restart' + ('s' if number_of_runs > 1 else '')) \
3058            % number_of_runs if number_of_runs else ''
3059        for k, v in self.stop().items():
3060            print('termination on %s=%s%s' % (k, str(v), s +
3061                  (' (%s)' % time_str if time_str else '')))
3062
3063        print('final/bestever f-value = %e %e' % (self.best.last.f,
3064                                                  fbestever))
3065        if self.N < 9:
3066            print('incumbent solution: ' + str(list(self.gp.pheno(self.mean, into_bounds=self.boundary_handler.repair))))
3067            print('std deviation: ' + str(list(self.sigma * self.sigma_vec.scaling * np.sqrt(self.dC) * self.gp.scales)))
3068        else:
3069            print('incumbent solution: %s ...]' % (str(self.gp.pheno(self.mean, into_bounds=self.boundary_handler.repair)[:8])[:-1]))
3070            print('std deviations: %s ...]' % (str((self.sigma * self.sigma_vec.scaling * np.sqrt(self.dC) * self.gp.scales)[:8])[:-1]))
3071        return self.result
3072
3073    def pickle_dumps(self):
3074        """return ``pickle.dumps(self)``,
3075
3076        if necessary remove unpickleable (and also unnecessary) local
3077        function reference beforehand.
3078
3079        The resulting `bytes` string-object can be saved to a file like::
3080
3081            import cma
3082            es = cma.CMAEvolutionStrategy(3 * [1], 1)
3083            es.optimize(cma.ff.elli, iterations=22)
3084            filename = 'es-pickle-test'
3085            open(filename, 'wb').write(es.pickle_dumps())
3086
3087        and recovered like::
3088
3089            import pickle
3090            es = pickle.load(open(filename, 'rb'))
3091
3092        or::
3093
3094            es = pickle.loads(open(filename, 'rb').read())
3095            es.optimize(cma.ff.elli, iterations=22)  # continue optimizing
3096
3097        """
3098        import pickle
3099        try:  # fine if local function self.objective_function was not assigned
3100            s = pickle.dumps(self)
3101        except:
3102            self.objective_function, fun = None, self.objective_function
3103            try:
3104                s = pickle.dumps(self)
3105            except: raise  # didn't work out
3106            finally:  # reset changed attribute either way
3107                self.objective_function = fun
3108        return s
3109
3110    def repair_genotype(self, x, copy_if_changed=False):
3111        """make sure that solutions fit to the sample distribution.
3112
3113        This interface is versatile and likely to change.
3114
3115        The Mahalanobis distance ``x - self.mean`` is clipping at
3116        ``N**0.5 + 2 * N / (N + 2)``, but the specific repair
3117        mechanism may change in future.
3118        """
3119        x = array(x, copy=False)
3120        mold = array(self.mean, copy=False)
3121        if 1 < 3:  # hard clip at upper_length
3122            upper_length = self.N**0.5 + 2 * self.N / (self.N + 2)
3123            # should become an Option, but how? e.g. [0, 2, 2]
3124            fac = self.mahalanobis_norm(x - mold) / upper_length
3125
3126            if fac > 1:
3127                if copy_if_changed:
3128                    x = (x - mold) / fac + mold
3129                else:  # should be 25% faster:
3130                    x -= mold
3131                    x /= fac
3132                    x += mold
3133                # print self.countiter, k, fac, self.mahalanobis_norm(pop[k] - mold)
3134                # adapt also sigma: which are the trust-worthy/injected solutions?
3135            elif 11 < 3:
3136                return np.exp(np.tanh(((upper_length * fac)**2 / self.N - 1) / 2) / 2)
3137
3138        return x
3139
3140    def manage_plateaus(self, sigma_fac=1.5, sample_fraction=0.5):
3141        """increase `sigma` by `sigma_fac` in case of a plateau.
3142
3143        A plateau is assumed to be present if the best sample and
3144        ``popsize * sample_fraction``-th best sample have the same
3145        fitness.
3146
3147        Example:
3148
3149        >>> import cma
3150        >>> def f(X):
3151        ...     return (len(X) - 1) * [1] + [2]
3152        >>> es = cma.CMAEvolutionStrategy(4 * [0], 5, {'verbose':-9, 'tolflatfitness':1e4})
3153        >>> while not es.stop():
3154        ...     X = es.ask()
3155        ...     es.tell(X, f(X))
3156        ...     es.manage_plateaus()
3157        >>> if es.sigma < 1.5**es.countiter: print((es.sigma, 1.5**es.countiter, es.stop()))
3158
3159        """
3160        if not self._flgtelldone:
3161            utils.print_warning("Inbetween `ask` and `tell` plateaus cannot" +
3162            " be managed, because `sigma` should not change.",
3163                           "manage_plateaus", "CMAEvolutionStrategy",
3164                                self.countiter)
3165            return
3166        f0, fm = Mh.prctile(self.fit.fit, [0, sample_fraction * 100])
3167        if f0 == fm:
3168            self.sigma *= sigma_fac
3169
3170    @property
3171    def condition_number(self):
3172        """condition number of the statistical-model sampler.
3173
3174        Details: neither encoding/decoding from `sigma_vec`-scaling nor
3175        `gp`-transformation are taken into account for this computation.
3176        """
3177        try:
3178            return self.sm.condition_number
3179        except (AttributeError):
3180            return (max(self.D) / min(self.D))**2
3181        except (NotImplementedError):
3182            return (max(self.D) / min(self.D))**2
3183
3184    def alleviate_conditioning_in_coordinates(self, condition=1e8):
3185        """pass scaling from `C` to `sigma_vec`.
3186
3187        As a result, `C` is a correlation matrix, i.e., all diagonal
3188        entries of `C` are `1`.
3189        """
3190        if condition and np.isfinite(condition) and np.max(self.dC) / np.min(self.dC) > condition:
3191            # allows for much larger condition numbers, if axis-parallel
3192            if hasattr(self, 'sm') and isinstance(self.sm, sampler.GaussFullSampler):
3193                old_coordinate_condition = np.max(self.dC) / np.min(self.dC)
3194                old_condition = self.sm.condition_number
3195                factors = self.sm.to_correlation_matrix()
3196                self.sigma_vec *= factors
3197                self.pc /= factors
3198                self._updateBDfromSM(self.sm)
3199                utils.print_message('\ncondition in coordinate system exceeded'
3200                                    ' %.1e, rescaled to %.1e, '
3201                                    '\ncondition changed from %.1e to %.1e'
3202                                      % (old_coordinate_condition, np.max(self.dC) / np.min(self.dC),
3203                                         old_condition, self.sm.condition_number),
3204                                    iteration=self.countiter)
3205
3206    def _tfp(self, x):
3207        return np.dot(self.gp._tf_matrix, x)
3208    def _tfg(self, x):
3209        return np.dot(self.gp._tf_matrix_inv, x)
3210
3211    def alleviate_conditioning(self, condition=1e12):
3212        """pass conditioning of `C` to linear transformation in `self.gp`.
3213
3214        Argument `condition` defines the limit condition number above
3215        which the action is taken.
3216
3217        Details: the action applies only if `self.gp.isidentity`. Then,
3218        the covariance matrix `C` is set (back) to identity and a
3219        respective linear transformation is "added" to `self.gp`.
3220        """
3221        # new interface: if sm.condition_number > condition ...
3222        if not self.gp.isidentity or not condition or self.condition_number < condition:
3223            return
3224        try:
3225            old_condition_number = self.condition_number
3226            tf_inv = self.sm.to_linear_transformation_inverse()
3227            tf = self.sm.to_linear_transformation(reset=True)
3228            self.pc = np.dot(tf_inv, self.pc)
3229            old_C_scales = self.dC**0.5
3230            self._updateBDfromSM(self.sm)
3231        except NotImplementedError:
3232            utils.print_warning("Not Implemented",
3233                                method_name="alleviate_conditioning")
3234            return
3235        self._updateBDfromSM(self.sm)
3236        # dmean_prev = np.dot(self.B, (1. / self.D) * np.dot(self.B.T, (self.mean - 0*self.mean_old) / self.sigma_vec))
3237
3238        # we may like to traverse tf through sigma_vec such that
3239        #       gp.tf * sigma_vec == sigma_vec * tf
3240        # but including sigma_vec shouldn't pose a problem even for
3241        # a large scaling which essentially just changes the exponents
3242        # uniformly in the rows of tf
3243        self.gp._tf_matrix = (self.sigma_vec * tf.T).T  # sig*tf.T .*-multiplies each column of tf with sig
3244        self.gp._tf_matrix_inv = tf_inv / self.sigma_vec  # here was the bug
3245        self.sigma_vec = transformations.DiagonalDecoding(1)
3246
3247        # TODO: refactor old_scales * old_sigma_vec into sigma_vec0 to prevent tolfacupx stopping
3248
3249        self.gp.tf_pheno = self._tfp  # lambda x: np.dot(self.gp._tf_matrix, x)
3250        self.gp.tf_geno = self._tfg  # lambda x: np.dot(self.gp._tf_matrix_inv, x)  # not really necessary
3251        self.gp.isidentity = False
3252        assert self.mean is not self.mean_old
3253
3254        # transform current mean and injected solutions accordingly
3255        self.mean = self.gp.tf_geno(self.mean)  # same as gp.geno()
3256        for i, x in enumerate(self.pop_injection_solutions):
3257            self.pop_injection_solutions[i] = self.gp.tf_geno(x)
3258        for i, x in enumerate(self.pop_injection_directions):
3259            self.pop_injection_directions[i] = self.gp.tf_geno(x)
3260
3261        self.mean_old = self.gp.tf_geno(self.mean_old)  # not needed!?
3262
3263        # self.pc = self.gp.geno(self.pc)  # now done above, which is a better place
3264
3265        # dmean_now = np.dot(self.B, (1. / self.D) * np.dot(self.B.T, (self.mean - 0*self.mean_old) / self.sigma_vec))
3266        # assert Mh.vequals_approximately(dmean_now, dmean_prev)
3267        utils.print_warning('''
3268        geno-pheno transformation introduced based on the
3269        current covariance matrix with condition %.1e -> %.1e,
3270        injected solutions become "invalid" in this iteration'''
3271                        % (old_condition_number, self.condition_number),
3272            'alleviate_conditioning', 'CMAEvolutionStrategy',
3273            self.countiter)
3274
3275    def _updateBDfromSM(self, sm_=None):
3276        """helper function for a smooth transition to sampling classes.
3277
3278        By now all tests run through without this method in effect.
3279        Gradient injection and noeffectaxis however rely on the
3280        non-documented attributes B and D in the sampler. """
3281        # return  # should be outcommented soon, but self.D is still in use (for some tests)!
3282        if sm_ is None:
3283            sm_ = self.sm
3284        if isinstance(sm_, sampler.GaussStandardConstant):
3285            self.B = array(1)
3286            self.D = sm_.variances**0.5
3287            self.C = array(1)
3288            self.dC = self.D
3289        elif isinstance(sm_, (_rgs.GaussVDSampler, _rgs.GaussVkDSampler)):
3290            self.dC = sm_.variances
3291        else:
3292            self.C = self.sm.covariance_matrix  # TODO: this should go away
3293            try:
3294                self.B = self.sm.B
3295                self.D = self.sm.D
3296            except AttributeError:
3297                if self.C is not None:
3298                    self.D, self.B = self.opts['CMA_eigenmethod'](self.C)
3299                    self.D **= 0.5
3300                elif 11 < 3:  # would retain consistency but fails
3301                    self.B = None
3302                    self.D = None
3303            if self.C is not None:
3304                self.dC = np.diag(self.C)
3305
3306    # ____________________________________________________________
3307    # ____________________________________________________________
3308    def feed_for_resume(self, X, function_values):
3309        """Resume a run using the solution history.
3310
3311        CAVEAT: this hasn't been thoroughly tested or in intensive use.
3312
3313        Given all "previous" candidate solutions and their respective
3314        function values, the state of a `CMAEvolutionStrategy` object
3315        can be reconstructed from this history. This is the purpose of
3316        function `feed_for_resume`.
3317
3318        Arguments
3319        ---------
3320        `X`:
3321          (all) solution points in chronological order, phenotypic
3322          representation. The number of points must be a multiple
3323          of popsize.
3324        `function_values`:
3325          respective objective function values
3326
3327        Details
3328        -------
3329        `feed_for_resume` can be called repeatedly with only parts of
3330        the history. The part must have the length of a multiple
3331        of the population size.
3332        `feed_for_resume` feeds the history in popsize-chunks into `tell`.
3333        The state of the random number generator might not be
3334        reconstructed, but this would be only relevant for the future.
3335
3336        Example
3337        -------
3338        ::
3339
3340            import cma
3341
3342            # prepare
3343            (x0, sigma0) = ... # initial values from previous trial
3344            X = ... # list of generated solutions from a previous trial
3345            f = ... # respective list of f-values
3346
3347            # resume
3348            es = cma.CMAEvolutionStrategy(x0, sigma0)
3349            es.feed_for_resume(X, f)
3350
3351            # continue with func as objective function
3352            while not es.stop():
3353                X = es.ask()
3354                es.tell(X, [func(x) for x in X])
3355
3356
3357        Credits to Dirk Bueche and Fabrice Marchal for the feeding idea.
3358
3359        :See also: class `CMAEvolutionStrategy` for a simple dump/load
3360            to resume.
3361
3362        """
3363        if self.countiter > 0:
3364            utils.print_warning('feed should generally be used with a new object instance')
3365        if len(X) != len(function_values):
3366            raise ValueError('number of solutions ' + str(len(X)) +
3367                ' and number function values ' +
3368                str(len(function_values)) + ' must not differ')
3369        popsize = self.sp.popsize
3370        if (len(X) % popsize) != 0:
3371            raise ValueError('number of solutions ' + str(len(X)) +
3372                    ' must be a multiple of popsize (lambda) ' +
3373                    str(popsize))
3374        for i in rglen((X) / popsize):
3375            # feed in chunks of size popsize
3376            self.ask()  # a fake ask, mainly for a conditioned calling of
3377                        # updateBD and secondary to get possibly the same
3378                        # random state
3379            self.tell(X[i * popsize:(i + 1) * popsize],
3380                      function_values[i * popsize:(i + 1) * popsize])
3381
3382    # ____________________________________________________________
3383    # ____________________________________________________________
3384    # ____________________________________________________________
3385    # ____________________________________________________________
3386    def mahalanobis_norm(self, dx):
3387        """return Mahalanobis norm based on the current sample
3388        distribution.
3389
3390        The norm is based on Covariance matrix ``C`` times ``sigma**2``,
3391        and includes ``sigma_vec``. The expected Mahalanobis distance to
3392        the sample mean is about ``sqrt(dimension)``.
3393
3394        Argument
3395        --------
3396        A *genotype* difference `dx`.
3397
3398        Example
3399        -------
3400        >>> import cma, numpy
3401        >>> es = cma.CMAEvolutionStrategy(numpy.ones(10), 1)  #doctest: +ELLIPSIS
3402        (5_w,...
3403        >>> xx = numpy.random.randn(2, 10)
3404        >>> d = es.mahalanobis_norm(es.gp.geno(xx[0]-xx[1]))
3405
3406        `d` is the distance "in" the true sample distribution,
3407        sampled points have a typical distance of ``sqrt(2*es.N)``,
3408        where ``es.N`` is the dimension, and an expected distance of
3409        close to ``sqrt(N)`` to the sample mean. In the example,
3410        `d` is the Euclidean distance, because C = I and sigma = 1.
3411
3412        """
3413        return self.sm.norm(np.asarray(dx) / self.sigma_vec.scaling) / self.sigma
3414
3415    @property
3416    def isotropic_mean_shift(self):
3417        """normalized last mean shift, under random selection N(0,I)
3418
3419        distributed.
3420
3421        Caveat: while it is finite and close to sqrt(n) under random
3422        selection, the length of the normalized mean shift under
3423        *systematic* selection (e.g. on a linear function) tends to
3424        infinity for mueff -> infty. Hence it must be used with great
3425        care for large mueff.
3426        """
3427        z = self.sm.transform_inverse((self.mean - self.mean_old) /
3428                                      self.sigma_vec.scaling)
3429        # TODO:
3430        # works unless a re-parametrisation has been done, and otherwise?
3431        # assert Mh.vequals_approximately(z, np.dot(es.B, (1. / es.D) *
3432        #         np.dot(es.B.T, (es.mean - es.mean_old) / es.sigma_vec)))
3433        z /= self.sigma * self.sp.cmean
3434        z *= self.sp.weights.mueff**0.5
3435        return z
3436
3437    def disp_annotation(self):
3438        """print annotation line for `disp` ()"""
3439        print('Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]')
3440        sys.stdout.flush()
3441
3442    def disp(self, modulo=None):
3443        """print current state variables in a single-line.
3444
3445        Prints only if ``iteration_counter % modulo == 0``.
3446
3447        :See also: `disp_annotation`.
3448        """
3449        if modulo is None:
3450            modulo = self.opts['verb_disp']
3451
3452        # console display
3453
3454        if modulo:
3455            if (self.countiter - 1) % (10 * modulo) < 1:
3456                self.disp_annotation()
3457            if not hasattr(self, 'times_displayed'):
3458                self.time_last_displayed = 0
3459                self.times_displayed = 0
3460
3461            if self.countiter > 0 and (self.stop() or self.countiter < 4
3462                              or self.countiter % modulo < 1
3463                              or self.timer.elapsed - self.time_last_displayed > self.times_displayed):
3464                self.time_last_displayed = self.timer.elapsed
3465                self.times_displayed += 1
3466                if self.opts['verb_time']:
3467                    toc = self.timer.elapsed
3468                    stime = str(int(toc // 60)) + ':' + ("%2.1f" % (toc % 60)).rjust(4, '0')
3469                else:
3470                    stime = ''
3471                print(' '.join((repr(self.countiter).rjust(5),
3472                                repr(self.countevals).rjust(6),
3473                                '%.15e' % (min(self.fit.fit)),
3474                                '%4.1e' % (self.D.max() / self.D.min()
3475                                           if not self.opts['CMA_diagonal'] or self.countiter > self.opts['CMA_diagonal']
3476                                           else max(self.sigma_vec*1) / min(self.sigma_vec*1)),
3477                                '%6.2e' % self.sigma,
3478                                '%6.0e' % (self.sigma * min(self.sigma_vec * self.dC**0.5)),
3479                                '%6.0e' % (self.sigma * max(self.sigma_vec * self.dC**0.5)),
3480                                stime)))
3481                # if self.countiter < 4:
3482                sys.stdout.flush()
3483        return self
3484    def plot(self, *args, **kwargs):
3485        """plot current state variables using `matplotlib`.
3486
3487        Details: calls `self.logger.plot`.
3488        """
3489        try:
3490            self.logger.plot(*args, **kwargs)
3491        except AttributeError:
3492            utils.print_warning('plotting failed, no logger attribute found')
3493        except:
3494            utils.print_warning(('plotting failed with:', sys.exc_info()[0]),
3495                           'plot', 'CMAEvolutionStrategy')
3496        return self
3497
3498class _CMAStopDict(dict):
3499    """keep and update a termination condition dictionary.
3500
3501    The dictionary is "usually" empty and returned by
3502    `CMAEvolutionStrategy.stop()`. The class methods entirely depend on
3503    `CMAEvolutionStrategy` class attributes.
3504
3505    Details
3506    -------
3507    This class is not relevant for the end-user and could be a nested
3508    class, but nested classes cannot be serialized.
3509
3510    Example
3511    -------
3512    >>> import cma
3513    >>> es = cma.CMAEvolutionStrategy(4 * [1], 1, {'verbose':-9})  #doctest: +ELLIPSIS
3514    >>> print(es.stop())
3515    {}
3516    >>> es.optimize(cma.ff.sphere, verb_disp=0)  #doctest: +ELLIPSIS
3517    <...
3518    >>> es.stop()['tolfun'] == 1e-11
3519    True
3520
3521    :See: `OOOptimizer.stop()`, `CMAEvolutionStrategy.stop()`
3522
3523    """
3524    def __init__(self, d={}):
3525        update = isinstance(d, CMAEvolutionStrategy)
3526        super(_CMAStopDict, self).__init__({} if update else d)
3527        self.stoplist = []  # to keep multiple entries
3528        self.lastiter = 0  # probably not necessary
3529        self._get_value = None  # a hack to pass some value
3530        self._value = None  # in iteration zero value is always None
3531        try:
3532            self.stoplist = d.stoplist  # multiple entries
3533        except:
3534            pass
3535        try:
3536            self.lastiter = d.lastiter    # probably not necessary
3537        except:
3538            pass
3539        if update:
3540            self._update(d)
3541
3542    def __call__(self, es=None, check=True):
3543        """update and return the termination conditions dictionary
3544
3545        """
3546        if not check:
3547            return self
3548        if es is None and self.es is None:
3549            raise ValueError('termination conditions need an optimizer to act upon')
3550        self._update(es)
3551        return self
3552
3553    def _update(self, es):
3554        """Test termination criteria and update dictionary
3555
3556        """
3557        if es is None:
3558            es = self.es
3559        assert es is not None
3560
3561        if es.countiter == 0:  # in this case termination tests fail
3562            if self._get_value:
3563                warnings.warn("Cannot get stop value before the first iteration")
3564            self.__init__()
3565            return self
3566
3567        if 11 < 3:  # options might have changed, so countiter ==
3568            if es.countiter == self.lastiter:  # lastiter doesn't help
3569                try:
3570                    if es == self.es:
3571                        return self
3572                except:  # self.es not yet assigned
3573                    pass
3574
3575        self.lastiter = es.countiter
3576        self.es = es
3577
3578        self._get_value or self.clear()  # compute conditions from scratch
3579
3580        N = es.N
3581        opts = es.opts
3582        self.opts = opts  # a hack to get _addstop going
3583
3584        # check user versatile options from signals file
3585
3586        # in 5-D: adds 0% if file does not exist and 25% = 0.2ms per iteration if it exists and verbose=-9
3587        # old measure: adds about 40% time in 5-D, 15% if file is not present
3588        # to avoid any file checking set signals_filename to None or ''
3589        if opts['verbose'] >= -9 and opts['signals_filename'] and os.path.isfile(self.opts['signals_filename']):
3590            with open(opts['signals_filename'], 'r') as f:
3591                s = f.read()
3592            try:
3593                d = dict(ast.literal_eval(s.strip()))
3594            except SyntaxError:
3595                warnings.warn("SyntaxError when parsing the following expression with `ast.literal_eval`:"
3596                            "\n\n%s\n(contents of file %s)" % (s, str(self.opts['signals_filename'])))
3597            else:
3598                for key in list(d):
3599                    if key not in opts.versatile_options():
3600                        utils.print_warning("        unkown or non-versatile option '%s' found in file %s.\n"
3601                                            "        Check out the #v annotation in ``cma.CMAOptions()``."
3602                                            % (key, self.opts['signals_filename']))
3603                        d.pop(key)
3604                opts.update(d)
3605                for key in d:
3606                    opts.eval(key, {'N': N, 'dim': N})
3607
3608        # fitness: generic criterion, user defined w/o default
3609        self._addstop('ftarget',
3610                      es.best.f < opts['ftarget'])
3611        # maxiter, maxfevals: generic criteria
3612        self._addstop('maxfevals',
3613                      es.countevals - 1 >= opts['maxfevals'])
3614        self._addstop('maxiter',
3615                      ## meta_parameters.maxiter_multiplier == 1.0
3616                      es.countiter >= 1.0 * opts['maxiter'])
3617        # tolx, tolfacupx: generic criteria
3618        # tolfun, tolfunhist (CEC:tolfun includes hist)
3619
3620        sigma_x_sigma_vec_x_sqrtdC = es.sigma * (es.sigma_vec.scaling * np.sqrt(es.dC))
3621        self._addstop('tolfacupx',
3622                      np.any(sigma_x_sigma_vec_x_sqrtdC >
3623                          es.sigma0 * es.sigma_vec0 * opts['tolfacupx']))
3624        self._addstop('tolx',
3625                      all(sigma_x_sigma_vec_x_sqrtdC < opts['tolx']) and
3626                      all(es.sigma * (es.sigma_vec.scaling * es.pc) < opts['tolx']),
3627                      max(sigma_x_sigma_vec_x_sqrtdC) if self._get_value else None)
3628                      # None only to be backwards compatible for the time being
3629
3630        current_fitness_range = max(es.fit.fit) - min(es.fit.fit)
3631        historic_fitness_range = max(es.fit.hist) - min(es.fit.hist)
3632        self._addstop('tolfun',
3633                      current_fitness_range < opts['tolfun'] and  # fit.fit is sorted including bound penalties
3634                      historic_fitness_range < opts['tolfun'])
3635        self._addstop('tolfunrel',
3636                      current_fitness_range < opts['tolfunrel'] * (es.fit.median0 - es.fit.median_min),
3637                      current_fitness_range if self._get_value else None)
3638        self._addstop('tolfunhist',
3639                      len(es.fit.hist) > 9 and
3640                      historic_fitness_range < opts['tolfunhist'])
3641
3642        # worst seen false positive: table N=80,lam=80, getting worse for fevals=35e3 \approx 50 * N**1.5
3643        # but the median is not so much getting worse
3644        # / 5 reflects the sparsity of histbest/median
3645        # / 2 reflects the left and right part to be compared
3646        ## meta_parameters.tolstagnation_multiplier == 1.0
3647        l = max(( 1.0 * opts['tolstagnation'] / 5. / 2, len(es.fit.histbest) / 10))
3648        # TODO: why max(..., len(histbest)/10) ???
3649        # TODO: the problem in the beginning is only with best ==> ???
3650        if 11 < 3:  # print for debugging
3651            print(es.countiter, (opts['tolstagnation'], es.countiter > N * (5 + 100 / es.popsize),
3652                  len(es.fit.histbest) > 100,
3653                  np.median(es.fit.histmedian[:l]) >= np.median(es.fit.histmedian[l:2 * l]),
3654                  np.median(es.fit.histbest[:l]) >= np.median(es.fit.histbest[l:2 * l])))
3655        # equality should handle flat fitness
3656        if l <= es.countiter:
3657            l = int(l)  # doesn't work for infinite l
3658            self._addstop('tolstagnation',  # leads sometimes early stop on ftablet, fcigtab, N>=50?
3659                    1 < 3 and opts['tolstagnation'] and es.countiter > N * (5 + 100 / es.popsize) and
3660                    len(es.fit.histbest) > 100 and 2 * l < len(es.fit.histbest) and
3661                    np.median(es.fit.histmedian[:l]) >= np.median(es.fit.histmedian[l:2 * l]) and
3662                    np.median(es.fit.histbest[:l]) >= np.median(es.fit.histbest[l:2 * l]))
3663        # iiinteger: stagnation termination can prevent to find the optimum
3664
3665        self._addstop('tolupsigma', opts['tolupsigma'] and
3666                      es.sigma / np.max(es.D) > es.sigma0 * opts['tolupsigma'],
3667                      es.sigma / np.max(es.D) if self._get_value else None)
3668        try:
3669            self._addstop('timeout',
3670                          es.timer.elapsed > opts['timeout'],
3671                          es.timer.elapsed if self._get_value else None)
3672        except AttributeError:
3673            if es.countiter <= 0:
3674                pass
3675            # else: raise
3676
3677        if 11 < 3 and 2 * l < len(es.fit.histbest):  # TODO: this might go wrong, because the nb of written columns changes
3678            tmp = np.array((-np.median(es.fit.histmedian[:l]) + np.median(es.fit.histmedian[l:2 * l]),
3679                        - np.median(es.fit.histbest[:l]) + np.median(es.fit.histbest[l:2 * l])))
3680            es.more_to_write += [(10**t if t < 0 else t + 1) for t in tmp]  # the latter to get monotonicy
3681
3682        if 1 < 3:
3683            # non-user defined, method specific
3684            # noeffectaxis (CEC: 0.1sigma), noeffectcoord (CEC:0.2sigma), conditioncov
3685            idx = np.nonzero(es.mean == es.mean + 0.2 * sigma_x_sigma_vec_x_sqrtdC)[0]
3686            self._addstop('noeffectcoord', any(idx), list(idx))
3687#                         any([es.mean[i] == es.mean[i] + 0.2 * es.sigma *
3688#                                                         (es.sigma_vec if np.isscalar(es.sigma_vec) else es.sigma_vec[i]) *
3689#                                                         sqrt(es.dC[i])
3690#                              for i in range(N)])
3691#                )
3692            if (opts['CMA_diagonal'] is not True and es.countiter > opts['CMA_diagonal'] and
3693                (es.countiter % 1) == 0):  # save another factor of two?
3694                i = es.countiter % N
3695                try:
3696                    self._addstop('noeffectaxis',
3697                                 np.sum(es.mean == es.mean + 0.1 * es.sigma *
3698                                     es.sm.D[i] * es.sigma_vec.scaling *
3699                                     (es.sm.B[:, i] if len(es.sm.B.shape) > 1 else es.sm.B[0])) == N)
3700                except AttributeError:
3701                    pass
3702            self._addstop('tolconditioncov',
3703                          opts['tolconditioncov'] and
3704                          es.D[-1] > opts['tolconditioncov']**0.5 * es.D[0], opts['tolconditioncov'])
3705
3706            self._addstop('callback', any(es.callbackstop), es.callbackstop)  # termination_callback
3707
3708        if 1 < 3 or len(self): # only if another termination criterion is satisfied
3709            if 1 < 3:
3710                if es.fit.fit[0] < es.fit.fit[int(0.75 * es.popsize)]:
3711                    es.fit.flatfit_iterations = 0
3712                else:
3713                    # print(es.fit.fit)
3714                    es.fit.flatfit_iterations += 1
3715                    if (es.fit.flatfit_iterations > opts['tolflatfitness'] # or
3716                        # mainly for historical reasons:
3717                        # max(es.fit.hist[:1 + int(opts['tolflatfitness'])]) == min(es.fit.hist[:1 + int(opts['tolflatfitness'])])
3718                       ):
3719                        self._addstop('tolflatfitness')
3720                        if 11 < 3 and max(es.fit.fit) == min(es.fit.fit) == es.best.last.f:  # keep warning for historical reasons for the time being
3721                            utils.print_warning(
3722                                "flat fitness (f=%f, sigma=%.2e). "
3723                                "For small sigma, this could indicate numerical convergence. \n"
3724                                "Otherwise, please (re)consider how to compute the fitness more elaborately." %
3725                                (es.fit.fit[0], es.sigma), iteration=es.countiter)
3726            if 11 < 3:  # add stop condition, in case, replaced by above, subject to removal
3727                self._addstop('flat fitness',  # message via stopdict
3728                         len(es.fit.hist) > 9 and
3729                         max(es.fit.hist) == min(es.fit.hist) and
3730                              max(es.fit.fit) == min(es.fit.fit),
3731                         "please (re)consider how to compute the fitness more elaborately if sigma=%.2e is large" % es.sigma)
3732        if 11 < 3 and opts['vv'] == 321:
3733            self._addstop('||xmean||^2<ftarget', sum(es.mean**2) <= opts['ftarget'])
3734
3735        return self
3736
3737    def _addstop(self, key, cond=True, val=None):
3738        if key == self._get_value:
3739            self._value = val
3740            self._get_value = None
3741        elif cond:
3742            self.stoplist.append(key)  # can have the same key twice
3743            self[key] = val if val is not None \
3744                            else self.opts.get(key, None)
3745
3746    def clear(self):
3747        """empty the stopdict"""
3748        for k in list(self):
3749            self.pop(k)
3750        self.stoplist = []
3751
3752class _CMAParameters(object):
3753    """strategy parameters like population size and learning rates.
3754
3755    Note:
3756        contrary to `CMAOptions`, `_CMAParameters` is not (yet) part of the
3757        "user-interface" and subject to future changes (it might become
3758        a `collections.namedtuple`)
3759
3760    Example
3761    -------
3762    >>> import cma
3763    >>> es = cma.CMAEvolutionStrategy(20 * [0.1], 1)  #doctest: +ELLIPSIS
3764    (6_w,12)-aCMA-ES (mu_w=3.7,w_1=40%) in dimension 20 (seed=...)
3765    >>>
3766    >>> type(es.sp)  # sp contains the strategy parameters
3767    <class 'cma.evolution_strategy._CMAParameters'>
3768    >>> es.sp.disp()  #doctest: +ELLIPSIS
3769    {'CMA_on': True,
3770     'N': 20,
3771     'c1': 0.00437235...,
3772     'c1_sep': 0.0343279...,
3773     'cc': 0.171767...,
3774     'cc_sep': 0.252594...,
3775     'cmean': array(1...,
3776     'cmu': 0.00921656...,
3777     'cmu_sep': 0.0565385...,
3778     'lam_mirr': 0,
3779     'mu': 6,
3780     'popsize': 12,
3781     'weights': [0.4024029428...,
3782                 0.2533890840...,
3783                 0.1662215645...,
3784                 0.1043752252...,
3785                 0.05640347757...,
3786                 0.01720770576...,
3787                 -0.05018713636...,
3788                 -0.1406167894...,
3789                 -0.2203813963...,
3790                 -0.2917332686...,
3791                 -0.3562788884...,
3792                 -0.4152044225...]}
3793    >>>
3794
3795    :See: `CMAOptions`, `CMAEvolutionStrategy`
3796
3797    """
3798    def __init__(self, N, opts, ccovfac=1, verbose=True):
3799        """Compute strategy parameters, mainly depending on
3800        dimension and population size, by calling `set`
3801
3802        """
3803        self.N = N
3804        if ccovfac == 1:
3805            ccovfac = opts['CMA_on']  # that's a hack
3806        self.popsize = None  # type: int - declaring the attribute, not necessary though
3807        self.set(opts, ccovfac=ccovfac, verbose=verbose)
3808
3809    def set(self, opts, popsize=None, ccovfac=1, verbose=True):
3810        """Compute strategy parameters as a function
3811        of dimension and population size """
3812
3813        limit_fac_cc = 4.0  # in future: 10**(1 - N**-0.33)?
3814
3815        def conedf(df, mu, N):
3816            """used for computing separable learning rate"""
3817            return 1. / (df + 2. * np.sqrt(df) + float(mu) / N)
3818
3819        def cmudf(df, mu, alphamu):
3820            """used for computing separable learning rate"""
3821            return (alphamu + mu + 1. / mu - 2) / (df + 4 * np.sqrt(df) + mu / 2.)
3822
3823        sp = self  # mainly for historical reasons
3824        N = sp.N
3825        if popsize:
3826            opts.evalall({'N':N, 'popsize':popsize})
3827        else:
3828            popsize = opts.evalall({'N':N})['popsize']  # the default popsize is computed in CMAOptions()
3829            popsize *= opts['popsize_factor']
3830        ## meta_parameters.lambda_exponent == 0.0
3831        popsize = int(popsize + N** 0.0 - 1)
3832
3833        # set weights
3834        if utils.is_(opts['CMA_recombination_weights']):
3835            sp.weights = RecombinationWeights(opts['CMA_recombination_weights'])
3836            popsize = len(sp.weights)
3837        elif opts['CMA_mu']:
3838            sp.weights = RecombinationWeights(2 * opts['CMA_mu'])
3839            while len(sp.weights) < popsize:
3840                sp.weights.insert(sp.weights.mu, 0.0)  # doesn't change mu or mueff
3841        else:  # default
3842            sp.weights = RecombinationWeights(popsize)
3843        # weights.finalize_negative_weights will be called below
3844        sp.popsize = popsize
3845        sp.mu = sp.weights.mu  # not used anymore but for the record
3846
3847        if opts['CMA_mirrors'] < 0.5:
3848            sp.lam_mirr = int(0.5 + opts['CMA_mirrors'] * popsize)
3849        elif opts['CMA_mirrors'] > 1:
3850            sp.lam_mirr = int(0.5 + opts['CMA_mirrors'])
3851        else:
3852            sp.lam_mirr = int(0.5 + 0.16 * min((popsize, 2 * N + 2)) + 0.29)  # 0.158650... * popsize is optimal
3853            # lam = arange(2,22)
3854            # mirr = 0.16 + 0.29/lam
3855            # print(lam); print([int(0.5 + l) for l in mirr*lam])
3856            # [ 2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21]
3857            # [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4]
3858        # in principle we have mu_opt = popsize/2 + lam_mirr/2,
3859        # which means in particular weights should only be negative for q > 0.5+mirr_frac/2
3860        if sp.popsize // 2 > sp.popsize - 2 * sp.lam_mirr + 1:
3861            utils.print_warning("pairwise selection is not implemented, therefore " +
3862                  " mu = %d > %d = %d - 2*%d + 1 = popsize - 2*mirr + 1 can produce a bias" % (
3863                    sp.popsize // 2, sp.popsize - 2 * sp.lam_mirr + 1, sp.popsize, sp.lam_mirr))
3864        if sp.lam_mirr > sp.popsize // 2:
3865            raise ValueError("fraction of mirrors in the population as read from option CMA_mirrors cannot be larger 0.5, " +
3866                         "theoretically optimal is 0.159")
3867
3868        mueff = sp.weights.mueff
3869
3870        # line 3415
3871        ## meta_parameters.cc_exponent == 1.0
3872        b = 1.0
3873        ## meta_parameters.cc_multiplier == 1.0
3874        sp.cc = 1.0 * (limit_fac_cc + mueff / N)**b / \
3875                (N**b + (limit_fac_cc + 2 * mueff / N)**b)
3876        sp.cc_sep = (1 + 1 / N + mueff / N) / \
3877                    (N**0.5 + 1 / N + 2 * mueff / N)
3878        if hasattr(opts['vv'], '__getitem__'):
3879            if 'sweep_ccov1' in opts['vv']:
3880                sp.cc = 1.0 * (4 + mueff / N)**0.5 / ((N + 4)**0.5 +
3881                                                    (2 * mueff / N)**0.5)
3882            if 'sweep_cc' in opts['vv']:
3883                sp.cc = opts['vv']['sweep_cc']
3884                sp.cc_sep = sp.cc
3885                print('cc is %f' % sp.cc)
3886
3887        ## meta_parameters.c1_multiplier == 1.0
3888        sp.c1 = (1.0 * opts['CMA_rankone'] * ccovfac * min(1, sp.popsize / 6) *
3889                 ## meta_parameters.c1_exponent == 2.0
3890                 2 / ((N + 1.3)** 2.0 + mueff))
3891                 # 2 / ((N + 1.3)** 1.5 + mueff))  # TODO
3892                 # 2 / ((N + 1.3)** 1.75 + mueff))  # TODO
3893        # 1/0
3894        sp.c1_sep = opts['CMA_rankone'] * ccovfac * conedf(N, mueff, N)
3895        if 11 < 3:
3896            sp.c1 = 0.
3897            print('c1 is zero')
3898        if utils.is_(opts['CMA_rankmu']):  # also empty
3899            ## meta_parameters.cmu_multiplier == 2.0
3900            alphacov = 2.0
3901            ## meta_parameters.rankmu_offset == 0.25
3902            rankmu_offset = 0.25
3903            # the influence of rankmu_offset in [0, 1] on performance is
3904            # barely visible
3905            if hasattr(opts['vv'], '__getitem__') and 'sweep_rankmu_offset' in opts['vv']:
3906                rankmu_offset = opts['vv']['sweep_rankmu_offset']
3907                print("rankmu_offset = %.2f" % rankmu_offset)
3908            mu = mueff
3909            sp.cmu = min(1 - sp.c1,
3910                         opts['CMA_rankmu'] * ccovfac * alphacov *
3911                         # simpler nominator would be: (mu - 0.75)
3912                         (rankmu_offset + mu + 1 / mu - 2) /
3913                         ## meta_parameters.cmu_exponent == 2.0
3914                         ((N + 2)** 2.0 + alphacov * mu / 2))
3915                         # ((N + 2)** 1.5 + alphacov * mu / 2))  # TODO
3916                         # ((N + 2)** 1.75 + alphacov * mu / 2))  # TODO
3917                         # cmu -> 1 for mu -> N**2 * (2 / alphacov)
3918            if hasattr(opts['vv'], '__getitem__') and 'sweep_ccov' in opts['vv']:
3919                sp.cmu = opts['vv']['sweep_ccov']
3920            sp.cmu_sep = min(1 - sp.c1_sep, ccovfac * cmudf(N, mueff, rankmu_offset))
3921        else:
3922            sp.cmu = sp.cmu_sep = 0
3923        if hasattr(opts['vv'], '__getitem__') and 'sweep_ccov1' in opts['vv']:
3924            sp.c1 = opts['vv']['sweep_ccov1']
3925
3926        if any(w < 0 for w in sp.weights):
3927            if opts['CMA_active'] and opts['CMA_on'] and opts['CMA_rankmu']:
3928                sp.weights.finalize_negative_weights(N, sp.c1, sp.cmu)
3929                # this is re-done using self.sm.parameters()['c1']...
3930            else:
3931                sp.weights.zero_negative_weights()
3932
3933        # line 3834
3934        sp.CMA_on = sp.c1 + sp.cmu > 0
3935        # print(sp.c1_sep / sp.cc_sep)
3936
3937        if not opts['CMA_on'] and opts['CMA_on'] not in (None, [], (), ''):
3938            sp.CMA_on = False
3939            # sp.c1 = sp.cmu = sp.c1_sep = sp.cmu_sep = 0
3940        # line 3480
3941        if 11 < 3:
3942            # this is worse than damps = 1 + sp.cs for the (1,10000)-ES on 40D parabolic ridge
3943            sp.damps = 0.3 + 2 * max([mueff / sp.popsize, ((mueff - 1) / (N + 1))**0.5 - 1]) + sp.cs
3944        if 11 < 3:
3945            # this does not work for lambda = 4*N^2 on the parabolic ridge
3946            sp.damps = opts['CSA_dampfac'] * (2 - 0 * sp.lam_mirr / sp.popsize) * mueff / sp.popsize + 0.3 + sp.cs
3947            # nicer future setting
3948            print('damps =', sp.damps)
3949        if 11 < 3:
3950            sp.damps = 10 * sp.damps  # 1e99 # (1 + 2*max(0,sqrt((mueff-1)/(N+1))-1)) + sp.cs;
3951            # sp.damps = 20 # 1. + 20 * sp.cs**-1  # 1e99 # (1 + 2*max(0,sqrt((mueff-1)/(N+1))-1)) + sp.cs;
3952            print('damps is %f' % (sp.damps))
3953
3954        sp.cmean = np.asarray(opts['CMA_cmean'], dtype=float)
3955        # sp.kappa = 1  # 4-D, lam=16, rank1, kappa < 4 does not influence convergence rate
3956                        # in larger dim it does, 15-D with defaults, kappa=8 factor 2
3957        if 11 < 3 and np.any(sp.cmean != 1):
3958            print('  cmean = ' + str(sp.cmean))
3959
3960        if verbose:
3961            if not sp.CMA_on:
3962                print('covariance matrix adaptation turned off')
3963            if opts['CMA_mu'] != None:
3964                print('mu = %d' % (sp.weights.mu))
3965
3966        # return self  # the constructor returns itself
3967
3968    def disp(self):
3969        pprint(self.__dict__)
3970
3971
3972def fmin2(*args, **kwargs):
3973    """wrapper around `cma.fmin` returning the tuple ``(xbest, es)``,
3974
3975    and with the same in input arguments as `fmin`. Hence a typical
3976    calling pattern may be::
3977
3978        x, es = cma.fmin2(...)  # recommended pattern
3979        es = cma.fmin2(...)[1]  # `es` contains all available information
3980        x = cma.fmin2(...)[0]   # keep only the best evaluated solution
3981
3982    `fmin2` is an alias for::
3983
3984        res = fmin(...)
3985        return res[0], res[-2]
3986
3987    `fmin` from `fmin2` is::
3988
3989        es = fmin2(...)[1]  # fmin2(...)[0] is es.result[0]
3990        return es.result + (es.stop(), es, es.logger)
3991
3992    The best found solution is equally available under::
3993
3994        fmin(...)[0]
3995        fmin2(...)[0]
3996        fmin2(...)[1].result[0]
3997        fmin2(...)[1].result.xbest
3998        fmin2(...)[1].best.x
3999
4000    The incumbent, current estimate for the optimum is available under::
4001
4002        fmin(...)[5]
4003        fmin2(...)[1].result[5]
4004        fmin2(...)[1].result.xfavorite
4005
4006    """
4007    res = fmin(*args, **kwargs)
4008    return res[0], res[-2]
4009
4010
4011all_stoppings = []  # accessable via cma.evolution_strategy.all_stoppings, bound to change
4012def fmin(objective_function, x0, sigma0,
4013         options=None,
4014         args=(),
4015         gradf=None,
4016         restarts=0,
4017         restart_from_best='False',
4018         incpopsize=2,
4019         eval_initial_x=False,
4020         parallel_objective=None,
4021         noise_handler=None,
4022         noise_change_sigma_exponent=1,
4023         noise_kappa_exponent=0,  # TODO: add max kappa value as parameter
4024         bipop=False,
4025         callback=None):
4026    """functional interface to the stochastic optimizer CMA-ES
4027    for non-convex function minimization.
4028
4029    Calling Sequences
4030    =================
4031    ``fmin(objective_function, x0, sigma0)``
4032        minimizes ``objective_function`` starting at ``x0`` and with
4033        standard deviation ``sigma0`` (step-size)
4034    ``fmin(objective_function, x0, sigma0, options={'ftarget': 1e-5})``
4035        minimizes ``objective_function`` up to target function value 1e-5,
4036        which is typically useful for benchmarking.
4037    ``fmin(objective_function, x0, sigma0, args=('f',))``
4038        minimizes ``objective_function`` called with an additional
4039        argument ``'f'``.
4040    ``fmin(objective_function, x0, sigma0, options={'ftarget':1e-5, 'popsize':40})``
4041        uses additional options ``ftarget`` and ``popsize``
4042    ``fmin(objective_function, esobj, None, options={'maxfevals': 1e5})``
4043        uses the `CMAEvolutionStrategy` object instance ``esobj`` to
4044        optimize ``objective_function``, similar to ``esobj.optimize()``.
4045
4046    Arguments
4047    =========
4048    ``objective_function``
4049        called as ``objective_function(x, *args)`` to be minimized.
4050        ``x`` is a one-dimensional `numpy.ndarray`. See also the
4051        `parallel_objective` argument.
4052        ``objective_function`` can return `numpy.NaN`, which is
4053        interpreted as outright rejection of solution ``x`` and invokes
4054        an immediate resampling and (re-)evaluation of a new solution
4055        not counting as function evaluation. The attribute
4056        ``variable_annotations`` is passed into the
4057        ``CMADataLogger.persistent_communication_dict``.
4058    ``x0``
4059        list or `numpy.ndarray`, initial guess of minimum solution
4060        before the application of the geno-phenotype transformation
4061        according to the ``transformation`` option.  It can also be a
4062        callable that is called (without input argument) before each
4063        restart to yield the initial guess such that each restart may start
4064        from a different place. Otherwise, ``x0`` can also be a
4065        `cma.CMAEvolutionStrategy` object instance, in that case ``sigma0``
4066        can be ``None``.
4067    ``sigma0``
4068        scalar, initial standard deviation in each coordinate.
4069        ``sigma0`` should be about 1/4th of the search domain width
4070        (where the optimum is to be expected). The variables in
4071        ``objective_function`` should be scaled such that they
4072        presumably have similar sensitivity.
4073        See also `ScaleCoordinates`.
4074    ``options``
4075        a dictionary with additional options passed to the constructor
4076        of class ``CMAEvolutionStrategy``, see ``cma.CMAOptions`` ()
4077        for a list of available options.
4078    ``args=()``
4079        arguments to be used to call the ``objective_function``
4080    ``gradf=None``
4081        gradient of f, where ``len(gradf(x, *args)) == len(x)``.
4082        ``gradf`` is called once in each iteration if
4083        ``gradf is not None``.
4084    ``restarts=0``
4085        number of restarts with increasing population size, see also
4086        parameter ``incpopsize``, implementing the IPOP-CMA-ES restart
4087        strategy, see also parameter ``bipop``; to restart from
4088        different points (recommended), pass ``x0`` as a string.
4089    ``restart_from_best=False``
4090        which point to restart from
4091    ``incpopsize=2``
4092        multiplier for increasing the population size ``popsize`` before
4093        each restart
4094    ``parallel_objective``
4095        an objective function that accepts a list of `numpy.ndarray` as
4096        input and returns a `list`, which is mostly used instead of
4097        `objective_function`, but for the initial (also initial
4098        elitist) and the final evaluations unless
4099        ``not callable(objective_function)``. If ``parallel_objective``
4100        is given, the ``objective_function`` (first argument) may be
4101        ``None``.
4102    ``eval_initial_x=None``
4103        evaluate initial solution, for ``None`` only with elitist option
4104    ``noise_handler=None``
4105        a ``NoiseHandler`` class or instance or ``None``. Example:
4106        ``cma.fmin(f, 6 * [1], 1, noise_handler=cma.NoiseHandler(6))``
4107        see ``help(cma.NoiseHandler)``.
4108    ``noise_change_sigma_exponent=1``
4109        exponent for the sigma increment provided by the noise handler for
4110        additional noise treatment. 0 means no sigma change.
4111    ``noise_evaluations_as_kappa=0``
4112        instead of applying reevaluations, the "number of evaluations"
4113        is (ab)used as scaling factor kappa (experimental).
4114    ``bipop=False``
4115        if `True`, run as BIPOP-CMA-ES; BIPOP is a special restart
4116        strategy switching between two population sizings - small
4117        (like the default CMA, but with more focused search) and
4118        large (progressively increased as in IPOP). This makes the
4119        algorithm perform well both on functions with many regularly
4120        or irregularly arranged local optima (the latter by frequently
4121        restarting with small populations).  For the `bipop` parameter
4122        to actually take effect, also select non-zero number of
4123        (IPOP) restarts; the recommended setting is ``restarts<=9``
4124        and `x0` passed as a string using `numpy.rand` to generate
4125        initial solutions. Note that small-population restarts
4126        do not count into the total restart count.
4127    ``callback=None``
4128        `callable` or list of callables called at the end of each
4129        iteration with the current `CMAEvolutionStrategy` instance
4130        as argument.
4131
4132    Optional Arguments
4133    ==================
4134    All values in the `options` dictionary are evaluated if they are of
4135    type `str`, besides `verb_filenameprefix`, see class `CMAOptions` for
4136    details. The full list is available by calling ``cma.CMAOptions()``.
4137
4138    >>> import cma
4139    >>> cma.CMAOptions()  #doctest: +ELLIPSIS
4140    {...
4141
4142    Subsets of options can be displayed, for example like
4143    ``cma.CMAOptions('tol')``, or ``cma.CMAOptions('bound')``,
4144    see also class `CMAOptions`.
4145
4146    Return
4147    ======
4148    Return the list provided in `CMAEvolutionStrategy.result` appended
4149    with termination conditions, an `OOOptimizer` and a `BaseDataLogger`::
4150
4151        res = es.result + (es.stop(), es, logger)
4152
4153    where
4154        - ``res[0]`` (``xopt``) -- best evaluated solution
4155        - ``res[1]`` (``fopt``) -- respective function value
4156        - ``res[2]`` (``evalsopt``) -- respective number of function evaluations
4157        - ``res[3]`` (``evals``) -- number of overall conducted objective function evaluations
4158        - ``res[4]`` (``iterations``) -- number of overall conducted iterations
4159        - ``res[5]`` (``xmean``) -- mean of the final sample distribution
4160        - ``res[6]`` (``stds``) -- effective stds of the final sample distribution
4161        - ``res[-3]`` (``stop``) -- termination condition(s) in a dictionary
4162        - ``res[-2]`` (``cmaes``) -- class `CMAEvolutionStrategy` instance
4163        - ``res[-1]`` (``logger``) -- class `CMADataLogger` instance
4164
4165    Details
4166    =======
4167    This function is an interface to the class `CMAEvolutionStrategy`. The
4168    latter class should be used when full control over the iteration loop
4169    of the optimizer is desired.
4170
4171    Examples
4172    ========
4173    The following example calls `fmin` optimizing the Rosenbrock function
4174    in 10-D with initial solution 0.1 and initial step-size 0.5. The
4175    options are specified for the usage with the `doctest` module.
4176
4177    >>> import cma
4178    >>> # cma.CMAOptions()  # returns all possible options
4179    >>> options = {'CMA_diagonal':100, 'seed':1234, 'verb_time':0}
4180    >>>
4181    >>> res = cma.fmin(cma.ff.rosen, [0.1] * 10, 0.3, options)  #doctest: +ELLIPSIS
4182    (5_w,10)-aCMA-ES (mu_w=3.2,w_1=45%) in dimension 10 (seed=1234...)
4183       Covariance matrix is diagonal for 100 iterations (1/ccov=26...
4184    Iterat #Fevals   function value  axis ratio  sigma ...
4185        1     10 ...
4186    termination on tolfun=1e-11 ...
4187    final/bestever f-value = ...
4188    >>> assert res[1] < 1e-12  # f-value of best found solution
4189    >>> assert res[2] < 8000  # evaluations
4190
4191    The above call is pretty much equivalent with the slightly more
4192    verbose call::
4193
4194        res = cma.CMAEvolutionStrategy([0.1] * 10, 0.3,
4195                    options=options).optimize(cma.ff.rosen).result
4196
4197    where `optimize` returns a `CMAEvolutionStrategy` instance. The
4198    following example calls `fmin` optimizing the Rastrigin function
4199    in 3-D with random initial solution in [-2,2], initial step-size 0.5
4200    and the BIPOP restart strategy (that progressively increases population).
4201    The options are specified for the usage with the `doctest` module.
4202
4203    >>> import cma
4204    >>> # cma.CMAOptions()  # returns all possible options
4205    >>> options = {'seed':12345, 'verb_time':0, 'ftarget': 1e-8}
4206    >>>
4207    >>> res = cma.fmin(cma.ff.rastrigin, lambda : 2. * np.random.rand(3) - 1, 0.5,
4208    ...                options, restarts=9, bipop=True)  #doctest: +ELLIPSIS
4209    (3_w,7)-aCMA-ES (mu_w=2.3,w_1=58%) in dimension 3 (seed=12345...
4210
4211    In either case, the method::
4212
4213        cma.plot();
4214
4215    (based on `matplotlib.pyplot`) produces a plot of the run and, if
4216    necessary::
4217
4218        cma.s.figshow()
4219
4220    shows the plot in a window. Finally::
4221
4222        cma.s.figsave('myfirstrun')  # figsave from matplotlib.pyplot
4223
4224    will save the figure in a png.
4225
4226    We can use the gradient like
4227
4228    >>> import cma
4229    >>> res = cma.fmin(cma.ff.rosen, np.zeros(10), 0.1,
4230    ...             options = {'ftarget':1e-8,},
4231    ...             gradf=cma.ff.grad_rosen,
4232    ...         )  #doctest: +ELLIPSIS
4233    (5_w,...
4234    >>> assert cma.ff.rosen(res[0]) < 1e-8
4235    >>> assert res[2] < 3600  # 1% are > 3300
4236    >>> assert res[3] < 3600  # 1% are > 3300
4237
4238    If solution can only be comparatively ranked, either use
4239    `CMAEvolutionStrategy` directly or the objective accepts a list
4240    of solutions as input:
4241
4242    >>> def parallel_sphere(X): return [cma.ff.sphere(x) for x in X]
4243    >>> x, es = cma.fmin2(None, 3 * [0], 0.1, {'verbose': -9},
4244    ...                   parallel_objective=parallel_sphere)
4245    >>> assert es.result[1] < 1e-9
4246
4247    :See also: `CMAEvolutionStrategy`, `OOOptimizer.optimize`, `plot`,
4248        `CMAOptions`, `scipy.optimize.fmin`
4249
4250    """  # style guides say there should be the above empty line
4251    if 1 < 3:  # try: # pass on KeyboardInterrupt
4252        if not objective_function and not parallel_objective:  # cma.fmin(0, 0, 0)
4253            return CMAOptions()  # these opts are by definition valid
4254
4255        fmin_options = locals().copy()  # archive original options
4256        del fmin_options['objective_function']
4257        del fmin_options['x0']
4258        del fmin_options['sigma0']
4259        del fmin_options['options']
4260        del fmin_options['args']
4261
4262        if options is None:
4263            options = cma_default_options
4264        CMAOptions().check_attributes(options)  # might modify options
4265        # checked that no options.ftarget =
4266        opts = CMAOptions(options.copy()).complement()
4267
4268        if callback is None:
4269            callback = []
4270        elif callable(callback):
4271            callback = [callback]
4272
4273        # BIPOP-related variables:
4274        runs_with_small = 0
4275        small_i = []
4276        large_i = []
4277        popsize0 = None  # to be evaluated after the first iteration
4278        maxiter0 = None  # to be evaluated after the first iteration
4279        base_evals = 0
4280
4281        irun = 0
4282        best = ot.BestSolution()
4283        all_stoppings = []
4284        while True:  # restart loop
4285            sigma_factor = 1
4286
4287            # Adjust the population according to BIPOP after a restart.
4288            if not bipop:
4289                # BIPOP not in use, simply double the previous population
4290                # on restart.
4291                if irun > 0:
4292                    popsize_multiplier = fmin_options['incpopsize']**(irun - runs_with_small)
4293                    opts['popsize'] = popsize0 * popsize_multiplier
4294
4295            elif irun == 0:
4296                # Initial run is with "normal" population size; it is
4297                # the large population before first doubling, but its
4298                # budget accounting is the same as in case of small
4299                # population.
4300                poptype = 'small'
4301
4302            elif sum(small_i) < sum(large_i):
4303                # An interweaved run with small population size
4304                poptype = 'small'
4305                if 11 < 3:  # not needed when compared to irun - runs_with_small
4306                    restarts += 1  # A small restart doesn't count in the total
4307                runs_with_small += 1  # _Before_ it's used in popsize_lastlarge
4308
4309                sigma_factor = 0.01**np.random.uniform()  # Local search
4310                popsize_multiplier = fmin_options['incpopsize']**(irun - runs_with_small)
4311                opts['popsize'] = np.floor(popsize0 * popsize_multiplier**(np.random.uniform()**2))
4312                opts['maxiter'] = min(maxiter0, 0.5 * sum(large_i) / opts['popsize'])
4313                # print('small basemul %s --> %s; maxiter %s' % (popsize_multiplier, opts['popsize'], opts['maxiter']))
4314
4315            else:
4316                # A run with large population size; the population
4317                # doubling is implicit with incpopsize.
4318                poptype = 'large'
4319
4320                popsize_multiplier = fmin_options['incpopsize']**(irun - runs_with_small)
4321                opts['popsize'] = popsize0 * popsize_multiplier
4322                opts['maxiter'] = maxiter0
4323                # print('large basemul %s --> %s; maxiter %s' % (popsize_multiplier, opts['popsize'], opts['maxiter']))
4324
4325            if not callable(objective_function) and callable(parallel_objective):
4326                objective_function = lambda x, *args: parallel_objective([x], *args)[0]
4327                objective_function.__doc__ = ('created from `parallel_objective`, '
4328                                              'assign to `None` to pickle')
4329
4330            # recover from a CMA object
4331            if irun == 0 and isinstance(x0, CMAEvolutionStrategy):
4332                es = x0
4333                x0 = es.inputargs['x0']  # for the next restarts
4334                if np.isscalar(sigma0) and np.isfinite(sigma0) and sigma0 > 0:
4335                    es.sigma = sigma0
4336                # debatable whether this makes sense:
4337                sigma0 = es.inputargs['sigma0']  # for the next restarts
4338                if options is not None:
4339                    es.opts.set(options)
4340                # ignore further input args and keep original options
4341            else:  # default case
4342                if irun and eval(str(fmin_options['restart_from_best'])):
4343                    utils.print_warning('CAVE: restart_from_best is often not useful',
4344                                        verbose=opts['verbose'])
4345                    es = CMAEvolutionStrategy(best.x, sigma_factor * sigma0, opts)
4346                else:
4347                    es = CMAEvolutionStrategy(x0, sigma_factor * sigma0, opts)
4348                # return opts, es
4349                if callable(objective_function) and (
4350                        eval_initial_x
4351                        or es.opts['CMA_elitist'] == 'initial'
4352                        or (es.opts['CMA_elitist'] and
4353                                    eval_initial_x is None)):
4354                    x = es.gp.pheno(es.mean,
4355                                    into_bounds=es.boundary_handler.repair,
4356                                    archive=es.sent_solutions)
4357                    es.f0 = objective_function(x, *args)
4358                    es.best.update([x], es.sent_solutions,
4359                                   [es.f0], 1)
4360                    es.countevals += 1
4361            es.objective_function = objective_function  # only for the record
4362
4363            opts = es.opts  # processed options, unambiguous
4364            # a hack:
4365            fmin_opts = CMAOptions("unchecked", **fmin_options.copy())
4366            for k in fmin_opts:
4367                # locals() cannot be modified directly, exec won't work
4368                # in 3.x, therefore
4369                fmin_opts.eval(k, loc={'N': es.N,
4370                                       'popsize': opts['popsize']},
4371                               correct_key=False)
4372
4373            es.logger.append = opts['verb_append'] or es.countiter > 0 or irun > 0
4374            # es.logger is "the same" logger, because the "identity"
4375            # is only determined by the `verb_filenameprefix` option
4376            logger = es.logger  # shortcut
4377            try:
4378                logger.persistent_communication_dict.update(
4379                    {'variable_annotations':
4380                    objective_function.variable_annotations})
4381            except AttributeError:
4382                pass
4383
4384            if 11 < 3:
4385                if es.countiter == 0 and es.opts['verb_log'] > 0 and \
4386                        not es.opts['verb_append']:
4387                   logger = CMADataLogger(es.opts['verb_filenameprefix']
4388                                            ).register(es)
4389                   logger.add()
4390                es.writeOutput()  # initial values for sigma etc
4391
4392            if noise_handler:
4393                if isinstance(noise_handler, type):
4394                    noisehandler = noise_handler(es.N)
4395                else:
4396                    noisehandler = noise_handler
4397                noise_handling = True
4398                if fmin_opts['noise_change_sigma_exponent'] > 0:
4399                    es.opts['tolfacupx'] = inf
4400            else:
4401                noisehandler = ot.NoiseHandler(es.N, 0)  # switched off
4402                noise_handling = False
4403            es.noise_handler = noisehandler
4404
4405            # the problem: this assumes that good solutions cannot take longer than bad ones:
4406            # with EvalInParallel(objective_function, 2, is_feasible=opts['is_feasible']) as eval_in_parallel:
4407            if 1 < 3:
4408                while not es.stop():  # iteration loop
4409                    # X, fit = eval_in_parallel(lambda: es.ask(1)[0], es.popsize, args, repetitions=noisehandler.evaluations-1)
4410                    X, fit = es.ask_and_eval(parallel_objective or objective_function,
4411                                             args, gradf=gradf,
4412                                             evaluations=noisehandler.evaluations,
4413                                             aggregation=np.median,
4414                                             parallel_mode=parallel_objective)  # treats NaN with resampling if not parallel_mode
4415                    # TODO: check args and in case use args=(noisehandler.evaluations, )
4416
4417                    if 11 < 3 and opts['vv']:  # inject a solution
4418                        # use option check_point = [0]
4419                        if 0 * np.random.randn() >= 0:
4420                            X[0] = 0 + opts['vv'] * es.sigma**0 * np.random.randn(es.N)
4421                            fit[0] = objective_function(X[0], *args)
4422                            # print fit[0]
4423                    if es.opts['verbose'] > 4:  # may be undesirable with dynamic fitness (e.g. Augmented Lagrangian)
4424                        if es.countiter < 2 or min(fit) <= es.best.last.f:
4425                            degrading_iterations_count = 0  # comes first to avoid code check complaint
4426                        else:  # min(fit) > es.best.last.f:
4427                            degrading_iterations_count += 1
4428                            if degrading_iterations_count > 4:
4429                                utils.print_message('%d f-degrading iterations (set verbose<=4 to suppress)'
4430                                                    % degrading_iterations_count,
4431                                                    iteration=es.countiter)
4432                    es.tell(X, fit)  # prepare for next iteration
4433                    if noise_handling:  # it would be better to also use these f-evaluations in tell
4434                        es.sigma *= noisehandler(X, fit, objective_function, es.ask,
4435                                                 args=args)**fmin_opts['noise_change_sigma_exponent']
4436
4437                        es.countevals += noisehandler.evaluations_just_done  # TODO: this is a hack, not important though
4438                        # es.more_to_write.append(noisehandler.evaluations_just_done)
4439                        if noisehandler.maxevals > noisehandler.minevals:
4440                            es.more_to_write.append(noisehandler.evaluations)
4441                        if 1 < 3:
4442                            # If sigma was above multiplied by the same
4443                            #  factor cmean is divided by here, this is
4444                            #  like only multiplying kappa instead of
4445                            #  changing cmean and sigma.
4446                            es.sp.cmean *= np.exp(-noise_kappa_exponent * np.tanh(noisehandler.noiseS))
4447                            es.sp.cmean[es.sp.cmean > 1] = 1.0  # also works with "scalar arrays" like np.array(1.2)
4448                    for f in callback:
4449                        f is None or f(es)
4450                    es.disp()
4451                    logger.add(# more_data=[noisehandler.evaluations, 10**noisehandler.noiseS] if noise_handling else [],
4452                               modulo=1 if es.stop() and logger.modulo else None)
4453                    if (opts['verb_log'] and opts['verb_plot'] and
4454                          (es.countiter % max(opts['verb_plot'], opts['verb_log']) == 0 or es.stop())):
4455                        logger.plot(324)
4456
4457            # end while not es.stop
4458            if opts['eval_final_mean'] and callable(objective_function):
4459                mean_pheno = es.gp.pheno(es.mean,
4460                                         into_bounds=es.boundary_handler.repair,
4461                                         archive=es.sent_solutions)
4462                fmean = objective_function(mean_pheno, *args)
4463                es.countevals += 1
4464                es.best.update([mean_pheno], es.sent_solutions, [fmean], es.countevals)
4465
4466            best.update(es.best, es.sent_solutions)  # in restarted case
4467            # es.best.update(best)
4468
4469            this_evals = es.countevals - base_evals
4470            base_evals = es.countevals
4471
4472            # BIPOP stats update
4473
4474            if irun == 0:
4475                popsize0 = opts['popsize']
4476                maxiter0 = opts['maxiter']
4477                # XXX: This might be a bug? Reproduced from Matlab
4478                # small_i.append(this_evals)
4479
4480            if bipop:
4481                if poptype == 'small':
4482                    small_i.append(this_evals)
4483                else:  # poptype == 'large'
4484                    large_i.append(this_evals)
4485
4486            # final message
4487            if opts['verb_disp']:
4488                es.result_pretty(irun, time.asctime(time.localtime()),
4489                                 best.f)
4490
4491            irun += 1
4492            # if irun > fmin_opts['restarts'] or 'ftarget' in es.stop() \
4493            # if irun > restarts or 'ftarget' in es.stop() \
4494            all_stoppings.append(dict(es.stop(check=False)))  # keeping the order
4495            if irun - runs_with_small > fmin_opts['restarts'] or 'ftarget' in es.stop() \
4496                    or 'maxfevals' in es.stop(check=False) or 'callback' in es.stop(check=False):
4497                break
4498            opts['verb_append'] = es.countevals
4499            opts['popsize'] = fmin_opts['incpopsize'] * es.sp.popsize  # TODO: use rather options?
4500            try:
4501                opts['seed'] += 1
4502            except TypeError:
4503                pass
4504
4505        # while irun
4506
4507        # es.out['best'] = best  # TODO: this is a rather suboptimal type for inspection in the shell
4508        if irun:
4509            es.best.update(best)
4510            # TODO: there should be a better way to communicate the overall best
4511        return es.result + (es.stop(), es, logger)
4512        ### 4560
4513        # TODO refine output, can #args be flexible?
4514        # is this well usable as it is now?
4515    else:  # except KeyboardInterrupt:  # Exception as e:
4516        if eval(str(options['verb_disp'])) > 0:
4517            print(' in/outcomment ``raise`` in last line of cma.fmin to prevent/restore KeyboardInterrupt exception')
4518        raise KeyboardInterrupt  # cave: swallowing this exception can silently mess up experiments, if ctrl-C is hit
4519
4520def no_constraints(x):
4521    return []
4522
4523def _al_set_logging(al, kwargs):
4524    def extract_logging_value(kwargs):
4525        if 'logging' in kwargs:
4526            return kwargs['logging']
4527        if 'verbose' in kwargs and kwargs['verbose'] <= -3:
4528            return False
4529        if 'options' in kwargs:
4530            ko = kwargs['options']
4531            if 'verb_log' in ko:
4532                return ko['verb_log']
4533            if 'verbose' in ko and ko['verbose'] <= -3:
4534                return False
4535    logging = extract_logging_value(kwargs)
4536    if logging is not None:
4537        al.logging = logging
4538
4539def fmin_con(objective_function, x0, sigma0,
4540             g=no_constraints, h=no_constraints, **kwargs):
4541    """optimize f with constraints g (inequalities) and h (equalities).
4542
4543    Construct an Augmented Lagrangian instance ``f_aug_lag`` of the type
4544    `cma.constraints_handler.AugmentedLagrangian` from `objective_function`
4545    and `g` and `h`.
4546
4547    Equality constraints should preferably be passed as two inequality
4548    constraints like ``[h - eps, -h - eps]``, with eps >= 0. When eps > 0,
4549    also feasible solution tracking can succeed.
4550
4551    Return a `tuple` ``es.results.xfavorite:numpy.array, es:CMAEvolutionStrategy``,
4552    where ``es == cma.fmin2(f_aug_lag, x0, sigma0, **kwargs)[1]``.
4553
4554    Depending on ``kwargs['logging']`` and on the verbosity settings in
4555    ``kwargs['options']``, the `AugmentedLagrangian` writes (hidden)
4556    logging files.
4557
4558    The second return value:`CMAEvolutionStrategy` has an (additional)
4559    attribute ``best_feasible`` which contains the information about the
4560    best feasible solution in the ``best_feasible.info`` dictionary, given
4561    any feasible solution was found. This only works with inequality
4562    constraints (equality constraints are wrongly interpreted as inequality
4563    constraints).
4564
4565    See `cma.fmin` for further parameters ``**kwargs``.
4566
4567    >>> import cma
4568    >>> x, es = cma.evolution_strategy.fmin_con(
4569    ...             cma.ff.sphere, 3 * [0], 1, g=lambda x: [1 - x[0]**2, -(1 - x[0]**2) - 1e-6],
4570    ...             options={'termination_callback': lambda es: -1e-5 < es.mean[0]**2 - 1 < 1e-5,
4571    ...                      'verbose':-9})
4572    >>> assert 'callback' in es.stop()
4573    >>> assert es.result.evaluations < 1500  # 10%-ish above 1000, 1%-ish above 1300
4574    >>> assert (sum(es.mean**2) - 1)**2 < 1e-9
4575
4576    >>> x, es = cma.evolution_strategy.fmin_con(
4577    ...             cma.ff.sphere, 2 * [0], 1, g=lambda x: [1 - x[0]**2],
4578    ...             options={'termination_callback': lambda es: -1e-5 < es.mean[0]**2 - 1 < 1e-5,
4579    ...                      'seed':1, 'verbose':-9})
4580    >>> es.best_feasible.f < 1 + 1e-5
4581    True
4582    >>> ".info attribute dictionary keys: {}".format(sorted(es.best_feasible.info))
4583    ".info attribute dictionary keys: ['f', 'g', 'g_al', 'x']"
4584
4585    Details: this is a versatile function subject to changes. It is possible to access
4586    the `AugmentedLagrangian` instance like
4587
4588    >>> al = es.augmented_lagrangian
4589    >>> isinstance(al, cma.constraints_handler.AugmentedLagrangian)
4590    True
4591    >>> # al.logger.plot()  # plots the evolution of AL coefficients
4592
4593    """
4594    # TODO: need to rethink equality/inequality interface?
4595
4596    if 'parallel_objective' in kwargs:
4597        raise ValueError("`parallel_objective` parameter is not supported by cma.fmin_con")
4598    # prepare callback list
4599    if callable(kwargs.setdefault('callback', [])):
4600        kwargs['callback'] = [kwargs['callback']]
4601
4602    global _al  # for debugging, may be removed at some point
4603    F = []
4604    G = []
4605    _al = AugmentedLagrangian(len(x0))
4606    _al_set_logging(_al, kwargs)
4607
4608    # _al.chi_domega = 1.1
4609    # _al.dgamma = 1.5
4610
4611    best_feasible_solution = ot.BestSolution2()
4612
4613    def f(x):
4614        F.append(objective_function(x))
4615        return F[-1]
4616    def constraints(x):
4617        gvals, hvals = g(x), h(x)
4618        # set m and equality attributes of al
4619        if _al.lam is None:  # TODO: better abide by an "official" interface?
4620            _al.set_m(len(gvals) + len(hvals))
4621            _al._equality = np.asarray(len(gvals) * [False] + len(hvals) * [True],
4622                                       dtype='bool')
4623        G.append(list(gvals) + list(hvals))
4624        return G[-1]
4625    def auglag(x):
4626        fval, gvals = f(x), constraints(x)
4627        alvals = _al(gvals)
4628        if all([gi <= 0 for gi in gvals]):
4629            best_feasible_solution.update(fval,
4630                info={'x':x, 'f': fval, 'g':gvals, 'g_al':alvals})
4631        return fval + sum(alvals)
4632    def set_coefficients(es):
4633        _al.set_coefficients(F, G)
4634        F[:], G[:] = [], []
4635    def update(es):
4636        x = es.ask(1, sigma_fac=0)[0]
4637        _al.update(f(x), constraints(x))
4638
4639    kwargs['callback'].extend([set_coefficients, update])
4640    # The smallest observed f-values may be below the limit value f(x^*_feas)
4641    # because f-values depend on the adaptive multipliers. Hence we overwrite
4642    # the default tolstagnation value:
4643    kwargs.setdefault('options', {}).setdefault('tolstagnation', 0)
4644    _, es = fmin2(auglag, x0, sigma0, **kwargs)
4645    es.objective_function_complements = [_al]
4646    es.augmented_lagrangian = _al
4647    es.best_feasible = best_feasible_solution
4648    return es.result.xfavorite, es
4649