Repository: facebookresearch/nevergrad

Issue:
TypeError: cannot pickle 'module' object with CMA
## Steps to reproduce

  1. Run the relevant code

## Observed Results

```
0.4.2.post2
Traceback (most recent call last):
  File "../AppData/Roaming/JetBrains/PyCharmCE2020.2/scratches/scratch2.py", line 27, in <module>
    optimizer.dump(data_fn)
  File "..\venv\lib\site-packages\nevergrad\optimization\base.py", line 211, in dump
    pickle.dump(self, f)
TypeError: cannot pickle 'module' object
```


## Relevant Code

```python
import nevergrad as ng


def lossf(**x):
  a = x['a']
  b = x['b']
  return -a ** 2 + 1 + b


print(ng.__version__)


data_fn = 'opt_cma.dat'

arg = {'a': ng.p.Scalar(init=4, lower=1, upper=10).set_integer_casting(),
       'b': ng.p.Scalar(init=4, lower=2, upper=12).set_integer_casting()}

param = ng.p.Instrumentation(**arg)

optimizer = ng.optimizers.CMA(parametrization=param, budget=20)

for _ in range(optimizer.budget):
    x = optimizer.ask()
    loss = lossf(**x.kwargs)
    optimizer.tell(x, loss)

    optimizer.dump(data_fn)

print(f'best param: {optimizer.provide_recommendation().value[1]}')
```


<!--- In any case, don't hesitate to join and ask questions if you need on Nevergrad users Facebook group https://www.facebook.com/groups/nevergradusers/ -->



Discussion:

Resolving patch:
diff --git a/nevergrad/optimization/optimizerlib.py b/nevergrad/optimization/optimizerlib.py
--- a/nevergrad/optimization/optimizerlib.py
+++ b/nevergrad/optimization/optimizerlib.py
@@ -45,10 +45,10 @@ class _OnePlusOne(base.Optimizer):
     It was independently rediscovered by Devroye (1972) and Rechenberg (1973).
     We use asynchronous updates, so that the 1+1 can actually be parallel and even
     performs quite well in such a context - this is naturally close to 1+lambda.
-    
+
     Posssible mutations include gaussian and cauchy for the continuous case, and in the discrete case:
     discrete, fastga, doublefastga, adaptive, portfolio, discreteBSO, doerr.
-    
+
     Discrete is the most classical discrete mutation operator,
     DoubleFastGA is an adaptation of FastGA to arity > 2, Portfolio corresponds to random mutation rates,
     DiscreteBSO corresponds to a decreasing schedule of mutation rate.
@@ -307,7 +307,7 @@ def __init__(
     def es(self) -> tp.Any:  # typing not possible since cmaes not imported :(
         if self._es is None:
             if not self._fcmaes:
-                inopts = {"popsize": self._popsize, "randn": self._rng.randn, "CMA_diagonal": self._diagonal, "verbose": 0}
+                inopts = dict(popsize=self._popsize, randn=self._rng.randn, CMA_diagonal=self._diagonal, verbose=0, seed=np.nan)
                 self._es = cma.CMAEvolutionStrategy(x0=self._rng.normal(size=self.dimension) if self._random_init else np.zeros(
                     self.dimension, dtype=np.float), sigma0=self._scale, inopts=inopts)
             else:


Test patch:
diff --git a/nevergrad/optimization/test_optimizerlib.py b/nevergrad/optimization/test_optimizerlib.py
--- a/nevergrad/optimization/test_optimizerlib.py
+++ b/nevergrad/optimization/test_optimizerlib.py
@@ -333,17 +333,18 @@ def test_optimization_discrete_with_one_sample() -> None:
     optimizer.minimize(_square)
 
 
-@pytest.mark.parametrize("name", ["TBPSA", "PSO", "TwoPointsDE"])  # type: ignore
-# this test is added because some generic class can fail to be pickled
-def test_population_pickle(name: str) -> None:
+@pytest.mark.parametrize("name", ["TBPSA", "PSO", "TwoPointsDE", "CMA", "BO"])  # type: ignore
+def test_optim_pickle(name: str) -> None:
+    # some generic class can fail to be pickled:
     # example of work around:
     # "self.population = base.utils.Population[DEParticle]([])"
     # becomes:
     # "self.population: base.utils.Population[DEParticle] = base.utils.Population([])""
+    #
+    # Scipy optimizers also fail to be pickled, but this is more complex to solve (not supported yet)
     optim = registry[name](parametrization=12, budget=100, num_workers=2)
     with tempfile.TemporaryDirectory() as folder:
-        filepath = Path(folder) / "dump_test.pkl"
-        optim.dump(filepath)
+        optim.dump(Path(folder) / "dump_test.pkl")
 
 
 def test_bo_parametrization_and_parameters() -> None: