Repository: optuna/optuna

Issue:
sample_categorical does not work with RDBStorage
`sample_categorical` raises errors when storage is an RDBStorage, due to a cast problem.

Repro:
```python
from pfnopt import minimize
from pfnopt.storage import RDBStorage
from pfnopt.study import create_new_study


def objective(client):
    
    y = client.sample_categorical('y', (-1.0, 1.0))
    return y


if __name__ == '__main__':

    storage = RDBStorage('sqlite:///:memory:')
    study = create_new_study(storage)

    minimize(objective, storage=storage, n_trials=500)
```

Error messages:
```
Traceback (most recent call last):
  File "/Users/sano/PycharmProjects/pfnopt/repro.py", line 17, in <module>
    minimize(objective, storage=storage, n_trials=500)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 145, in minimize
    study.run(func, n_trials, timeout_seconds, n_jobs)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 66, in run
    self._run_sequential(func, n_trials, timeout_seconds)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 88, in _run_sequential
    result = func(client)
  File "/Users/sano/PycharmProjects/pfnopt/repro.py", line 8, in objective
    y = client.sample_categorical('y', (-1.0, 1.0))
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/client.py", line 33, in sample_categorical
    return self._sample(name, distributions.CategoricalDistribution(choices=choices))
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/client.py", line 90, in _sample
    self.storage, self.study_id, name, distribution)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/tpe.py", line 54, in sample
    param_distribution, below_param_values, above_param_values)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/tpe.py", line 80, in _sample_categorical
    upper=len(choices), size=(self.n_ei_candidates, ), rng=self.rng)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/_hyperopt.py", line 627, in sample_categorical
    counts_b = np.bincount(obs_below, minlength=upper, weights=weights_b)
TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'
```

This does not happen when `n_trials` is small (e.g., 5).
sample_categorical does not work with RDBStorage
`sample_categorical` raises errors when storage is an RDBStorage, due to a cast problem.

Repro:
```python
from pfnopt import minimize
from pfnopt.storage import RDBStorage
from pfnopt.study import create_new_study


def objective(client):
    
    y = client.sample_categorical('y', (-1.0, 1.0))
    return y


if __name__ == '__main__':

    storage = RDBStorage('sqlite:///:memory:')
    study = create_new_study(storage)

    minimize(objective, storage=storage, n_trials=500)
```

Error messages:
```
Traceback (most recent call last):
  File "/Users/sano/PycharmProjects/pfnopt/repro.py", line 17, in <module>
    minimize(objective, storage=storage, n_trials=500)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 145, in minimize
    study.run(func, n_trials, timeout_seconds, n_jobs)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 66, in run
    self._run_sequential(func, n_trials, timeout_seconds)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/study.py", line 88, in _run_sequential
    result = func(client)
  File "/Users/sano/PycharmProjects/pfnopt/repro.py", line 8, in objective
    y = client.sample_categorical('y', (-1.0, 1.0))
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/client.py", line 33, in sample_categorical
    return self._sample(name, distributions.CategoricalDistribution(choices=choices))
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/client.py", line 90, in _sample
    self.storage, self.study_id, name, distribution)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/tpe.py", line 54, in sample
    param_distribution, below_param_values, above_param_values)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/tpe.py", line 80, in _sample_categorical
    upper=len(choices), size=(self.n_ei_candidates, ), rng=self.rng)
  File "/Users/sano/PycharmProjects/pfnopt/pfnopt/samplers/_hyperopt.py", line 627, in sample_categorical
    counts_b = np.bincount(obs_below, minlength=upper, weights=weights_b)
TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'
```

This does not happen when `n_trials` is small (e.g., 5).


Discussion:

Resolving patch:
diff --git a/pfnopt/samplers/tpe.py b/pfnopt/samplers/tpe.py
--- a/pfnopt/samplers/tpe.py
+++ b/pfnopt/samplers/tpe.py
@@ -75,6 +75,8 @@ def _sample_loguniform(self, distribution, below, above):
     def _sample_categorical(self, distribution, below, above):
         # type: (distributions.CategoricalDistribution, List[float], List[float]) -> float
         choices = distribution.choices
+        below = list(map(int, below))
+        above = list(map(int, above))
         idx = _hyperopt.sample_categorical(
             obs_below=below, obs_above=above, prior_weight=self.prior_weight,
             upper=len(choices), size=(self.n_ei_candidates, ), rng=self.rng)


Test patch:
diff --git a/tests/test_study.py b/tests/test_study.py
--- a/tests/test_study.py
+++ b/tests/test_study.py
@@ -15,9 +15,9 @@
 from pfnopt import trial as trial_module
 
 
-def func(x, y):
-    # type: (float, float) -> float
-    return (x - 2) ** 2 + (y - 25) ** 2
+def func(x, y, z):
+    # type: (float, float, float) -> float
+    return (x - 2) ** 2 + (y - 25) ** 2 + z
 
 
 class Func(object):
@@ -38,19 +38,20 @@ def __call__(self, client):
             time.sleep(self.sleep_sec)
 
         x = client.sample_uniform('x', -10, 10)
-        y = client.sample_uniform('y', 20, 30)
-        return func(x, y)
+        y = client.sample_loguniform('y', 20, 30)
+        z = client.sample_categorical('z', (-1.0, 1.0))
+        return func(x, y, z)
 
 
 def check_params(params):
     # type: (Dict[str, Any]) -> None
-    assert sorted(params.keys()) == ['x', 'y']
+    assert sorted(params.keys()) == ['x', 'y', 'z']
 
 
 def check_value(value):
     # type: (float) -> None
     assert isinstance(value, float)
-    assert 0.0 <= value <= 12.0 ** 2 + 5.0 ** 2
+    assert -1.0 <= value <= 12.0 ** 2 + 5.0 ** 2 + 1.0
 
 
 def check_trial(trial):