Passed
Push — master ( c241e4...b050e9 )
by Simon
01:57
created

GridOptimizer._get_optimizer()   C

Complexity

Conditions 9

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 29
rs 6.6666
c 0
b 0
f 0
cc 9
nop 1
1
"""Grid optimizer."""
2
# copyright: hyperactive developers, MIT License (see LICENSE file)
3
4
from .._adapters._base_optuna_adapter import _BaseOptunaAdapter
5
6
7
class GridOptimizer(_BaseOptunaAdapter):
8
    """Grid search optimizer.
9
10
    Parameters
11
    ----------
12
    param_space : dict[str, tuple or list or optuna distributions]
13
        The search space to explore. Dictionary with parameter names
14
        as keys and either tuples/lists of (low, high) or
15
        optuna distribution objects as values.
16
    n_trials : int, default=100
17
        Number of optimization trials.
18
    initialize : dict[str, int], default=None
19
        The method to generate initial positions. A dictionary with
20
        the following key literals and the corresponding value type:
21
        {"grid": int, "vertices": int, "random": int, "warm_start": list[dict]}
22
    random_state : None, int, default=None
23
        If None, create a new random state. If int, create a new random state
24
        seeded with the value.
25
    early_stopping : int, default=None
26
        Number of trials after which to stop if no improvement.
27
    max_score : float, default=None
28
        Maximum score threshold. Stop optimization when reached.
29
    search_space : dict, default=None
30
        Explicit search space for grid search.
31
    experiment : BaseExperiment, optional
32
        The experiment to optimize parameters for.
33
        Optional, can be passed later via ``set_params``.
34
35
    Examples
36
    --------
37
    Basic usage of GridOptimizer with a scikit-learn experiment:
38
39
    >>> from hyperactive.experiment.integrations import SklearnCvExperiment
40
    >>> from hyperactive.opt.optuna import GridOptimizer
41
    >>> from sklearn.datasets import load_iris
42
    >>> from sklearn.svm import SVC
43
    >>> X, y = load_iris(return_X_y=True)
44
    >>> sklearn_exp = SklearnCvExperiment(estimator=SVC(), X=X, y=y)
45
    >>> param_space = {
46
    ...     "C": [0.01, 0.1, 1, 10],
47
    ...     "gamma": [0.0001, 0.01, 0.1, 1],
48
    ... }
49
    >>> optimizer = GridOptimizer(
50
    ...     param_space=param_space, n_trials=50, experiment=sklearn_exp
51
    ... )
52
    >>> best_params = optimizer.run()
53
    """
54
55
    _tags = {
56
        "info:name": "Grid Optimizer",
57
        "info:local_vs_global": "global",
58
        "info:explore_vs_exploit": "explore",
59
        "info:compute": "low",
60
        "python_dependencies": ["optuna"],
61
    }
62
63
    def __init__(
64
        self,
65
        param_space=None,
66
        n_trials=100,
67
        initialize=None,
68
        random_state=None,
69
        early_stopping=None,
70
        max_score=None,
71
        search_space=None,
72
        experiment=None,
73
    ):
74
        self.search_space = search_space
75
76
        super().__init__(
77
            param_space=param_space,
78
            n_trials=n_trials,
79
            initialize=initialize,
80
            random_state=random_state,
81
            early_stopping=early_stopping,
82
            max_score=max_score,
83
            experiment=experiment,
84
        )
85
86
    def _get_optimizer(self):
87
        """Get the Grid optimizer.
88
89
        Returns
90
        -------
91
        optimizer
92
            The Optuna GridOptimizer instance
93
        """
94
        import optuna
95
96
        # Convert param_space to Optuna search space format if needed
97
        search_space = self.search_space
98
        if search_space is None and self.param_space is not None:
99
            search_space = {}
100
            for key, space in self.param_space.items():
101
                if isinstance(space, list):
102
                    search_space[key] = space
103
                elif isinstance(space, (tuple,)) and len(space) == 2:
104
                    # Convert range to discrete list for grid search
105
                    low, high = space
106
                    if isinstance(low, int) and isinstance(high, int):
107
                        search_space[key] = list(range(low, high + 1))
108
                    else:
109
                        # Create a reasonable grid for continuous spaces
110
                        import numpy as np
111
112
                        search_space[key] = np.linspace(low, high, 10).tolist()
113
114
        return optuna.samplers.GridSampler(search_space)
115
116
    @classmethod
117
    def get_test_params(cls, parameter_set="default"):
118
        """Return testing parameter settings for the optimizer."""
119
        from sklearn.datasets import load_iris
120
        from sklearn.neighbors import KNeighborsClassifier
121
        from sklearn.svm import SVC
122
123
        from hyperactive.experiment.integrations import SklearnCvExperiment
124
125
        X, y = load_iris(return_X_y=True)
126
127
        # Test case 1: Basic continuous parameters (converted to discrete)
128
        svm_exp = SklearnCvExperiment(estimator=SVC(), X=X, y=y)
129
        param_space_1 = {
130
            "C": [0.01, 0.1, 1, 10],
131
            "gamma": [0.0001, 0.01, 0.1, 1],
132
        }
133
134
        # Test case 2: Mixed categorical and discrete parameters
135
        knn_exp = SklearnCvExperiment(estimator=KNeighborsClassifier(), X=X, y=y)
136
        param_space_2 = {
137
            "n_neighbors": [1, 3, 5, 7],  # Discrete integers
138
            "weights": ["uniform", "distance"],  # Categorical
139
            "metric": ["euclidean", "manhattan"],  # Categorical
140
            "p": [1, 2],  # Discrete for minkowski
141
        }
142
143
        # Test case 3: Small exhaustive grid (tests complete enumeration)
144
        param_space_3 = {
145
            "C": [0.1, 1],  # 2 values
146
            "kernel": ["rbf", "linear"],  # 2 values
147
        }
148
        # Total: 2 x 2 = 4 combinations, n_trials should cover all
149
150
        return [
151
            {
152
                "param_space": param_space_1,
153
                "n_trials": 10,
154
                "experiment": svm_exp,
155
            },
156
            {
157
                "param_space": param_space_2,
158
                "n_trials": 15,
159
                "experiment": knn_exp,
160
            },
161
            {
162
                "param_space": param_space_3,
163
                "n_trials": 4,  # Exact number for exhaustive search
164
                "experiment": svm_exp,
165
            },
166
        ]
167