CMA-ES / pycma

Python implementation of CMA-ES
Other
1.1k stars 178 forks source link

Warm Covariance ? #255

Closed ivarbratberg closed 3 months ago

ivarbratberg commented 3 months ago

Hi, the context is the following. I have optimized the system already. Then the system has changed slightly and I would like to optimize again

In this context, it would be nice not only to provide the initial values where to start the search and the sigma, but also the covariance matrix.

Would it be possible to provide a initial covariance parameter ?

nikohansen commented 3 months ago

The _copy_light method seems to be what you are looking for.

import cma

cma.CMAEvolutionStrategy._copy_light??
Signature: cma.CMA._copy_light(self, sigma=None, inopts=None)
Source:   
    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 sum((es.sm.C - es2.sm.C).flat < 1e-12)
        >>> 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

        """
        if sigma is None:
            sigma = self.sigma
        opts = dict(self.inopts)
        if inopts is not None:
            opts.update(inopts)
        es = type(self)(self.gp.pheno(self.mean[:], into_bounds=self.boundary_handler.repair),
                        sigma, opts)
        es.sigma_vec = transformations.DiagonalDecoding(self.sigma_vec.scaling)
        try: es.sm.C = self.sm.C.copy()
        except: warnings.warn("self.sm.C.copy failed")
        es.sm.update_now(-1)  # make B and D consistent with C
        es._updateBDfromSM()
        return es

In case you use the functional interface: the second return argument of cma.fmin2 is the cma.CMAEvolutionStrategy class instance to be used and the second argument to fmin2 can be the light-copied class instance.

nikohansen commented 3 months ago

You may also just directly use these few lines given es is the previous class instance:

es2 = cma.CMAEvolutionStrategy(...)
es2.sigma_vec = cma.transformations.DiagonalDecoding(es.sigma_vec.scaling)
es2.sm.C = es.sm.C.copy()
es2.sm.update_now(-1)  # make B and D consistent with C
es2._updateBDfromSM()

It seems like a good idea if this would work (it doesn't currently) by executing the above lines:

es2 = cma.CMAEvolutionStrategy(...)
es2._set_C_from(es)

EDIT: this works now with the current code.

ivarbratberg commented 3 months ago

Great thank you , I will check this out and give feedback.

nikohansen commented 3 months ago

Using this class would allow the above two liner to work [update: works now without the below class]:

class CMAEvolutionStrategyWithSetCovarianceFrom(cma.CMAEvolutionStrategy):
    def _set_C_from(self, es):
        """set the current covariance matrix from another class instance.

        This may not work as expected unless the default sampler is used.
        """
        if es.N != self.N:
            warnings.warn("setting C with dimension {} != {} (the current "
                          "dimension). This is likely to fail."
                          .format(es.N, self.N))
        self.sigma_vec = transformations.DiagonalDecoding(es.sigma_vec.scaling)
        try:
            self.sm.C = es.sm.C.copy()
        except Exception:
            warnings.warn("es.sm.C.copy() failed")
        self.sm.update_now(-1)  # make B and D consistent with C
        self._updateBDfromSM()
ivarbratberg commented 3 months ago

Ok thanks!

nikohansen commented 3 months ago

Can this be closed?

ivarbratberg commented 3 months ago

Yes, thank you a lot for your help. I have tested it out, and it works as expected, albeit for many cases I work with it seems like starting with previous found best optimum point and diagonal covariance matrix gives just as good progress

jpiabrantes commented 2 weeks ago

@nikohansen any way to also copy the evolution paths? I think in the OP's use-case it would be useful to continue training with a checkpoint of the last evolution paths.

nikohansen commented 2 weeks ago

Evolution paths can be found in the attributes ps, pc and pc2.