Repository: optuna/optuna

Issue:
trial.suggest_int() ignores step
I used `trial.suggest_int()` with step. But, `suggest_int()` ignores arguments when study has trials with different distributions.

## Environment

- Optuna version: 2.6.0 
- Python version: 3.7.10
- OS: (I used Google Colab)
- (Optional) Other libraries and their versions:

## Error messages, stack traces, or logs

```
# error messages, stack traces, or logs
```

## Steps to reproduce

1. run reproducible example on Google Colab

## Reproducible examples (optional)

Bug case:
```python
import optuna

sampler = optuna.samplers.TPESampler(multivariate=True, n_startup_trials=10)
study = optuna.create_study(direction='maximize', sampler=sampler)

test_int_dist = optuna.distributions.IntUniformDistribution(0, 100) # no step
for v in range(100):
  trial = optuna.trial.create_trial(
          params={'test-int':v},
          distributions={'test-int': test_int_dist},
          value=0)
  study.add_trial(trial)

trial = study.ask()
trial.suggest_int('test-int', low=0, high=100, step=5) # specific step
```

the output is this:
![image](https://user-images.githubusercontent.com/261656/113367103-1f091b00-9396-11eb-8d80-4a7dba851d0f.png)

correct case:
```python
import optuna

sampler = optuna.samplers.TPESampler(multivariate=True, n_startup_trials=10)
study = optuna.create_study(direction='maximize', sampler=sampler)

test_int_dist = optuna.distributions.IntUniformDistribution(0, 100, step=5) 
for v in range(100):
  trial = optuna.trial.create_trial(
          params={'test-int':v},
          distributions={'test-int': test_int_dist},
          value=0)
  study.add_trial(trial)

trial = study.ask()
trial.suggest_int('test-int', low=0, high=100, step=5) # specific step
```

## Additional context (optional)

<!-- Please add any other context or screenshots about the problem here. -->



Discussion:
Thank you for your investigation! It seems we need a more detailed checking in `check_distribution_compatibility`.
https://github.com/optuna/optuna/blob/c228f0a5011579b21449b06f4b7ba308090fb515/optuna/distributions.py#L503-L534
Based from this,

```python
test_int_dist = optuna.distributions.IntUniformDistribution(0, 100) # no step
for v in range(100):
  trial = optuna.trial.create_trial(
          params={'test-int':v},
          distributions={'test-int': test_int_dist},
          value=0)
  study.add_trial(trial)
```
the params `test-int` can have a value of 8. Later the step is changed to 5. Now 8 - 5 = 3. So 3 is just fine because 8 is in the trial history and the optimizer considers the value of 8 in its calculation.


This issue has not seen any recent activity.

Resolving patch:
diff --git a/optuna/distributions.py b/optuna/distributions.py
--- a/optuna/distributions.py
+++ b/optuna/distributions.py
@@ -228,7 +228,8 @@ def single(self) -> bool:
     def _contains(self, param_value_in_internal_repr: float) -> bool:
 
         value = param_value_in_internal_repr
-        return self.low <= value <= self.high
+        k = (value - self.low) / self.q
+        return self.low <= value <= self.high and abs(k - round(k)) < 1.0e-8
 
 
 class IntUniformDistribution(BaseDistribution):
@@ -291,7 +292,7 @@ def single(self) -> bool:
     def _contains(self, param_value_in_internal_repr: float) -> bool:
 
         value = param_value_in_internal_repr
-        return self.low <= value <= self.high
+        return self.low <= value <= self.high and (value - self.low) % self.step == 0
 
 
 class IntLogUniformDistribution(BaseDistribution):
@@ -374,7 +375,7 @@ def single(self) -> bool:
 
     def _contains(self, param_value_in_internal_repr: float) -> bool:
         value = param_value_in_internal_repr
-        return self.low <= value <= self.high
+        return self.low <= value <= self.high and (value - self.low) % self.step == 0
 
     @property
     def step(self) -> int:


Test patch:
diff --git a/tests/test_distributions.py b/tests/test_distributions.py
--- a/tests/test_distributions.py
+++ b/tests/test_distributions.py
@@ -126,8 +126,8 @@ def test_contains() -> None:
         du = distributions.DiscreteUniformDistribution(low=1.0, high=10.0, q=2.0)
     assert not du._contains(0.9)
     assert du._contains(1.0)
-    assert du._contains(3.5)
-    assert du._contains(6)
+    assert not du._contains(3.5)
+    assert not du._contains(6)
     assert du._contains(9)
     assert not du._contains(9.1)
     assert not du._contains(10)
@@ -149,8 +149,8 @@ def test_contains() -> None:
         iuq = distributions.IntUniformDistribution(low=1, high=10, step=2)
     assert not iuq._contains(0.9)
     assert iuq._contains(1)
-    assert iuq._contains(4)
-    assert iuq._contains(6)
+    assert not iuq._contains(4)
+    assert not iuq._contains(6)
     assert iuq._contains(9)
     assert not iuq._contains(9.1)
     assert not iuq._contains(10)