class documentation

CMA-ES stochastic optimizer class with ask-and-tell interface.

Calling Sequences

  • es = CMAEvolutionStrategy(x0, sigma0)

  • es = CMAEvolutionStrategy(x0, sigma0, opts)

  • es = CMAEvolutionStrategy(x0, sigma0).optimize(objective_fct)

  • res = CMAEvolutionStrategy(x0, sigma0,
                            opts).optimize(objective_fct).result
    

Arguments

x0

initial solution, starting point. x0 is given as "phenotype" which means, if:

opts = {'transformation': [transform, inverse]}

is given and inverse is None, the initial mean is not consistent with x0 in that transform(mean) does not equal to x0 unless transform(mean) equals mean.

sigma0
initial standard deviation. The problem variables should have been scaled, such that a single standard deviation on all variables is useful and the optimum is expected to lie within about x0 +- 3*sigma0. Often one wants to check for solutions close to the initial point. This allows, for example, for an easier check of consistency of the objective function and its interfacing with the optimizer. In this case, a much smaller sigma0 is advisable.
opts
options, a dictionary with optional settings, see class CMAOptions.

Main interface / usage

The interface is inherited from the generic OOOptimizer class (see also there). An object instance is generated from:

es = cma.CMAEvolutionStrategy(8 * [0.5], 0.2)

The least verbose interface is via the optimize method:

es.optimize(objective_func)
res = es.result

More verbosely, the optimization is done using the methods stop, ask, and tell:

while not es.stop():
    solutions = es.ask()
    es.tell(solutions, [cma.ff.rosen(s) for s in solutions])
    es.disp()
es.result_pretty()

where ask delivers new candidate solutions and tell updates the optim instance by passing the respective function values (the objective function cma.ff.rosen can be replaced by any properly defined objective function, see cma.ff for more examples).

To change an option, for example a termination condition to continue the optimization, call:

es.opts.set({'tolfacupx': 1e4})

The class CMAEvolutionStrategy also provides:

(solutions, func_values) = es.ask_and_eval(objective_func)

and an entire optimization can also be written like:

while not es.stop():
    es.tell(*es.ask_and_eval(objective_func))

Besides for termination criteria, in CMA-ES only the ranks of the func_values are relevant.

Attributes and Properties

  • inputargs: passed input arguments
  • inopts: passed options
  • opts: actually used options, some of them can be changed any time via opts.set, see class CMAOptions
  • popsize: population size lambda, number of candidate solutions returned by ask ()
  • logger: a CMADataLogger instance utilized by optimize

Examples

Super-short example, with output shown:

>>> import cma
>>> # construct an object instance in 4-D, sigma0=1:
>>> es = cma.CMAEvolutionStrategy(4 * [1], 1, {'seed':234})
...      # doctest: +ELLIPSIS
(4_w,8)-aCMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=234...)

and optimize the ellipsoid function

>>> es.optimize(cma.ff.elli, verb_disp=1)  # doctest: +ELLIPSIS
Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]
    1      8 2.09...
>>> assert len(es.result) == 8, es.result
>>> assert es.result[1] < 1e-9, es.result

The optimization loop can also be written explicitly:

>>> es = cma.CMAEvolutionStrategy(4 * [1], 1)  # doctest: +ELLIPSIS
(4_w,8)-aCMA-ES (mu_w=2.6,w_1=52%) in dimension 4 (seed=...
>>> while not es.stop():
...    X = es.ask()
...    es.tell(X, [cma.ff.elli(x) for x in X])
...    es.disp()  # doctest: +ELLIPSIS
Iterat #Fevals   function value  axis ratio  sigma  min&max std  t[m:s]
    1      8 ...

achieving the same result as above.

An example with lower bounds (at zero) and handling infeasible solutions:

>>> import numpy as np
>>> es = cma.CMAEvolutionStrategy(10 * [0.2], 0.5,
...         {'bounds': [0, np.inf]})  #doctest: +ELLIPSIS
(5_w,...
>>> while not es.stop():
...     fit, X = [], []
...     while len(X) < es.popsize:
...         curr_fit = None
...         while curr_fit in (None, np.nan):
...             x = es.ask(1)[0]
...             curr_fit = cma.ff.somenan(x, cma.ff.elli) # might return np.nan
...         X.append(x)
...         fit.append(curr_fit)
...     es.tell(X, fit)
...     es.logger.add()
...     es.disp()  #doctest: +ELLIPSIS
Itera...
>>>
>>> assert es.result[1] < 1e-9, es.result
>>> assert es.result[2] < 9000, es.result  # by internal termination
>>> # es.logger.plot()  # will plot data
>>> # cma.s.figshow()  # display plot window

An example with user-defined transformation, in this case to realize a lower bound of 2.

>>> import warnings
>>> with warnings.catch_warnings(record=True) as warns:
...     es = cma.CMAEvolutionStrategy(5 * [3], 0.1,
...                 {"transformation": [lambda x: x**2+1.2, None],
...                  "ftarget": 1e-7 + 5.54781521192,
...                  "verbose": -2,})
>>> warns[0].message  # doctest:+ELLIPSIS
UserWarning('in class GenoPheno: user defined transformations have not been tested thoroughly (...
>>> warns[1].message  # doctest:+ELLIPSIS
UserWarning('computed initial point...
>>> es.optimize(cma.ff.rosen, verb_disp=0)  #doctest: +ELLIPSIS
<cma...
>>> assert cma.ff.rosen(es.result[0]) < 1e-7 + 5.54781521192, es.result
>>> assert es.result[2] < 3300, es.result

The inverse transformation is (only) necessary if the BoundPenalty boundary handler is used at the same time.

The CMAEvolutionStrategy class also provides a default logger (cave: files are overwritten when the logger is used with the same filename prefix):

>>> es = cma.CMAEvolutionStrategy(4 * [0.2], 0.5, {'verb_disp': 0})
>>> es.logger.disp_header()  # annotate the print of disp
Iterat Nfevals  function value    axis ratio maxstd  minstd
>>> while not es.stop():
...     X = es.ask()
...     es.tell(X, [cma.ff.sphere(x) for x in X])
...     es.logger.add()  # log current iteration
...     es.logger.disp([-1])  # display info for last iteration   #doctest: +ELLIPSIS
    1  ...
>>> es.logger.disp_header()
Iterat Nfevals  function value    axis ratio maxstd  minstd
>>> # es.logger.plot() # will make a plot

Example implementing restarts with increasing popsize (IPOP):

>>> bestever = cma.optimization_tools.BestSolution()
>>> for lam in 10 * 2**np.arange(8):  # 10, 20, 40, 80, ..., 10 * 2**7
...     es = cma.CMAEvolutionStrategy(6 - 8 * np.random.rand(4),  # 4-D
...                                   5,  # initial std sigma0
...                                   {'popsize': lam,  # options
...                                    'verb_append': bestever.evalsall})
...     # logger = cma.CMADataLogger().register(es, append=bestever.evalsall)
...     while not es.stop():
...         X = es.ask()    # get list of new solutions
...         fit = [cma.ff.rastrigin(x) for x in X]  # evaluate each solution
...         es.tell(X, fit) # besides for termination only the ranking in fit is used
...
...         # display some output
...         # logger.add()  # add a "data point" to the log, writing in files
...         es.disp()  # uses option verb_disp with default 100
...
...     print('termination:', es.stop())
...     cma.s.pprint(es.best.__dict__)
...
...     bestever.update(es.best)
...
...     # show a plot
...     # logger.plot();
...     if bestever.f < 1e-8:  # global optimum was hit
...         break  #doctest: +ELLIPSIS
(5_w,...
>>> assert es.result[1] < 1e-8, es.result

On the Rastrigin function, usually after five restarts the global optimum is located.

Using the multiprocessing module, we can evaluate the function in parallel with a simple modification of the example (however multiprocessing seems not always reliable):

>>> from cma.fitness_functions import elli  # cannot be an instance method
>>> from cma.optimization_tools import EvalParallel2
>>> es = cma.CMAEvolutionStrategy(22 * [0.0], 1.0, {'maxiter':10})  # doctest:+ELLIPSIS
(6_w,13)-aCMA-ES (mu_w=...
>>> with EvalParallel2(elli, es.popsize + 1) as eval_all:
...     while not es.stop():
...         X = es.ask()
...         es.tell(X, eval_all(X))
...         es.disp()
...         # es.logger.add()  # doctest:+ELLIPSIS
Iterat...

The final example shows how to resume:

>>> import pickle
>>>
>>> es0 = cma.CMAEvolutionStrategy(12 * [0.1],  # a new instance, 12-D
...                                0.12)         # initial std sigma0
...   #doctest: +ELLIPSIS
(5_w,...
>>> es0.optimize(cma.ff.rosen, iterations=100)  #doctest: +ELLIPSIS
I...
>>> s = es0.pickle_dumps()  # return pickle.dumps(es) with safeguards
>>> # save string s to file like open(filename, 'wb').write(s)
>>> del es0  # let's start fresh
>>> # s = open(filename, 'rb').read()  # load string s from file
>>> es = pickle.loads(s)  # read back es instance from string
>>> # resuming
>>> es.optimize(cma.ff.rosen, verb_disp=200)  #doctest: +ELLIPSIS
  200 ...
>>> assert es.result[2] < 15000, es.result
>>> assert cma.s.Mh.vequals_approximately(es.result[0], 12 * [1], 1e-5), es.result
>>> assert len(es.result) == 8, es.result

Details

The following two enhancements are implemented, the latter is only turned on by default for very small population sizes.

Active CMA is implemented with option CMA_active and conducts an update of the covariance matrix with negative weights. The negative update is implemented, such that positive definiteness is guarantied. A typical speed up factor (number of f-evaluations) is between 1.1 and two.

References: Jastrebski and Arnold, Improving evolution strategies through active covariance matrix adaptation, CEC 2006. Hansen, The CMA evolution strategy: a tutorial, arXiv 2016.

Selective mirroring is implemented with option CMA_mirrors in the method get_mirror and get_selective_mirrors. The method ask_and_eval (used by fmin) will then sample selectively mirrored vectors within the iteration (CMA_mirrormethod==1). Otherwise, or if CMA_mirromethod==2, selective mirrors are injected for the next iteration. In selective mirroring, only the worst solutions are mirrored. With the default small number of mirrors, pairwise selection (where at most one of the two mirrors contribute to the update of the distribution mean) is implicitly guarantied under selective mirroring and therefore not explicitly implemented.

Update: pairwise selection for injected mirrors is also applied in the covariance matrix update: for all injected solutions, as for those from TPA, this is now implemented in that the recombination weights are constrained to be nonnegative for injected solutions in the covariance matrix (otherwise recombination weights are anyway nonnegative). This is a precaution to prevent failure when injected solutions are systematically bad (see e.g. https://github.com/CMA-ES/pycma/issues/124), but may not be "optimal" for mirrors.

References: Brockhoff et al, PPSN 2010, Auger et al, GECCO 2011.

See Also
fmin (), OOOptimizer, CMAOptions, plot (), ask (), tell (), ask_and_eval ()
Method __init__ see class CMAEvolutionStrategy
Method alleviate_conditioning pass conditioning of C to linear transformation in self.gp.
Method alleviate_conditioning_in_coordinates pass scaling from C to sigma_vec.
Method ask get/sample new candidate solutions.
Method ask_and_eval sample number solutions and evaluate them on func.
Method ask_geno get new candidate solutions in genotyp.
Method disp print current state variables in a single-line.
Method disp_annotation print annotation line for disp ()
Method feed_for_resume Resume a run using the solution history.
Method get_mirror return pheno(self.mean - (geno(x) - self.mean)).
Method get_selective_mirrors get mirror genotypic directions from worst solutions.
Method inject inject list of one or several genotypic solution(s).
Method limit_integer_relative_deltas versatile: limit absolute values of int-coordinates in vector list dX
Method mahalanobis_norm return Mahalanobis norm based on the current sample distribution.
Method manage_plateaus increase sigma by sigma_fac in case of a plateau.
Method pickle_dumps return pickle.dumps(self),
Method plot plot current state variables using matplotlib.
Method random_rescale_to_mahalanobis change x like for injection, all on genotypic level
Method repair_genotype make sure that solutions fit to the sample distribution.
Method result_pretty pretty print result.
Method stop return the termination status as dictionary.
Method tell pass objective function values to prepare for next iteration. This core procedure of the CMA-ES algorithm updates all state variables, in particular the two evolution paths, the distribution mean, the covariance matrix and a step-size.
Instance Variable adapt_sigma Undocumented
Instance Variable archive Undocumented
Instance Variable ary Undocumented
Instance Variable B Undocumented
Instance Variable best Undocumented
Instance Variable boundary_handler Undocumented
Instance Variable C Undocumented
Instance Variable callbackstop return values of callbacks, used like if any(callbackstop)
Instance Variable const Undocumented
Instance Variable count_eigen Undocumented
Instance Variable countevals Undocumented
Instance Variable countiter Undocumented
Instance Variable D Undocumented
Instance Variable dC Undocumented
Instance Variable evaluations_per_f_value Undocumented
Instance Variable fit Undocumented
Instance Variable gp Undocumented
Instance Variable hsiglist Undocumented
Instance Variable inopts Undocumented
Instance Variable inputargs Undocumented
Instance Variable integer_centering Undocumented
Instance Variable itereigenupdated Undocumented
Instance Variable last_iteration_with_gradient Undocumented
Instance Variable logger Undocumented
Instance Variable mean Undocumented
Instance Variable mean0 Undocumented
Instance Variable mean_after_tell Undocumented
Instance Variable mean_old Undocumented
Instance Variable mean_old_old Undocumented
Instance Variable mean_shift_samples Undocumented
Instance Variable mirrors_idx Undocumented
Instance Variable more_to_write Undocumented
Instance Variable N Undocumented
Instance Variable N_pheno Undocumented
Instance Variable noiseS Undocumented
Instance Variable number_of_injections Undocumented
Instance Variable number_of_injections_delivered Undocumented
Instance Variable number_of_solutions_asked Undocumented
Instance Variable opts Undocumented
Instance Variable pc Undocumented
Instance Variable pc2 Undocumented
Instance Variable pc_neg Undocumented
Instance Variable pop Undocumented
Instance Variable pop_injection_directions Undocumented
Instance Variable pop_injection_solutions Undocumented
Instance Variable pop_sorted Undocumented
Instance Variable sent_solutions Undocumented
Instance Variable sigma Undocumented
Instance Variable sigma0 Undocumented
Instance Variable sigma_vec Undocumented
Instance Variable sigma_vec0 Undocumented
Instance Variable sm Undocumented
Instance Variable sp Undocumented
Instance Variable sp0 Undocumented
Instance Variable time_last_displayed Undocumented
Instance Variable timer Undocumented
Instance Variable times_displayed Undocumented
Instance Variable x0 Undocumented
Property condition_number condition number of the statistical-model sampler.
Property isotropic_mean_shift normalized last mean shift, under random selection N(0,I)
Property popsize number of samples by default returned by ask ()
Property result return a CMAEvolutionStrategyResult namedtuple.
Property stds return array of coordinate-wise standard deviations (phenotypic).
Method _copy_light tentative copy of self, versatile (interface and functionalities may change).
Method _prepare_injection_directions provide genotypic directions for TPA and selective mirroring, with no specific length normalization, to be used in the coming iteration.
Method _random_rescaling_factor_to_mahalanobis_size self.mean + self._random_rescaling_factor_to_mahalanobis_size(y) * y is guarantied to appear like from the sample distribution.
Method _record_rankings do nothing by default, otherwise assign to _record_rankings_ after instantiation
Method _record_rankings_ compute ranks of vals in function_values and
Method _set_C_from set the current covariance matrix from another class instance.
Method _set_x0 Assign self.x0 from argument x0.
Method _stds_into_limits set self.sigma_vec.scaling to respect opts['max/minstd']
Method _tfg Undocumented
Method _tfp Undocumented
Method _try_update_sm_now call sm.update_now like sm.sample would do.
Method _updateBDfromSM helper function for a smooth transition to sampling classes.
Instance Variable _flgtelldone Undocumented
Instance Variable _indices_of_selective_mirrors Undocumented
Instance Variable _injected_solutions_archive Undocumented
Instance Variable _isotropic_mean_shift_iteration Undocumented
Instance Variable _mirrormethod1_done Undocumented
Instance Variable _recorded_rankings Undocumented
Instance Variable _sigma_old Undocumented
Instance Variable _stall_sigma_change_on_divergence_events Undocumented
Instance Variable _stopdict attribute for stopping criteria in function stop
Instance Variable _stoptolxstagnation Undocumented
Property _stds_geno return array of coordinate-wise standard deviations (genotypic).

Inherited from OOOptimizer:

Method initialize (re-)set to the initial state
Method optimize find minimizer of objective_fct.
Instance Variable more_mandatory_args Undocumented
Instance Variable optional_kwargs Undocumented
Instance Variable xcurrent Undocumented
Instance Variable xstart Undocumented
Method _force_final_logging try force the logger to log NOW
Method _prepare_callback_list return a list of callbacks including self.logger.add.
def __init__(self, x0, sigma0, inopts=None, options=None):

see class CMAEvolutionStrategy

options is for consistency with fmin2 options and is only in effect if inopts is None.

def alleviate_conditioning(self, condition=1000000000000.0):

pass conditioning of C to linear transformation in self.gp.

Argument condition defines the limit condition number above which the action is taken.

>>> import cma
>>> for dd in [0, 1]:
...     es = cma.CMA(2 * [1], 0.1, {'CMA_diagonal_decoding' : dd, 'verbose':-9})
...     es = es.optimize(cma.ff.elli, iterations=4)
...     es.alleviate_conditioning(1.01)  # check that alleviation_conditioning "works"
...     assert all(es.sigma_vec.scaling == [1, 1]), es.sigma_vec.scaling
...     assert es.sm.condition_number <= 1.01, es.sm.C

Details: the action applies only if self.gp.isidentity. Then, the covariance matrix C is set (back) to identity and a respective linear transformation is "added" to self.gp.

def alleviate_conditioning_in_coordinates(self, condition=100000000.0):

pass scaling from C to sigma_vec.

As a result, C is a correlation matrix, i.e., all diagonal entries of C are 1.

def ask(self, number=None, xmean=None, sigma_fac=1, gradf=None, args=(), **kwargs):

get/sample new candidate solutions.

Solutions are sampled from a multi-variate normal distribution and transformed to f-representation (phenotype) to be evaluated.

Arguments

number
number of returned solutions, by default the population size popsize (AKA lambda).
xmean
distribution mean, phenotyp?
sigma_fac
multiplier for internal sample width (standard deviation)
gradf
gradient, len(gradf(x)) == len(x), if gradf is not None the third solution in the returned list is "sampled" in supposedly Newton direction np.dot(C, gradf(xmean, *args)).
args
additional arguments passed to gradf

Return

A list of N-dimensional candidate solutions to be evaluated

Example

>>> import cma
>>> es = cma.CMAEvolutionStrategy([0,0,0,0], 0.3)  #doctest: +ELLIPSIS
(4_w,...
>>> while not es.stop() and es.best.f > 1e-6:
...     X = es.ask()  # get list of new solutions
...     fit = [cma.ff.rosen(x) for x in X]  # call fct with each solution
...     es.tell(X, fit)  # feed values
See Also
ask_and_eval, ask_geno, tell
def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma_fac=1, evaluations=1, aggregation=np.median, kappa=1, parallel_mode=False):

sample number solutions and evaluate them on func.

Each solution s is resampled until self.is_feasible(s, func(s)) is True.

Arguments

func:
objective function, func(x) accepts a numpy.ndarray and returns a scalar if not parallel_mode. Else returns a list of scalars from a list of numpy.ndarray.
args:
additional parameters for func
gradf:
gradient of objective function, g = gradf(x, *args) must satisfy len(g) == len(x)
number:
number of solutions to be sampled, by default population size popsize (AKA lambda)
xmean:
mean for sampling the solutions, by default self.mean.
sigma_fac:
multiplier for sampling width, standard deviation, for example to get a small perturbation of solution xmean
evaluations:
number of evaluations for each sampled solution
aggregation:
function that aggregates evaluations values to as single value.
kappa:
multiplier used for the evaluation of the solutions, in that func(m + kappa*(x - m)) is the f-value for x.

Return

(X, fit), where

  • X: list of solutions
  • fit: list of respective function values

Details

While not self.is_feasible(x, func(x)) new solutions are sampled. By default self.is_feasible == cma.feasible == lambda x, f: f not in (None, np.nan). The argument to func can be freely modified within func.

Depending on the CMA_mirrors option, some solutions are not sampled independently but as mirrors of other bad solutions. This is a simple derandomization that can save 10-30% of the evaluations in particular with small populations, for example on the cigar function.

Example

>>> import cma
>>> x0, sigma0 = 8 * [10], 1  # 8-D
>>> es = cma.CMAEvolutionStrategy(x0, sigma0)  #doctest: +ELLIPSIS
(5_w,...
>>> while not es.stop():
...     X, fit = es.ask_and_eval(cma.ff.elli)  # handles NaN with resampling
...     es.tell(X, fit)  # pass on fitness values
...     es.disp(20) # print every 20-th iteration  #doctest: +ELLIPSIS
Iterat #Fevals...
>>> print('terminated on ' + str(es.stop()))  #doctest: +ELLIPSIS
terminated on ...

A single iteration step can be expressed in one line, such that an entire optimization after initialization becomes:

while not es.stop():
    es.tell(*es.ask_and_eval(cma.ff.elli))
def ask_geno(self, number=None, xmean=None, sigma_fac=1):

get new candidate solutions in genotyp.

Solutions are sampled from a multi-variate normal distribution.

Arguments are
number
number of returned solutions, by default the population size popsize (AKA lambda).
xmean
distribution mean
sigma_fac
multiplier for internal sample width (standard deviation)

ask_geno returns a list of N-dimensional candidate solutions in genotyp representation and is called by ask.

Details: updates the sample distribution if needed and might change the geno-pheno transformation during this update.

See Also
ask, ask_and_eval
def disp(self, modulo=None, overwrite=None):

print current state variables in a single-line.

Prints only if iteration_counter % modulo == 0. Overwrites the line after iteration overwrite.

See Also
disp_annotation.
def disp_annotation(self):

print annotation line for disp ()

def feed_for_resume(self, X, function_values):

Resume a run using the solution history.

CAVEAT: this hasn't been thoroughly tested or in intensive use.

Given all "previous" candidate solutions and their respective function values, the state of a CMAEvolutionStrategy object can be reconstructed from this history. This is the purpose of function feed_for_resume.

Arguments

X:
(all) solution points in chronological order, phenotypic representation. The number of points must be a multiple of popsize.
function_values:
respective objective function values

Details

feed_for_resume can be called repeatedly with only parts of the history. The part must have the length of a multiple of the population size. feed_for_resume feeds the history in popsize-chunks into tell. The state of the random number generator might not be reconstructed, but this would be only relevant for the future.

Example

import cma

# prepare
(x0, sigma0) = ... # initial values from previous trial
X = ... # list of generated solutions from a previous trial
f = ... # respective list of f-values

# resume
es = cma.CMAEvolutionStrategy(x0, sigma0)
es.feed_for_resume(X, f)

# continue with func as objective function
while not es.stop():
    X = es.ask()
    es.tell(X, [func(x) for x in X])

Credits to Dirk Bueche and Fabrice Marchal for the feeding idea.

See Also
class CMAEvolutionStrategy for a simple dump/load to resume.
def get_mirror(self, x, preserve_length=False):

return pheno(self.mean - (geno(x) - self.mean)).

>>> import numpy as np, cma
>>> es = cma.CMAEvolutionStrategy(np.random.randn(3), 1)  #doctest: +ELLIPSIS
(3_w,...
>>> x = np.random.randn(3)
>>> assert cma.utilities.math.Mh.vequals_approximately(es.mean - (x - es.mean), es.get_mirror(x, preserve_length=True))
>>> x = es.ask(1)[0]
>>> vals = (es.get_mirror(x) - es.mean) / (x - es.mean)
>>> assert cma.utilities.math.Mh.equals_approximately(sum(vals), len(vals) * vals[0])

TODO: this implementation is yet experimental.

TODO: this implementation includes geno-pheno transformation, however in general GP-transformation should be separated from specific code.

Selectively mirrored sampling improves to a moderate extend but overadditively with active CMA for quite understandable reasons.

Optimal number of mirrors are suprisingly small: 1,2,3 for maxlam=7,13,20 where 3,6,10 are the respective maximal possible mirrors that must be clearly suboptimal.

def get_selective_mirrors(self, number=None):

get mirror genotypic directions from worst solutions.

Details:

To be called after the mean has been updated.

Takes the last number=sp.lam_mirr entries in the self.pop[self.fit.idx] as solutions to be mirrored.

Do not take a mirror if it is suspected to stem from a previous mirror in order to not go endlessly back and forth.

def inject(self, solutions, force=None):

inject list of one or several genotypic solution(s).

This is the preferable way to pass outside proposal solutions into CMAEvolutionStrategy. Passing (bad) solutions directly via tell is likely to fail when CMA_active is True as by default.

Unless force is True, the solutions are used as direction relative to the distribution mean to compute a new candidate solution returned in method ask_geno which in turn is used in method ask. Even when force is True, the update in tell takes later care of possibly trimming the update vector.

inject is to be called before ask or after tell and can be called repeatedly.

>>> import cma
>>> es = cma.CMAEvolutionStrategy(4 * [1], 2)  #doctest: +ELLIPSIS
(4_w,...
>>> while not es.stop():
...     es.inject([4 * [0.0]])
...     X = es.ask()
...     if es.countiter == 0:
...         assert X[0][0] == X[0][1]  # injected sol. is on the diagonal
...     es.tell(X, [cma.ff.sphere(x) for x in X])

Details: injected solutions are not used in the "active" update which would decrease variance in the covariance matrix in this direction.

def limit_integer_relative_deltas(self, dX, threshold=None, recombination_weight_condition=None):

versatile: limit absolute values of int-coordinates in vector list dX

relative to the current sample standard deviations and by default only when the respective recombination weight is negative.

This function is currently not in effect (called with threshold=inf) and not guarantied to stay as is.

dX == pop_sorted - mold where pop_sorted is a genotype.

threshold=2.3 by default.

A 2.3-sigma threshold affects 2 x 1.1% of the unmodified (nonsorted) normal samples.

def mahalanobis_norm(self, dx):

return Mahalanobis norm based on the current sample distribution.

The norm is based on Covariance matrix C times sigma**2, and includes sigma_vec. The expected Mahalanobis distance to the sample mean is about sqrt(dimension).

Argument

A genotype difference dx.

Example

>>> import cma, numpy
>>> es = cma.CMAEvolutionStrategy(numpy.ones(10), 1)  #doctest: +ELLIPSIS
(5_w,...
>>> xx = numpy.random.randn(2, 10)
>>> d = es.mahalanobis_norm(es.gp.geno(xx[0]-xx[1]))

d is the distance "in" the true sample distribution, sampled points have a typical distance of sqrt(2*es.N), where es.N is the dimension, and an expected distance of close to sqrt(N) to the sample mean. In the example, d is the Euclidean distance, because C = I and sigma = 1.

def manage_plateaus(self, sigma_fac=1.5, sample_fraction=0.5):

increase sigma by sigma_fac in case of a plateau.

A plateau is assumed to be present if the best sample and popsize * sample_fraction-th best sample have the same fitness.

Example:

>>> import cma
>>> def f(X):
...     return (len(X) - 1) * [1] + [2]
>>> es = cma.CMAEvolutionStrategy(4 * [0], 5, {'verbose':-9, 'tolflatfitness':1e4})
>>> while not es.stop():
...     X = es.ask()
...     es.tell(X, f(X))
...     es.manage_plateaus()
>>> if es.sigma < 1.5**es.countiter: print((es.sigma, 1.5**es.countiter, es.stop()))
def pickle_dumps(self):

return pickle.dumps(self),

if necessary remove unpickleable (and also unnecessary) local function reference beforehand.

The resulting bytes string-object can be saved to a file like:

import cma
es = cma.CMAEvolutionStrategy(3 * [1], 1)
es.optimize(cma.ff.elli, iterations=22)
filename = 'es-pickle-test'
open(filename, 'wb').write(es.pickle_dumps())

and recovered like:

import pickle
es = pickle.load(open(filename, 'rb'))

or:

es = pickle.loads(open(filename, 'rb').read())
es.optimize(cma.ff.elli, iterations=22)  # continue optimizing
def plot(self, *args, **kwargs):

plot current state variables using matplotlib.

Details: calls self.logger.plot.

def random_rescale_to_mahalanobis(self, x):

change x like for injection, all on genotypic level

def repair_genotype(self, x, copy_if_changed=False):

make sure that solutions fit to the sample distribution.

This interface is versatile and likely to change.

The Mahalanobis distance x - self.mean is clipping at N**0.5 + 2 * N / (N + 2), but the specific repair mechanism may change in future.

def result_pretty(self, number_of_runs=0, time_str=None, fbestever=None):

pretty print result.

Returns result of self.

def stop(self, check=True, ignore_list=(), check_in_same_iteration=False, get_value=None):

return the termination status as dictionary.

With check == False, the termination conditions are not checked and the status might not reflect the current situation. check_on_same_iteration == False (new) does not re-check during the same iteration. When termination options are manually changed, it must be set to True to advance afterwards. stop().clear() removes the currently active termination conditions.

As a convenience feature, keywords in ignore_list are removed from the conditions.

If get_value is set to a condition name (not the empty string), stop does not update the termination dictionary but returns the measured value that would be compared to the threshold. This only works for some conditions, like 'tolx'. If the condition name is not known or cannot be computed, None is returned and no warning is issued.

Testing get_value functionality:

>>> import cma
>>> es = cma.CMAEvolutionStrategy(2 * [1], 1e4, {'verbose': -9})
>>> with warnings.catch_warnings(record=True) as w:
...     es.stop(get_value='tolx')  # triggers zero iteration warning
...     assert len(w) == 1, [str(wi) for wi in w]
>>> es = es.optimize(cma.ff.sphere, iterations=4)
>>> assert 1e3 < es.stop(get_value='tolx') < 1e4, es.stop(get_value='tolx')
>>> assert es.stop() == {}
>>> assert es.stop(get_value='catch 22') is None
def tell(self, solutions, function_values, check_points=None, copy=False):

pass objective function values to prepare for next iteration. This core procedure of the CMA-ES algorithm updates all state variables, in particular the two evolution paths, the distribution mean, the covariance matrix and a step-size.

Arguments

solutions
list or array of candidate solution points (of type numpy.ndarray), most presumably before delivered by method ask() or ask_and_eval().
function_values
list or array of objective function values corresponding to the respective points. Beside for termination decisions, only the ranking of values in function_values is used.
check_points
If check_points is None, only solutions that are not generated by ask() are possibly clipped (recommended). False does not clip any solution (not recommended). If True, clips solutions that realize long steps (i.e. also those that are unlikely to be generated with ask()). check_points can be a list of indices to be checked in solutions.
copy
solutions can be modified in this routine, if copy is False

Details

tell() updates the parameters of the multivariate normal search distribution, namely covariance matrix and step-size and updates also the attributes countiter and countevals. To check the points for consistency is quadratic in the dimension (like sampling points).

Bugs

The effect of changing the solutions delivered by ask() depends on whether boundary handling is applied. With boundary handling, modifications are disregarded. This is necessary to apply the default boundary handling that uses unrepaired solutions but might change in future.

Example

>>> import cma
>>> func = cma.ff.sphere  # choose objective function
>>> es = cma.CMAEvolutionStrategy(np.random.rand(2) / 3, 1.5)
... # doctest:+ELLIPSIS
(3_...
>>> while not es.stop():
...    X = es.ask()
...    es.tell(X, [func(x) for x in X])
>>> es.result  # result is a `namedtuple` # doctest:+ELLIPSIS
CMAEvolutionStrategyResult(xbest=array([...
See Also
class CMAEvolutionStrategy, ask, ask_and_eval, fmin
adapt_sigma =

Undocumented

archive =

Undocumented

ary =

Undocumented

B =

Undocumented

best =

Undocumented

boundary_handler =

Undocumented

C =

Undocumented

callbackstop: tuple =

return values of callbacks, used like if any(callbackstop)

const =

Undocumented

count_eigen: int =

Undocumented

countevals =

Undocumented

countiter: int =

Undocumented

D =

Undocumented

dC =

Undocumented

evaluations_per_f_value =

Undocumented

fit =

Undocumented

gp =

Undocumented

hsiglist: list =

Undocumented

inopts =

Undocumented

inputargs =

Undocumented

integer_centering =

Undocumented

itereigenupdated =

Undocumented

last_iteration_with_gradient =

Undocumented

logger =

Undocumented

mean =

Undocumented

mean0 =

Undocumented

mean_after_tell =

Undocumented

mean_old =

Undocumented

mean_old_old =

Undocumented

mean_shift_samples =

Undocumented

mirrors_idx =

Undocumented

more_to_write =

Undocumented

N =

Undocumented

N_pheno =

Undocumented

noiseS: int =

Undocumented

number_of_injections =

Undocumented

number_of_injections_delivered: int =

Undocumented

number_of_solutions_asked: int =

Undocumented

opts =

Undocumented

pc =

Undocumented

pc2 =

Undocumented

pc_neg =

Undocumented

pop: list =

Undocumented

pop_injection_directions =

Undocumented

pop_injection_solutions: list =

Undocumented

pop_sorted =

Undocumented

sent_solutions =

Undocumented

sigma =

Undocumented

sigma0 =

Undocumented

sigma_vec =

Undocumented

sigma_vec0 =

Undocumented

sm =

Undocumented

sp =

Undocumented

sp0 =

Undocumented

time_last_displayed =

Undocumented

timer =

Undocumented

times_displayed: int =

Undocumented

x0 =

Undocumented

@property
condition_number =

condition number of the statistical-model sampler.

Details: neither encoding/decoding from sigma_vec-scaling nor gp-transformation are taken into account for this computation.

@property
isotropic_mean_shift =

normalized last mean shift, under random selection N(0,I)

distributed.

Caveat: while it is finite and close to sqrt(n) under random selection, the length of the normalized mean shift under systematic selection (e.g. on a linear function) tends to infinity for mueff -> infty. Hence it must be used with great care for large mueff.

@property
popsize =

number of samples by default returned by ask ()

@property
result =

return a CMAEvolutionStrategyResult namedtuple.

See Also
cma.evolution_strategy.CMAEvolutionStrategyResult or try help(...result) on the result property of an CMAEvolutionStrategy instance or on the CMAEvolutionStrategyResult instance itself.
@property
stds =

return array of coordinate-wise standard deviations (phenotypic).

Takes into account geno-phenotype transformation, step-size, diagonal decoding, and the covariance matrix. Only the latter three apply to self.mean.

def _copy_light(self, sigma=None, inopts=None):

tentative copy of self, versatile (interface and functionalities may change).

sigma overwrites the original initial sigma. inopts allows to overwrite any of the original options.

This copy may not work as expected depending on the used sampler.

Copy mean and sample distribution parameters and input options. Do not copy evolution paths, termination status or other state variables.

>>> import cma
>>> es = cma.CMAEvolutionStrategy(3 * [1], 0.1,
...          {'bounds':[0,9], 'verbose':-9}).optimize(cma.ff.elli, iterations=10)
>>> es2 = es._copy_light()
>>> assert es2.sigma == es.sigma
>>> assert not sum((es.sm.C - es2.sm.C).flat > 1e-12), (es.sm.C, es2.sm.C)
>>> assert not sum((es.sm.C - es2.sm.C).flat < -1e-12), (es.sm.C, es2.sm.C)
>>> es3 = es._copy_light(sigma=3)
>>> assert es3.sigma == es3.sigma0 == 3
>>> es.mean[0] = -11
>>> es4 = es._copy_light(inopts={'CMA_on': False})
>>> assert es4.sp.c1 == es4.sp.cmu == 0
>>> assert es.mean[0] == -11 and es4.mean[0] >= -4.5
def _prepare_injection_directions(self):

provide genotypic directions for TPA and selective mirroring, with no specific length normalization, to be used in the coming iteration.

Details: This method is called in the end of tell. The result is assigned to self.pop_injection_directions and used in ask_geno.

def _random_rescaling_factor_to_mahalanobis_size(self, y):

self.mean + self._random_rescaling_factor_to_mahalanobis_size(y) * y is guarantied to appear like from the sample distribution.

def _record_rankings(self, vals, function_values):

do nothing by default, otherwise assign to _record_rankings_ after instantiation

def _record_rankings_(self, vals, function_values):

compute ranks of vals in function_values and

in self.fit.fit and store the results in _recorded_rankings. The ranking differences between two solutions appear to be similar in the current and last iteration.

def _set_C_from(self, es, scaling=True):

set the current covariance matrix from another class instance.

If scaling, also set the diagonal decoding scaling from es.sigma_vec.

This method may not work as expected unless the default sampler is used.

def _set_x0(self, x0):

Assign self.x0 from argument x0.

Input x0 may be a callable or a list or numpy.ndarray of the desired length.

Below an artificial example is given, where calling x0 delivers in the first two calls dimension * [5] and in succeeding calls``dimension * [0.01]``. Only the initial value of 0.01 solves the Rastrigin function:

>>> import cma
>>> class X0:
...     def __init__(self, dimension):
...         self.irun = 0
...         self.dimension = dimension
...     def __call__(self):
...
...         self.irun += 1
...         return (self.dimension * [5] if self.irun < 3
...                 else self.dimension * [0.01])
>>> xopt, es = cma.fmin2(cma.ff.rastrigin, X0(3), 0.01,
...                      {'verbose':-9}, restarts=1)
>>> assert es.result.fbest > 1e-5
>>> xopt, es = cma.fmin2(cma.ff.rastrigin, X0(3), 0.01,
...                      {'verbose':-9}, restarts=2)
>>> assert es.result.fbest < 1e-5  # third run succeeds due to x0
def _stds_into_limits(self, warn=False):

set self.sigma_vec.scaling to respect opts['max/minstd']

def _tfg(self, x):

Undocumented

def _tfp(self, x):

Undocumented

def _try_update_sm_now(self):

call sm.update_now like sm.sample would do.

This avoids a bias when using _random_rescaling_factor_to_mahalanobis_size which was visible with TPA line samples.

def _updateBDfromSM(self, sm_=None):

helper function for a smooth transition to sampling classes.

By now all tests run through without this method in effect. Gradient injection and noeffectaxis however rely on the non-documented attributes B and D in the sampler.

_flgtelldone: bool =

Undocumented

_indices_of_selective_mirrors: list =

Undocumented

_injected_solutions_archive =

Undocumented

_isotropic_mean_shift_iteration: int =

Undocumented

_mirrormethod1_done =

Undocumented

_recorded_rankings =

Undocumented

_sigma_old =

Undocumented

_stall_sigma_change_on_divergence_events =

Undocumented

_stopdict =

attribute for stopping criteria in function stop

_stoptolxstagnation =

Undocumented

@property
_stds_geno =

return array of coordinate-wise standard deviations (genotypic).

Takes into account step-size, diagonal decoding, and the covariance matrix but not the geno-phenotype transformation. Only the former three apply to self.mean.