Repository: facebookresearch/nevergrad

Issue:
BO optimizer budget error
## Steps to reproduce

  1. pip install nevergrad
  2. run relevant code below in jupyter notebook

## Observed Results
```
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
~/.conda/envs/ifp_design/lib/python3.6/site-packages/nevergrad/optimization/recaster.py in run(self)
     87         try:
---> 88             self.output = self._caller(self._fake_callable, *self._args, **self._kwargs)
     89         except StopOptimizerThread:  # gracefully stopping the thread

~/.conda/envs/ifp_design/lib/python3.6/site-packages/nevergrad/optimization/recastlib.py in _optimization_function(self, objective_function)
    119             bo.explore(points_dict)
--> 120         assert budget is not None
    121         assert self.budget is not None

UnboundLocalError: local variable 'budget' referenced before assignment

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
<ipython-input-14-f517d0f57590> in <module>
----> 1 optimizerlib.BO(dimension=1).ask()

~/.conda/envs/ifp_design/lib/python3.6/site-packages/nevergrad/optimization/base.py in ask(self)
    150         This function can be called multiple times to explore several points in parallel
    151         """
--> 152         suggestion = self._internal_ask()
    153         assert suggestion is not None, f"{self.__class__.__name__}._internal_ask method returned None instead of a point."
    154         self._num_suggestions += 1

~/.conda/envs/ifp_design/lib/python3.6/site-packages/nevergrad/optimization/recaster.py in _internal_ask(self)
    200             warnings.warn("Underlying optimizer has already converged, returning random points",
    201                           FinishedUnderlyingOptimizerWarning)
--> 202             self._check_error()
    203             return np.random.normal(0, 1, self.dimension)
    204         message = messages[0]  # take oldest message

~/.conda/envs/ifp_design/lib/python3.6/site-packages/nevergrad/optimization/recaster.py in _check_error(self)
    209         if self._messaging_thread is not None:
    210             if self._messaging_thread.error is not None:
--> 211                 raise RuntimeError("Recast optimizer raised an error") from self._messaging_thread.error
    212 
    213     def _internal_tell(self, x: base.ArrayLike, value: float) -> None:

RuntimeError: Recast optimizer raised an error
```

## Expected Results

Return a 10-tuple of floats.

## Relevant Code

  ```
 import nevergrad.optimization.optimizerlib as optimizerlib
bo_optimizer = optimizerlib.BO(budget=20, dimension=10)
bo_optimizer.ask()
  ```





Discussion:

Resolving patch:
diff --git a/nevergrad/optimization/recastlib.py b/nevergrad/optimization/recastlib.py
--- a/nevergrad/optimization/recastlib.py
+++ b/nevergrad/optimization/recastlib.py
@@ -117,7 +117,6 @@ def my_obj(**kwargs: Any) -> float:
                 for j in range(self.dimension):
                     points_dict[str(j)].append(s[j])
             bo.explore(points_dict)
-        assert budget is not None
         assert self.budget is not None
         budget = self.budget - (budget if self.qr != "none" else 0)
         ip = 1 if self.qr == "none" else 0


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
@@ -78,10 +78,11 @@ def tearDownClass(cls) -> None:
         recom.iloc[:, 1:] = np.round(recom.iloc[:, 1:], 12)
         recom.to_csv(cls._RECOM_FILE)
 
-    @genty.genty_dataset(**{name: (name, optimizer,) for name, optimizer in registry.items() if "BO" not in name})  # type: ignore
+    @genty.genty_dataset(**{name: (name, optimizer,) for name, optimizer in registry.items()})  # type: ignore
     def test_optimizers(self, name: str, optimizer_cls: Type[base.Optimizer]) -> None:
-        verify = not optimizer_cls.one_shot and name not in SLOW and "Discrete" not in name
-        check_optimizer(optimizer_cls, budget=300, verify_value=verify)
+        verify = not optimizer_cls.one_shot and name not in SLOW and not any(x in name for x in ["BO", "Discrete"])
+        # BO is extremely slow, run it anyway but very low budget and no verification
+        check_optimizer(optimizer_cls, budget=2 if "BO" in name else 300, verify_value=verify)
 
     @genty.genty_dataset(**{name: (name, optimizer,) for name, optimizer in registry.items() if "BO" not in name})  # type: ignore
     def test_optimizers_recommendation(self, name: str, optimizer_cls: Type[base.Optimizer]) -> None: