Let's say I am searching over a dimension:
from skopt import space
search_space = [
space.Real(1, 10, name="my_scale")
]
How can I make this Real number to be searched with discrete steps? E.g. 0.25. Because in my case, calculating data for each real value can be cached and I know that fine-tuning the value with small steps like 0.00001 does not give meaningful improvements. However if the value is calculated 1.25, 1.50, 1.75, etc. I can cache the results and speed up the optimisation process a lot.
E.g. something like
search_space = [
space.Real(1, 10, step=0.25, name="my_scale")
]
You can make space.Real taking discrete steps creating a custom class that inherits from space.Real as follows:
from skopt import space
import numpy as np
class DiscreteReal(space.Real):
def __init__(self, low, high, step, name=None):
super().__init__(low, high, name=name)
self.step = step
self.values = np.arange(low, high + step, step)
def rvs(self, n_samples=1, random_state=None):
rng = random_state if random_state is not None else np.random.default_rng()
return rng.choice(self.values, size=n_samples)
def _transform(self, X):
return X
def _inverse_transform(self, Xt):
return Xt
def bounds(self):
return (self.low, self.high)
def __repr__(self):
return f"DiscreteReal(low={self.low}, high={self.high}, step={self.step}, name={self.name})"
The rvs
method generates random samples from the possible values.
The transform
and inverse_transform
methods are kept as identity functions since they are not needed for the discrete steps.
The bounds
method returns the bounds of the dimension.
The __repr__
method provides a string representation of the object.
This custom dimension can be used in the search space and ensures that the optimization process respects the discrete steps.