Skip to content

Commit d0e27db

Browse files
authored
Merge pull request #59 from automl/smohsinali-cython_forbidden
Cythonize code
2 parents 595e412 + b41caea commit d0e27db

34 files changed

Lines changed: 1589 additions & 785 deletions

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ install:
3030
- pip install nose
3131
- pip install codecov
3232
- pip install typing
33+
- pip install cython
3334
- python setup.py install
3435

3536
# command to run tests, e.g. python setup.py test
3637
script:
37-
- nosetests -sv --with-coverage --cover-package=ConfigSpace
38+
- nosetests -sv --with-coverage --cover-package=ConfigSpace test
3839

3940
after_success:
4041
- codecov

ConfigSpace/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,7 @@
3737
UnParametrizedHyperparameter, OrdinalHyperparameter
3838
from ConfigSpace.conditions import AndConjunction, OrConjunction, \
3939
EqualsCondition, NotEqualsCondition, InCondition, GreaterThanCondition, LessThanCondition
40+
# from ConfigSpace.forbidden import ForbiddenAndConjunction, \
41+
# ForbiddenEqualsClause, ForbiddenInClause
4042
from ConfigSpace.forbidden import ForbiddenAndConjunction, \
4143
ForbiddenEqualsClause, ForbiddenInClause

ConfigSpace/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Version information."""
22

33
# The following line *must* be the last in the module, exactly as formatted:
4-
__version__ = "0.3.10"
4+
__version__ = "0.4.0"

ConfigSpace/c_util.pyx

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
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

Comments
 (0)