class CMAEvolutionStrategy(interfaces.OOOptimizer):
Constructor: CMAEvolutionStrategy(x0, sigma0, inopts, options)
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 tox0
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 smallersigma0
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 argumentsinopts
: passed optionsopts
: actually used options, some of them can be changed any time via opts.set, see classCMAOptions
popsize
: population size lambda, number of candidate solutions returned byask
()logger
: aCMADataLogger
instance utilized byoptimize
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 |
pass conditioning of C to linear transformation in self.gp . |
Method | alleviate |
pass scaling from C to sigma_vec . |
Method | ask |
get/sample new candidate solutions. |
Method | ask |
sample number solutions and evaluate them on func . |
Method | ask |
get new candidate solutions in genotyp. |
Method | disp |
print current state variables in a single-line. |
Method | disp |
print annotation line for disp () |
Method | feed |
Resume a run using the solution history. |
Method | get |
return pheno(self.mean - (geno(x) - self.mean)). |
Method | get |
get mirror genotypic directions from worst solutions. |
Method | inject |
inject list of one or several genotypic solution(s). |
Method | limit |
versatile: limit absolute values of int-coordinates in vector list dX |
Method | mahalanobis |
return Mahalanobis norm based on the current sample distribution. |
Method | manage |
increase sigma by sigma_fac in case of a plateau. |
Method | pickle |
return pickle.dumps(self), |
Method | plot |
plot current state variables using matplotlib . |
Method | random |
change x like for injection, all on genotypic level |
Method | repair |
make sure that solutions fit to the sample distribution. |
Method | result |
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 |
Undocumented |
Instance Variable | archive |
Undocumented |
Instance Variable | ary |
Undocumented |
Instance Variable | B |
Undocumented |
Instance Variable | best |
Undocumented |
Instance Variable | boundary |
Undocumented |
Instance Variable | C |
Undocumented |
Instance Variable | callbackstop |
return values of callbacks, used like if any(callbackstop) |
Instance Variable | const |
Undocumented |
Instance Variable | count |
Undocumented |
Instance Variable | countevals |
Undocumented |
Instance Variable | countiter |
Undocumented |
Instance Variable | D |
Undocumented |
Instance Variable | d |
Undocumented |
Instance Variable | evaluations |
Undocumented |
Instance Variable | fit |
Undocumented |
Instance Variable | gp |
Undocumented |
Instance Variable | hsiglist |
Undocumented |
Instance Variable | inopts |
Undocumented |
Instance Variable | inputargs |
Undocumented |
Instance Variable | integer |
Undocumented |
Instance Variable | itereigenupdated |
Undocumented |
Instance Variable | last |
Undocumented |
Instance Variable | logger |
Undocumented |
Instance Variable | mean |
Undocumented |
Instance Variable | mean0 |
Undocumented |
Instance Variable | mean |
Undocumented |
Instance Variable | mean |
Undocumented |
Instance Variable | mean |
Undocumented |
Instance Variable | mean |
Undocumented |
Instance Variable | mirrors |
Undocumented |
Instance Variable | more |
Undocumented |
Instance Variable | N |
Undocumented |
Instance Variable |
|
Undocumented |
Instance Variable | noise |
Undocumented |
Instance Variable | number |
Undocumented |
Instance Variable | number |
Undocumented |
Instance Variable | number |
Undocumented |
Instance Variable | opts |
Undocumented |
Instance Variable | pc |
Undocumented |
Instance Variable | pc2 |
Undocumented |
Instance Variable | pc |
Undocumented |
Instance Variable | pop |
Undocumented |
Instance Variable | pop |
Undocumented |
Instance Variable | pop |
Undocumented |
Instance Variable | pop |
Undocumented |
Instance Variable | sent |
Undocumented |
Instance Variable | sigma |
Undocumented |
Instance Variable | sigma0 |
Undocumented |
Instance Variable | sigma |
Undocumented |
Instance Variable | sigma |
Undocumented |
Instance Variable | sm |
Undocumented |
Instance Variable | sp |
Undocumented |
Instance Variable | sp0 |
Undocumented |
Instance Variable | time |
Undocumented |
Instance Variable | timer |
Undocumented |
Instance Variable | times |
Undocumented |
Instance Variable | x0 |
Undocumented |
Property | condition |
condition number of the statistical-model sampler. |
Property | isotropic |
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 |
tentative copy of self, versatile (interface and functionalities may change). |
Method | _prepare |
provide genotypic directions for TPA and selective mirroring, with no specific length normalization, to be used in the coming iteration. |
Method | _random |
self.mean + self._random_rescaling_factor_to_mahalanobis_size(y) * y is guarantied to appear like from the sample distribution. |
Method | _record |
do nothing by default, otherwise assign to _record_rankings_ after instantiation |
Method | _record |
compute ranks of vals in function_values and |
Method | _set_ |
set the current covariance matrix from another class instance. |
Method | _set |
Assign self.x0 from argument x0 . |
Method | _stds |
set self.sigma_vec.scaling to respect opts['max/minstd'] |
Method | _tfg |
Undocumented |
Method | _tfp |
Undocumented |
Method | _try |
call sm.update_now like sm.sample would do. |
Method | _update |
helper function for a smooth transition to sampling classes. |
Instance Variable | _flgtelldone |
Undocumented |
Instance Variable | _indices |
Undocumented |
Instance Variable | _injected |
Undocumented |
Instance Variable | _isotropic |
Undocumented |
Instance Variable | _mirrormethod1 |
Undocumented |
Instance Variable | _recorded |
Undocumented |
Instance Variable | _sigma |
Undocumented |
Instance Variable | _stall |
Undocumented |
Instance Variable | _stopdict |
attribute for stopping criteria in function stop |
Instance Variable | _stoptolxstagnation |
Undocumented |
Property | _stds |
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 |
Undocumented |
Instance Variable | optional |
Undocumented |
Instance Variable | xcurrent |
Undocumented |
Instance Variable | xstart |
Undocumented |
Method | _force |
try force the logger to log NOW |
Method | _prepare |
return a list of callbacks including self.logger.add. |
cma.interfaces.OOOptimizer.__init__
see class CMAEvolutionStrategy
options
is for consistency with fmin2
options and is only
in effect if inopts is None.
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
.
cma.interfaces.OOOptimizer.ask
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 |
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 alist
of scalars from alist
ofnumpy.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.
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))
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 |
cma.interfaces.OOOptimizer.disp
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 . |
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. |
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.
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.
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.
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.
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.
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()))
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
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.
cma.interfaces.OOOptimizer.stop
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
cma.interfaces.OOOptimizer.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.
Arguments
solutions
- list or array of candidate solution points (of
type
numpy.ndarray
), most presumably before delivered by methodask()
orask_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 withask()
).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 |
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.
cma.interfaces.OOOptimizer.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. |
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
.
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
self.mean + self._random_rescaling_factor_to_mahalanobis_size(y) * y is guarantied to appear like from the sample distribution.
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.
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.
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
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.
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.