|
| 1 | +from collections import deque |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +cimport numpy as np |
| 5 | + |
| 6 | +# We now need to fix a datatype for our arrays. I've used the variable |
| 7 | +# DTYPE for this, which is assigned to the usual NumPy runtime |
| 8 | +# type info object. |
| 9 | +DTYPE = np.float |
| 10 | +# "ctypedef" assigns a corresponding compile-time type to DTYPE_t. For |
| 11 | +# every type in the numpy module there's a corresponding compile-time |
| 12 | +# type with a _t-suffix. |
| 13 | +ctypedef np.float_t DTYPE_t |
| 14 | + |
| 15 | +from libc.stdlib cimport malloc, free |
| 16 | + |
| 17 | +from ConfigSpace.exceptions import ForbiddenValueError |
| 18 | +from ConfigSpace.forbidden import AbstractForbiddenComponent |
| 19 | + |
| 20 | +from ConfigSpace.forbidden import AbstractForbiddenComponent |
| 21 | +from ConfigSpace.forbidden cimport AbstractForbiddenComponent |
| 22 | +from ConfigSpace.hyperparameters import Hyperparameter |
| 23 | +from ConfigSpace.hyperparameters cimport Hyperparameter |
| 24 | +from ConfigSpace.conditions import ConditionComponent |
| 25 | +from ConfigSpace.conditions cimport ConditionComponent |
| 26 | + |
| 27 | + |
| 28 | +cpdef int check_forbidden(list forbidden_clauses, np.ndarray vector) except 1: |
| 29 | + cdef int I = len(forbidden_clauses) |
| 30 | + cdef AbstractForbiddenComponent clause |
| 31 | + |
| 32 | + for i in range(I): |
| 33 | + clause = forbidden_clauses[i] |
| 34 | + if clause.c_is_forbidden_vector(vector, strict=False): |
| 35 | + raise ForbiddenValueError("Given vector violates forbidden clause %s" % (str(clause))) |
| 36 | + |
| 37 | + |
| 38 | +cpdef int check_configuration( |
| 39 | + self, |
| 40 | + np.ndarray vector, |
| 41 | + bint allow_inactive_with_values |
| 42 | +) except 1: |
| 43 | + cdef str hp_name |
| 44 | + cdef int hp_index |
| 45 | + cdef Hyperparameter hyperparameter |
| 46 | + cdef int hyperparameter_idx |
| 47 | + cdef DTYPE_t hp_value |
| 48 | + cdef int add |
| 49 | + cdef ConditionComponent condition |
| 50 | + cdef Hyperparameter child |
| 51 | + cdef list conditions |
| 52 | + cdef list parents |
| 53 | + cdef list children |
| 54 | + cdef set inactive |
| 55 | + |
| 56 | + cdef int* active |
| 57 | + active = <int*> malloc(sizeof(int) * len(vector)) |
| 58 | + for i in range(len(vector)): |
| 59 | + active[i] = 0 |
| 60 | + |
| 61 | + unconditional_hyperparameters = self.get_all_unconditional_hyperparameters() |
| 62 | + to_visit = deque() |
| 63 | + to_visit.extendleft(unconditional_hyperparameters) |
| 64 | + inactive = set() |
| 65 | + |
| 66 | + for ch in unconditional_hyperparameters: |
| 67 | + active[self._hyperparameter_idx[ch]] = 1 |
| 68 | + |
| 69 | + while len(to_visit) > 0: |
| 70 | + hp_name = to_visit.pop() |
| 71 | + hp_idx = self._hyperparameter_idx[hp_name] |
| 72 | + hyperparameter = self._hyperparameters[hp_name] |
| 73 | + hp_value = vector[hp_idx] |
| 74 | + |
| 75 | + if not np.isnan(hp_value) and not hyperparameter.is_legal_vector(hp_value): |
| 76 | + free(active) |
| 77 | + raise ValueError("Hyperparameter instantiation '%s' " |
| 78 | + "(type: %s) is illegal for hyperparameter %s" % |
| 79 | + (hp_value, str(type(hp_value)), |
| 80 | + hyperparameter)) |
| 81 | + |
| 82 | + children = self._children_of[hp_name] |
| 83 | + for child in children: |
| 84 | + if child.name not in inactive: |
| 85 | + parents = self._parents_of[child.name] |
| 86 | + if len(parents) == 1: |
| 87 | + conditions = self._parent_conditions_of[child.name] |
| 88 | + add = True |
| 89 | + for condition in conditions: |
| 90 | + if not condition._evaluate_vector(vector): |
| 91 | + add = False |
| 92 | + inactive.add(child.name) |
| 93 | + break |
| 94 | + if add: |
| 95 | + hyperparameter_idx = self._hyperparameter_idx[ |
| 96 | + child.name] |
| 97 | + active[hyperparameter_idx] = 1 |
| 98 | + to_visit.appendleft(child.name) |
| 99 | + |
| 100 | + else: |
| 101 | + parent_names = set([p.name for p in parents]) |
| 102 | + if not parent_names <= set(to_visit): # make sure no parents are still unvisited |
| 103 | + conditions = self._parent_conditions_of[child.name] |
| 104 | + add = True |
| 105 | + for condition in conditions: |
| 106 | + if not condition._evaluate_vector(vector): |
| 107 | + add = False |
| 108 | + inactive.add(child.name) |
| 109 | + break |
| 110 | + |
| 111 | + if add: |
| 112 | + hyperparameter_idx = self._hyperparameter_idx[ |
| 113 | + child.name] |
| 114 | + active[hyperparameter_idx] = 1 |
| 115 | + to_visit.appendleft(child.name) |
| 116 | + |
| 117 | + else: |
| 118 | + continue |
| 119 | + |
| 120 | + if active[hp_idx] and np.isnan(hp_value): |
| 121 | + free(active) |
| 122 | + raise ValueError("Active hyperparameter '%s' not specified!" % |
| 123 | + hyperparameter.name) |
| 124 | + |
| 125 | + for hp_idx in self._idx_to_hyperparameter: |
| 126 | + |
| 127 | + if not allow_inactive_with_values and not active[hp_idx] and \ |
| 128 | + not np.isnan(vector[hp_idx]): |
| 129 | + # Only look up the value (in the line above) if the |
| 130 | + # hyperparameter is inactive! |
| 131 | + hp_name = self._idx_to_hyperparameter[hp_idx] |
| 132 | + hp_value = vector[hp_idx] |
| 133 | + free(active) |
| 134 | + raise ValueError("Inactive hyperparameter '%s' must not be " |
| 135 | + "specified, but has the vector value: '%s'." % |
| 136 | + (hp_name, hp_value)) |
| 137 | + free(active) |
| 138 | + self._check_forbidden(vector) |
| 139 | + |
| 140 | + |
| 141 | +cpdef np.ndarray correct_sampled_array( |
| 142 | + np.ndarray[DTYPE_t, ndim=1] vector, |
| 143 | + list forbidden_clauses_unconditionals, |
| 144 | + list forbidden_clauses_conditionals, |
| 145 | + list hyperparameters_with_children, |
| 146 | + int num_hyperparameters, |
| 147 | + list unconditional_hyperparameters, |
| 148 | + dict hyperparameter_to_idx, |
| 149 | + dict parent_conditions_of, |
| 150 | + dict parents_of, |
| 151 | + dict children_of, |
| 152 | +): |
| 153 | + cdef AbstractForbiddenComponent clause |
| 154 | + cdef ConditionComponent condition |
| 155 | + cdef int hyperparameter_idx |
| 156 | + cdef DTYPE_t NaN = np.NaN |
| 157 | + cdef set visited |
| 158 | + cdef set inactive |
| 159 | + cdef Hyperparameter child |
| 160 | + cdef list children |
| 161 | + cdef str child_name |
| 162 | + cdef list parents |
| 163 | + cdef set parent_names |
| 164 | + cdef list conditions |
| 165 | + cdef int add |
| 166 | + |
| 167 | + cdef int* active |
| 168 | + active = <int*> malloc(sizeof(int) * num_hyperparameters) |
| 169 | + for j in range(num_hyperparameters): |
| 170 | + active[j] = 0 |
| 171 | + |
| 172 | + for j in range(len(forbidden_clauses_unconditionals)): |
| 173 | + clause = forbidden_clauses_unconditionals[j] |
| 174 | + if clause.c_is_forbidden_vector(vector, strict=False): |
| 175 | + raise ForbiddenValueError( |
| 176 | + "Given vector violates forbidden clause %s" % ( |
| 177 | + str(clause))) |
| 178 | + |
| 179 | + hps = deque() |
| 180 | + visited = set() |
| 181 | + hps.extendleft(hyperparameters_with_children) |
| 182 | + |
| 183 | + for ch in unconditional_hyperparameters: |
| 184 | + active[hyperparameter_to_idx[ch]] = 1 |
| 185 | + |
| 186 | + inactive = set() |
| 187 | + |
| 188 | + while len(hps) > 0: |
| 189 | + hp = hps.pop() |
| 190 | + visited.add(hp) |
| 191 | + children = children_of[hp] |
| 192 | + for child in children: |
| 193 | + child_name = child.name |
| 194 | + if child_name not in inactive: |
| 195 | + parents = parents_of[child_name] |
| 196 | + hyperparameter_idx = hyperparameter_to_idx[child_name] |
| 197 | + if len(parents) == 1: |
| 198 | + conditions = parent_conditions_of[child_name] |
| 199 | + add = True |
| 200 | + for j in range(len(conditions)): |
| 201 | + condition = conditions[j] |
| 202 | + if not condition._evaluate_vector(vector): |
| 203 | + add = False |
| 204 | + vector[hyperparameter_idx] = NaN |
| 205 | + inactive.add(child_name) |
| 206 | + break |
| 207 | + if add == True: |
| 208 | + active[hyperparameter_idx] = 1 |
| 209 | + hps.appendleft(child_name) |
| 210 | + |
| 211 | + else: |
| 212 | + parent_names = set([p.name for p in parents]) |
| 213 | + if parent_names.issubset(visited): # make sure no parents are still unvisited |
| 214 | + conditions = parent_conditions_of[child_name] |
| 215 | + add = True |
| 216 | + for j in range(len(conditions)): |
| 217 | + condition = conditions[j] |
| 218 | + if not condition._evaluate_vector(vector): |
| 219 | + add = False |
| 220 | + vector[hyperparameter_idx] = NaN |
| 221 | + inactive.add(child_name) |
| 222 | + break |
| 223 | + |
| 224 | + if add == True: |
| 225 | + active[hyperparameter_idx] = 1 |
| 226 | + hps.appendleft(child_name) |
| 227 | + |
| 228 | + else: |
| 229 | + continue |
| 230 | + |
| 231 | + for j in range(len(vector)): |
| 232 | + if not active[j]: |
| 233 | + vector[j] = NaN |
| 234 | + |
| 235 | + free(active) |
| 236 | + for j in range(len(forbidden_clauses_conditionals)): |
| 237 | + clause = forbidden_clauses_conditionals[j] |
| 238 | + if clause.c_is_forbidden_vector(vector, strict=False): |
| 239 | + raise ForbiddenValueError( |
| 240 | + "Given vector violates forbidden clause %s" % ( |
| 241 | + str(clause))) |
| 242 | + |
| 243 | + return vector |
| 244 | + |
| 245 | + |
| 246 | +cpdef np.ndarray change_hp_value( |
| 247 | + configuration_space, |
| 248 | + np.ndarray[DTYPE_t, ndim=1] configuration_array, |
| 249 | + str hp_name, |
| 250 | + DTYPE_t hp_value, |
| 251 | + int index, |
| 252 | +): |
| 253 | + """Change hyperparameter value in configuration array to given value. |
| 254 | +
|
| 255 | + Does not check if the new value is legal. Activates and deactivates other |
| 256 | + hyperparameters if necessary. Does not check if new hyperparameter value |
| 257 | + results in the violation of any forbidden clauses. |
| 258 | +
|
| 259 | + Parameters |
| 260 | + ---------- |
| 261 | + configuration_space : ConfigurationSpace |
| 262 | +
|
| 263 | + configuration_array : np.ndarray |
| 264 | +
|
| 265 | + hp_name : str |
| 266 | +
|
| 267 | + hp_value : float |
| 268 | +
|
| 269 | + index : int |
| 270 | +
|
| 271 | + Returns |
| 272 | + ------- |
| 273 | + np.ndarray |
| 274 | + """ |
| 275 | + cdef Hyperparameter current |
| 276 | + cdef str current_name |
| 277 | + cdef list disabled |
| 278 | + cdef set visited |
| 279 | + cdef dict activated_values |
| 280 | + cdef int active |
| 281 | + cdef ConditionComponent condition |
| 282 | + cdef int current_idx |
| 283 | + cdef DTYPE_t current_value |
| 284 | + cdef DTYPE_t default_value |
| 285 | + cdef list children |
| 286 | + cdef list children_ |
| 287 | + cdef Hyperparameter ch |
| 288 | + cdef str child |
| 289 | + cdef set to_disable |
| 290 | + cdef DTYPE_t NaN = np.NaN |
| 291 | + cdef dict children_of = configuration_space._children_of |
| 292 | + |
| 293 | + configuration_array[index] = hp_value |
| 294 | + |
| 295 | + # Hyperparameters which are going to be set to inactive |
| 296 | + disabled = [] |
| 297 | + |
| 298 | + # Activate hyperparameters if their parent node got activated |
| 299 | + children = children_of[hp_name] |
| 300 | + if len(children) > 0: |
| 301 | + to_visit = deque() # type: deque |
| 302 | + to_visit.extendleft(children) |
| 303 | + visited = set() # type: Set[str] |
| 304 | + activated_values = dict() # type: Dict[str, Union[int, float, str]] |
| 305 | + |
| 306 | + while len(to_visit) > 0: |
| 307 | + current = to_visit.pop() |
| 308 | + current_name = current.name |
| 309 | + if current_name in visited: |
| 310 | + continue |
| 311 | + visited.add(current_name) |
| 312 | + if current_name in disabled: |
| 313 | + continue |
| 314 | + |
| 315 | + current_idx = configuration_space._hyperparameter_idx[current_name] |
| 316 | + current_value = configuration_array[current_idx] |
| 317 | + |
| 318 | + conditions = configuration_space._parent_conditions_of[current_name] |
| 319 | + |
| 320 | + active = True |
| 321 | + for condition in conditions: |
| 322 | + if not condition._evaluate_vector(configuration_array): |
| 323 | + active = False |
| 324 | + break |
| 325 | + |
| 326 | + if active and not current_value == current_value: |
| 327 | + default_value = current.normalized_default_value |
| 328 | + configuration_array[current_idx] = default_value |
| 329 | + children_ = children_of[current_name] |
| 330 | + if len(children_) > 0: |
| 331 | + to_visit.extendleft(children_) |
| 332 | + |
| 333 | + # If the hyperparameter was made inactive, |
| 334 | + # all its children need to be deactivade as well |
| 335 | + if not active and current_value == current_value: |
| 336 | + configuration_array[current_idx] = NaN |
| 337 | + |
| 338 | + children = children_of[current_name] |
| 339 | + |
| 340 | + if len(children) > 0: |
| 341 | + to_disable = set() |
| 342 | + for ch in children: |
| 343 | + to_disable.add(ch.name) |
| 344 | + while len(to_disable) > 0: |
| 345 | + child = to_disable.pop() |
| 346 | + child_idx = configuration_space._hyperparameter_idx[child] |
| 347 | + disabled.append(child_idx) |
| 348 | + children = children_of[child] |
| 349 | + |
| 350 | + for ch in children: |
| 351 | + to_disable.add(ch.name) |
| 352 | + |
| 353 | + for idx in disabled: |
| 354 | + configuration_array[idx] = NaN |
| 355 | + |
| 356 | + return configuration_array |
0 commit comments