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

hyperactive.opt.optuna._nsga_ii_optimizer   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 159
Duplicated Lines 27.67 %

Importance

Changes 0
Metric Value
wmc 4
eloc 70
dl 44
loc 159
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A NSGAIIOptimizer.get_test_params() 44 44 1
A NSGAIIOptimizer.__init__() 0 25 1
A NSGAIIOptimizer._get_optimizer() 0 20 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
"""NSGA-II multi-objective optimizer."""
2
# copyright: hyperactive developers, MIT License (see LICENSE file)
3
4
from .._adapters._base_optuna_adapter import _BaseOptunaAdapter
5
6
7
class NSGAIIOptimizer(_BaseOptunaAdapter):
8
    """NSGA-II multi-objective 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
    population_size : int, default=50
30
        Population size for NSGA-II.
31
    mutation_prob : float, default=0.1
32
        Mutation probability for NSGA-II.
33
    crossover_prob : float, default=0.9
34
        Crossover probability for NSGA-II.
35
    experiment : BaseExperiment, optional
36
        The experiment to optimize parameters for.
37
        Optional, can be passed later via ``set_params``.
38
39
    Examples
40
    --------
41
    Basic usage of NSGAIIOptimizer with a scikit-learn experiment:
42
43
    >>> from hyperactive.experiment.integrations import SklearnCvExperiment
44
    >>> from hyperactive.opt.optuna import NSGAIIOptimizer
45
    >>> from sklearn.datasets import load_iris
46
    >>> from sklearn.svm import SVC
47
    >>> X, y = load_iris(return_X_y=True)
48
    >>> sklearn_exp = SklearnCvExperiment(estimator=SVC(), X=X, y=y)
49
    >>> param_space = {
50
    ...     "C": (0.01, 10),
51
    ...     "gamma": (0.0001, 10),
52
    ... }
53
    >>> optimizer = NSGAIIOptimizer(
54
    ...     param_space=param_space, n_trials=50, experiment=sklearn_exp
55
    ... )
56
    >>> best_params = optimizer.run()
57
    """
58
59
    _tags = {
60
        "info:name": "NSGA-II Optimizer",
61
        "info:local_vs_global": "global",
62
        "info:explore_vs_exploit": "mixed",
63
        "info:compute": "high",
64
        "python_dependencies": ["optuna"],
65
    }
66
67
    def __init__(
68
        self,
69
        param_space=None,
70
        n_trials=100,
71
        initialize=None,
72
        random_state=None,
73
        early_stopping=None,
74
        max_score=None,
75
        population_size=50,
76
        mutation_prob=0.1,
77
        crossover_prob=0.9,
78
        experiment=None,
79
    ):
80
        self.population_size = population_size
81
        self.mutation_prob = mutation_prob
82
        self.crossover_prob = crossover_prob
83
84
        super().__init__(
85
            param_space=param_space,
86
            n_trials=n_trials,
87
            initialize=initialize,
88
            random_state=random_state,
89
            early_stopping=early_stopping,
90
            max_score=max_score,
91
            experiment=experiment,
92
        )
93
94
    def _get_optimizer(self):
95
        """Get the NSGA-II optimizer.
96
97
        Returns
98
        -------
99
        optimizer
100
            The Optuna NSGAIIOptimizer instance
101
        """
102
        import optuna
103
104
        optimizer_kwargs = {
105
            "population_size": self.population_size,
106
            "mutation_prob": self.mutation_prob,
107
            "crossover_prob": self.crossover_prob,
108
        }
109
110
        if self.random_state is not None:
111
            optimizer_kwargs["seed"] = self.random_state
112
113
        return optuna.samplers.NSGAIISampler(**optimizer_kwargs)
114
115 View Code Duplication
    @classmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
116
    def get_test_params(cls, parameter_set="default"):
117
        """Return testing parameter settings for the optimizer."""
118
        from sklearn.datasets import load_iris
119
        from sklearn.ensemble import RandomForestClassifier
120
121
        from hyperactive.experiment.integrations import SklearnCvExperiment
122
123
        # Test case 1: Basic single-objective (inherits from base)
124
        params = super().get_test_params(parameter_set)
125
        params[0].update(
126
            {
127
                "population_size": 20,
128
                "mutation_prob": 0.2,
129
                "crossover_prob": 0.8,
130
            }
131
        )
132
133
        # Test case 2: Multi-objective with mixed parameter types
134
        X, y = load_iris(return_X_y=True)
135
        rf_exp = SklearnCvExperiment(
136
            estimator=RandomForestClassifier(random_state=42), X=X, y=y
137
        )
138
139
        mixed_param_space = {
140
            "n_estimators": (10, 50),  # Continuous integer
141
            "max_depth": [3, 5, 7, None],  # Mixed discrete/None
142
            "criterion": ["gini", "entropy"],  # Categorical
143
            "min_samples_split": (2, 10),  # Continuous integer
144
            "bootstrap": [True, False],  # Boolean categorical
145
        }
146
147
        params.append(
148
            {
149
                "param_space": mixed_param_space,
150
                "n_trials": 15,  # Smaller for faster testing
151
                "experiment": rf_exp,
152
                "population_size": 8,  # Smaller population for testing
153
                "mutation_prob": 0.1,
154
                "crossover_prob": 0.9,
155
            }
156
        )
157
158
        return params
159