Converter.values2positions()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 2
dl 0
loc 16
rs 9.9
c 0
b 0
f 0
1
# Author: Simon Blanke
2
# Email: [email protected]
3
# License: MIT License
4
5
import numpy as np
6
import pandas as pd
7
8
from functools import reduce
9
from typing import Optional
10
11
from ..._result import Result
12
13
14
def check_numpy_array(search_space):
15
    for para_name, dim_values in search_space.items():
16
17
        def error_message(wrong_type):
18
            return "\n Value in '{}' of search space dictionary must be of type array but is '{}' \n".format(
19
                para_name, wrong_type
20
            )
21
22
        if not isinstance(dim_values, np.ndarray):
23
            raise ValueError(error_message(type(dim_values)))
24
25
26
class Converter:
27
    def __init__(self, search_space: dict, constraints: list = None) -> None:
28
        check_numpy_array(search_space)
29
30
        self.n_dimensions = len(search_space)
31
        self.search_space = search_space
32
33
        if constraints is None:
34
            self.constraints = []
35
        else:
36
            self.constraints = constraints
37
38
        self.para_names = list(search_space.keys())
39
40
        dim_sizes_list = [len(array) for array in search_space.values()]
41
        self.dim_sizes = np.array(dim_sizes_list)
42
43
        # product of list
44
        self.search_space_size = reduce((lambda x, y: x * y), dim_sizes_list)
45
        self.max_dim = np.amax(self.dim_sizes)
46
47
        self.search_space_positions = [
48
            list(range(len(array))) for array in search_space.values()
49
        ]
50
        self.pos_space = dict(
51
            zip(
52
                self.para_names,
53
                [np.arange(len(array)) for array in search_space.values()],
54
            )
55
        )
56
57
        self.max_positions = self.dim_sizes - 1
58
        self.search_space_values = list(search_space.values())
59
60
    def not_in_constraint(self, position):
61
        para = self.value2para(self.position2value(position))
62
63
        for constraint in self.constraints:
64
            if not constraint(para):
65
                return False
66
        return True
67
68
    def returnNoneIfArgNone(func_):
69
        def wrapper(self, *args):
70
            for arg in [*args]:
71
                if arg is None:
72
                    return None
73
            else:
74
                return func_(self, *args)
75
76
        return wrapper
77
78
    @returnNoneIfArgNone
79
    def position2value(self, position: Optional[list]) -> Optional[list]:
80
        value = []
81
82
        for n, space_dim in enumerate(self.search_space_values):
83
            value.append(space_dim[position[n]])
84
85
        return value
86
87
    @returnNoneIfArgNone
88
    def value2position(self, value: Optional[list]) -> Optional[list]:
89
        position = []
90
        for n, space_dim in enumerate(self.search_space_values):
91
            pos = np.abs(value[n] - np.array(space_dim)).argmin()
92
            position.append(int(pos))
93
94
        return np.array(position)
95
96
    @returnNoneIfArgNone
97
    def value2para(self, value: Optional[list]) -> Optional[dict]:
98
        para = {}
99
        for key, p_ in zip(self.para_names, value):
100
            para[key] = p_
101
102
        return para
103
104
    @returnNoneIfArgNone
105
    def para2value(self, para: Optional[dict]) -> Optional[list]:
106
        value = []
107
        for para_name in self.para_names:
108
            value.append(para[para_name])
109
110
        return value
111
112
    @returnNoneIfArgNone
113
    def values2positions(self, values: Optional[list]) -> Optional[list]:
114
        positions_temp = []
115
        values_np = np.array(values)
116
117
        for n, space_dim in enumerate(self.search_space_values):
118
            values_1d = values_np[:, n]
119
            # m_conv = np.abs(values_1d - space_dim[:, np.newaxis])
120
            # pos_list = m_conv.argmin(0)
121
            pos_list = space_dim.searchsorted(values_1d)
122
123
            positions_temp.append(pos_list)
124
125
        positions = list(np.array(positions_temp).T.astype(int))
126
127
        return positions
128
129
    @returnNoneIfArgNone
130
    def positions2values(self, positions: Optional[list]) -> Optional[list]:
131
        values = []
132
        positions_np = np.array(positions)
133
134
        for n, space_dim in enumerate(self.search_space_values):
135
            pos_1d = positions_np[:, n]
136
            value_ = np.take(space_dim, pos_1d, axis=0)
137
            values.append(value_)
138
139
        values = [list(t) for t in zip(*values)]
140
        return values
141
142
    @returnNoneIfArgNone
143
    def values2paras(self, values: list) -> list:
144
        paras = []
145
        for value in values:
146
            paras.append(self.value2para(value))
147
        return paras
148
149
    @returnNoneIfArgNone
150
    def positions_scores2memory_dict(
151
        self, positions: Optional[list], scores: Optional[list]
152
    ) -> Optional[dict]:
153
        value_tuple_list = list(map(tuple, positions))
154
        # Convert scores to Result objects
155
        result_objects = [Result(float(score), {}) for score in scores]
156
        memory_dict = dict(zip(value_tuple_list, result_objects))
157
158
        return memory_dict
159
160
    @returnNoneIfArgNone
161
    def memory_dict2positions_scores(self, memory_dict: Optional[dict]):
162
        positions = [np.array(pos).astype(int) for pos in list(memory_dict.keys())]
163
        # Extract scores from Result objects
164
        scores = [result.score if isinstance(result, Result) else result 
165
                 for result in memory_dict.values()]
166
167
        return positions, scores
168
169
    @returnNoneIfArgNone
170
    def dataframe2memory_dict(
171
        self, dataframe: Optional[pd.DataFrame]
172
    ) -> Optional[dict]:
173
        parameter = set(self.search_space.keys())
174
        memory_para = set(dataframe.columns)
175
176
        if parameter <= memory_para:
177
            values = list(dataframe[self.para_names].values)
178
            positions = self.values2positions(values)
179
            scores = dataframe["score"]
180
181
            memory_dict = self.positions_scores2memory_dict(positions, scores)
182
183
            return memory_dict
184
        else:
185
            missing = parameter - memory_para
186
187
            print(
188
                "\nWarning:",
189
                '"{}"'.format(*missing),
190
                "is in search_space but not in memory dataframe",
191
            )
192
            print("Optimization run will continue without memory warm start\n")
193
194
            return {}
195
196
    @returnNoneIfArgNone
197
    def memory_dict2dataframe(
198
        self, memory_dict: Optional[dict]
199
    ) -> Optional[pd.DataFrame]:
200
        positions, score = self.memory_dict2positions_scores(memory_dict)
201
        values = self.positions2values(positions)
202
203
        dataframe = pd.DataFrame(values, columns=self.para_names)
204
        dataframe["score"] = score
205
206
        return dataframe
207