Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 81 additions & 14 deletions src/hyperactive/opt/_adapters/_gfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# copyright: hyperactive developers, MIT License (see LICENSE file)

from skbase.utils.stdout_mute import StdoutMute

from collections.abc import Mapping, Sequence
from hyperactive.base import BaseOptimizer

__all__ = ["_BaseGFOadapter"]
Expand Down Expand Up @@ -102,14 +102,57 @@ def _to_dict_np(self, search_space):
"""
import numpy as np

def coerce_to_numpy(arr):
normalized = self._normalize_search_space(search_space)

def coerce_to_numpy(values, param_name):
"""Coerce a list or tuple to a numpy array."""
if not isinstance(arr, np.ndarray):
return np.array(arr)
arr = np.asarray(values)
if arr.ndim != 1:
raise ValueError(
"Search space values for parameter "
f"'{param_name}' must be 1-dimensional array-like; got "
f"shape {arr.shape}."
)
return arr

def convert(grid):
return {key: coerce_to_numpy(val, key) for key, val in grid.items()}

if isinstance(normalized, list):
return [convert(grid) for grid in normalized]

return convert(normalized)

def _normalize_search_space(self, search_space):

if search_space is None:
return None

if isinstance(search_space, Mapping):
return dict(search_space)

from sklearn.model_selection import ParameterGrid as parameter_grid

if isinstance(search_space, parameter_grid):

grids = [dict(grid) for grid in search_space.param_grid]

elif isinstance(search_space, Sequence) and not isinstance(search_space, (str,bytes)):

grids = [dict(grid) for grid in search_space]

else:

raise TypeError(
f"search space must be dict, list/tuple of dict, or sklearn.model_selection.ParameterGrid"
f"({type(search_space).__name__})."
)

if len(grids) == 1:
return grids[0]
else:
return grids

coerced_search_space = {k: coerce_to_numpy(v) for k, v in search_space.items()}
return coerced_search_space

def _solve(self, experiment, **search_config):
"""Run the optimization search process.
Expand All @@ -130,16 +173,40 @@ def _solve(self, experiment, **search_config):
n_iter = search_config.pop("n_iter", 100)
max_time = search_config.pop("max_time", None)

search_space = search_config.pop("search_space", None)

if isinstance(search_space, list):
search_spaces = search_space
else:
search_spaces = [search_space]

gfo_cls = self._get_gfo_class()
gfopt = gfo_cls(**search_config)

with StdoutMute(active=not self.verbose):
gfopt.search(
objective_function=experiment.score,
n_iter=n_iter,
max_time=max_time,
)
best_params = gfopt.best_para
best_score = None
best_params = None

for grid in search_spaces:
grid_config = dict(search_config)
grid_config["search_space"] = grid

gfopt = gfo_cls(**grid_config)

with StdoutMute(active=not self.verbose):
gfopt.search(
objective_function=experiment.score,
n_iter=n_iter,
max_time=max_time,
)

grid_params = gfopt.best_para
grid_score = getattr(gfopt, "best_score", None)
if grid_score is None and grid_params is not None:
grid_score = experiment.score(grid_params)[0]

if best_params is None or (grid_score is not None and grid_score > best_score):
best_params = grid_params
best_score = grid_score

return best_params

@classmethod
Expand Down