hyperactive.search_space   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 156
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 36
eloc 99
dl 0
loc 156
rs 9.52
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A SearchSpace.__call__() 0 3 1
A DictClass.__getitem__() 0 3 1
A SearchSpace.check_non_num_values() 0 14 5
A SearchSpace.is_function() 0 9 3
A DictClass.keys() 0 3 1
A SearchSpace._create_num_str_ss() 0 17 5
A DictClass.items() 0 3 1
A DictClass.__init__() 0 2 1
A SearchSpace.__init__() 0 17 2
A SearchSpace.dim_types() 0 15 4
A SearchSpace.is_number() 0 12 3
A DictClass.values() 0 3 1
A SearchSpace._string_or_object() 0 13 5
A SearchSpace.check_list() 0 11 3
1
"""Search space utilities for hyperparameter optimization.
2
3
Author: Simon Blanke
4
Email: [email protected]
5
License: MIT License
6
"""
7
8
import numpy as np
9
10
11
class DictClass:
12
    """DictClass class."""
13
14
    def __init__(self, search_space):
15
        self.search_space = search_space
16
17
    def __getitem__(self, key):
18
        """Get item from search space."""
19
        return self.search_space[key]
20
21
    def keys(self):
22
        """Keys function."""
23
        return self.search_space.keys()
24
25
    def values(self):
26
        """Values function."""
27
        return self.search_space.values()
28
29
    def items(self):
30
        """Items function."""
31
        return self.search_space.items()
32
33
34
class SearchSpace(DictClass):
35
    """SearchSpace class."""
36
37
    def __init__(self, search_space):
38
        super().__init__(search_space)
39
        self.search_space = search_space
40
41
        self.dim_keys = list(search_space.keys())
42
        self.values_l = list(self.search_space.values())
43
44
        positions = {}
45
        for key in search_space.keys():
46
            positions[key] = np.array(range(len(search_space[key])))
47
        self.positions = positions
48
49
        self.check_list()
50
        self.check_non_num_values()
51
52
        self.data_types = self.dim_types()
53
        self.func2str = self._create_num_str_ss()
54
55
    def __call__(self):
56
        """Return search space dictionary."""
57
        return self.search_space
58
59
    def dim_types(self):
60
        """Dim Types function."""
61
        data_types = {}
62
        for dim_key in self.dim_keys:
63
            dim_values = np.array(list(self.search_space[dim_key]))
64
            try:
65
                np.subtract(dim_values, dim_values)
66
                np.array(dim_values).searchsorted(dim_values)
67
            except (TypeError, ValueError):
68
                _type_ = "object"
69
            else:
70
                _type_ = "number"
71
72
            data_types[dim_key] = _type_
73
        return data_types
74
75
    def _create_num_str_ss(self):
76
        func2str = {}
77
        for dim_key in self.dim_keys:
78
            if self.data_types[dim_key] == "number":
79
                func2str[dim_key] = self.search_space[dim_key]
80
            else:
81
                func2str[dim_key] = []
82
83
                dim_values = self.search_space[dim_key]
84
                for value in dim_values:
85
                    try:
86
                        func_name = value.__name__
87
                    except AttributeError:
88
                        func_name = value
89
90
                    func2str[dim_key].append(func_name)
91
        return func2str
92
93
    def check_list(self):
94
        """Check List function."""
95
        for dim_key in self.dim_keys:
96
            search_dim = self.search_space[dim_key]
97
98
            err_msg = (
99
                f"\n Value in '{dim_key}' of search space dictionary must be of "
100
                "type list \n"
101
            )
102
            if not isinstance(search_dim, list):
103
                raise ValueError(err_msg)
104
105
    @staticmethod
106
    def is_function(value):
107
        """Is Function function."""
108
        try:
109
            value.__name__
110
        except AttributeError:
111
            return False
112
        else:
113
            return True
114
115
    @staticmethod
116
    def is_number(value):
117
        """Is Number function."""
118
        try:
119
            float(value)
120
            value * 0.1
121
            value - 0.1
122
            value / 0.1
123
        except (TypeError, ValueError, ZeroDivisionError):
124
            return False
125
        else:
126
            return True
127
128
    def _string_or_object(self, dim_key, dim_values):
129
        for dim_value in dim_values:
130
            is_str = isinstance(dim_value, str)
131
            is_func = self.is_function(dim_value)
132
            is_number = self.is_number(dim_value)
133
134
            if not is_str and not is_func and not is_number:
135
                msg = (
136
                    f"\n The value '{dim_value}' of type '{type(dim_value)}' in the "
137
                    f"search space dimension '{dim_key}' must be number, string or "
138
                    "function \n"
139
                )
140
                raise ValueError(msg)
141
142
    def check_non_num_values(self):
143
        """Check Non Num Values function."""
144
        for dim_key in self.dim_keys:
145
            dim_values = np.array(list(self.search_space[dim_key]))
146
147
            try:
148
                np.subtract(dim_values, dim_values)
149
                np.array(dim_values).searchsorted(dim_values)
150
            except (TypeError, ValueError):
151
                self._string_or_object(dim_key, dim_values)
152
            else:
153
                if dim_values.ndim != 1:
154
                    msg = f"Array-like object in '{dim_key}' must be one dimensional"
155
                    raise ValueError(msg)
156