Passed
Push — master ( 2e0454...bd7b54 )
by Simon
02:35 queued 16s
created

hyperactive.hyperactive   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 136
dl 0
loc 203
rs 10
c 0
b 0
f 0
wmc 30

11 Methods

Rating   Name   Duplication   Size   Complexity  
A Hyperactive._print_info() 0 10 4
A Hyperactive.best_para() 0 2 1
B Hyperactive.add_search() 0 56 3
A Hyperactive.__init__() 0 15 2
A Hyperactive.check_list() 0 10 3
A Hyperactive.run() 0 13 2
A Hyperactive._default_search_id() 0 5 2
A Hyperactive.best_score() 0 2 1
A Hyperactive._default_opt() 0 6 3
B Hyperactive._create_shared_memory() 0 25 7
A Hyperactive.search_data() 0 11 2
1
# Author: Simon Blanke
2
# Email: [email protected]
3
# License: MIT License
4
5
6
import copy
7
import multiprocessing as mp
8
import pandas as pd
9
10
from typing import Union, List, Dict, Type
11
12
from .optimizers import RandomSearchOptimizer
13
from .run_search import run_search
14
15
from .results import Results
16
from .print_results import PrintResults
17
from .search_space import SearchSpace
18
19
20
class Hyperactive:
21
    """
22
    Initialize the Hyperactive class to manage optimization processes.
23
24
    Parameters:
25
    - verbosity: List of verbosity levels (default: ["progress_bar", "print_results", "print_times"])
26
    - distribution: String indicating the distribution method (default: "multiprocessing")
27
    - n_processes: Number of processes to run in parallel or "auto" to determine automatically (default: "auto")
28
29
    Methods:
30
    - add_search: Add a new optimization search process with specified parameters
31
    - run: Execute the optimization searches
32
    - best_para: Get the best parameters for a specific search
33
    - best_score: Get the best score for a specific search
34
    - search_data: Get the search data for a specific search
35
    """
36
37
    def __init__(
38
        self,
39
        verbosity: list = ["progress_bar", "print_results", "print_times"],
40
        distribution: str = "multiprocessing",
41
        n_processes: Union[str, int] = "auto",
42
    ):
43
        super().__init__()
44
        if verbosity is False:
45
            verbosity = []
46
47
        self.verbosity = verbosity
48
        self.distribution = distribution
49
        self.n_processes = n_processes
50
51
        self.opt_pros = {}
52
53
    def _create_shared_memory(self):
54
        _bundle_opt_processes = {}
55
56
        for opt_pros in self.opt_pros.values():
57
            if opt_pros.memory != "share":
58
                continue
59
            name = opt_pros.objective_function.__name__
60
61
            _bundle_opt_processes.setdefault(name, []).append(opt_pros)
62
63
        for opt_pros_l in _bundle_opt_processes.values():
64
            # Check if the lengths of the search spaces of all optimizers in the list are the same.
65
            if (
66
                len(set(len(opt_pros.s_space()) for opt_pros in opt_pros_l))
67
                == 1
68
            ):
69
                manager = mp.Manager()  # get new manager.dict
70
                shared_memory = manager.dict()
71
                for opt_pros in opt_pros_l:
72
                    opt_pros.memory = shared_memory
73
            else:
74
                for opt_pros in opt_pros_l:
75
                    opt_pros.memory = opt_pros_l[
76
                        0
77
                    ].memory  # get same manager.dict
78
79
    @staticmethod
80
    def _default_opt(optimizer):
81
        if isinstance(optimizer, str):
82
            if optimizer == "default":
83
                optimizer = RandomSearchOptimizer()
84
        return copy.deepcopy(optimizer)
85
86
    @staticmethod
87
    def _default_search_id(search_id, objective_function):
88
        if not search_id:
89
            search_id = objective_function.__name__
90
        return search_id
91
92
    @staticmethod
93
    def check_list(search_space):
94
        for key in search_space.keys():
95
            search_dim = search_space[key]
96
97
            error_msg = "Value in '{}' of search space dictionary must be of type list".format(
98
                key
99
            )
100
            if not isinstance(search_dim, list):
101
                print("Warning", error_msg)
102
                # raise ValueError(error_msg)
103
104
    def add_search(
105
        self,
106
        objective_function: callable,
107
        search_space: Dict[str, list],
108
        n_iter: int,
109
        search_id=None,
110
        optimizer: Union[str, Type[RandomSearchOptimizer]] = "default",
111
        n_jobs: int = 1,
112
        initialize: Dict[str, int] = {"grid": 4, "random": 2, "vertices": 4},
113
        constraints: List[callable] = None,
114
        pass_through: Dict = None,
115
        callbacks: Dict[str, callable] = None,
116
        catch: Dict = None,
117
        max_score: float = None,
118
        early_stopping: Dict = None,
119
        random_state: int = None,
120
        memory: Union[str, bool] = "share",
121
        memory_warm_start: pd.DataFrame = None,
122
    ):
123
        """
124
        Set up and initialize a search process for optimizing an objective function over a given search space.
125
        """
126
        self.check_list(search_space)
127
128
        constraints = constraints or []
129
        pass_through = pass_through or {}
130
        callbacks = callbacks or {}
131
        catch = catch or {}
132
        early_stopping = early_stopping or {}
133
134
        optimizer = self._default_opt(optimizer)
135
        search_id = self._default_search_id(search_id, objective_function)
136
        s_space = SearchSpace(search_space)
137
138
        optimizer.setup_search(
139
            objective_function=objective_function,
140
            s_space=s_space,
141
            n_iter=n_iter,
142
            initialize=initialize,
143
            constraints=constraints,
144
            pass_through=pass_through,
145
            callbacks=callbacks,
146
            catch=catch,
147
            max_score=max_score,
148
            early_stopping=early_stopping,
149
            random_state=random_state,
150
            memory=memory,
151
            memory_warm_start=memory_warm_start,
152
            verbosity=self.verbosity,
153
        )
154
155
        n_jobs = mp.cpu_count() if n_jobs == -1 else n_jobs
156
157
        for _ in range(n_jobs):
158
            nth_process = len(self.opt_pros)
159
            self.opt_pros[nth_process] = optimizer
160
161
    def _print_info(self):
162
        print_res = PrintResults(self.opt_pros, self.verbosity)
163
164
        if self.verbosity:
165
            for _ in range(len(self.opt_pros)):
166
                print("")
167
168
        for results in self.results_list:
169
            nth_process = results["nth_process"]
170
            print_res.print_process(results, nth_process)
171
172
    def run(self, max_time: float = None):
173
        self._create_shared_memory()
174
175
        for opt in self.opt_pros.values():
176
            opt.max_time = max_time
177
178
        self.results_list = run_search(
179
            self.opt_pros, self.distribution, self.n_processes
180
        )
181
182
        self.results_ = Results(self.results_list, self.opt_pros)
183
184
        self._print_info()
185
186
    def best_para(self, id_):
187
        return self.results_.best_para(id_)
188
189
    def best_score(self, id_):
190
        return self.results_.best_score(id_)
191
192
    def search_data(self, id_, times=False):
193
        search_data_ = self.results_.search_data(id_)
194
195
        if times == False:
196
            search_data_.drop(
197
                labels=["eval_times", "iter_times"],
198
                axis=1,
199
                inplace=True,
200
                errors="ignore",
201
            )
202
        return search_data_
203