44from pyrfr import regression
55
66from smac .epm .base_epm import AbstractEPM
7+ from smac .configspace import (
8+ CategoricalHyperparameter ,
9+ UniformFloatHyperparameter ,
10+ UniformIntegerHyperparameter ,
11+ Constant ,
12+ )
713
814
915class RandomForestWithInstances (AbstractEPM ):
@@ -28,8 +34,11 @@ class RandomForestWithInstances(AbstractEPM):
2834 logger : logging.logger
2935 """
3036
31- def __init__ (self , types : np .ndarray ,
37+ def __init__ (self ,
38+ configspace ,
39+ types : np .ndarray ,
3240 bounds : np .ndarray ,
41+ seed : int ,
3342 num_trees : int = 10 ,
3443 do_bootstrapping : bool = True ,
3544 n_points_per_tree : int = - 1 ,
@@ -39,13 +48,14 @@ def __init__(self, types: np.ndarray,
3948 max_depth : int = 20 ,
4049 eps_purity : int = 1e-8 ,
4150 max_num_nodes : int = 2 ** 20 ,
42- seed : int = 42 ,
4351 logged_y : bool = True ,
4452 ** kwargs ):
4553 """Constructor
4654
4755 Parameters
4856 ----------
57+ configspace: ConfigurationSpace
58+ configspace to be passed to random forest (used to impute inactive parameter-values)
4959 types : np.ndarray (D)
5060 Specifies the number of categorical values of an input dimension where
5161 the i-th entry corresponds to the i-th input dimension. Let's say we
@@ -54,6 +64,8 @@ def __init__(self, types: np.ndarray,
5464 have to pass np.array([2, 0]). Note that we count starting from 0.
5565 bounds : np.ndarray (D, 2)
5666 Specifies the bounds for continuous features.
67+ seed : int
68+ The seed that is passed to the random_forest_run library.
5769 num_trees : int
5870 The number of trees in the random forest.
5971 do_bootstrapping : bool
@@ -74,17 +86,20 @@ def __init__(self, types: np.ndarray,
7486 different
7587 max_num_nodes : int
7688 The maxmimum total number of nodes in a tree
77- seed : int
78- The seed that is passed to the random_forest_run library.
7989 logged_y: bool
8090 Indicates if the y data is transformed (i.e. put on logscale) or not
8191 """
8292 try :
83- super ().__init__ (types , bounds , ** kwargs )
93+ super ().__init__ (configspace = configspace , types = types , bounds = bounds , seed = seed , ** kwargs )
8494 except TypeError :
85- # To ensure backwards-compatibility with smac<0.9.0
86- super ().__init__ (** kwargs )
87-
95+ try :
96+ # To ensure backwards-compatibility with smac==0.10.0
97+ super ().__init__ (types , bounds , ** kwargs )
98+ except TypeError :
99+ # To ensure backwards-compatibility with smac<0.9.0
100+ super ().__init__ (** kwargs )
101+
102+ self .configspace = configspace
88103 self .types = types
89104 self .bounds = bounds
90105 self .rng = regression .default_random_engine (seed )
@@ -112,9 +127,33 @@ def __init__(self, types: np.ndarray,
112127 min_samples_leaf , max_depth , eps_purity , seed ]
113128 self .seed = seed
114129
130+ self .impute_values = {}
131+
115132 self .logger = logging .getLogger (self .__module__ + "." +
116133 self .__class__ .__name__ )
117134
135+ def _impute_inactive (self , X : np .ndarray ) -> np .ndarray :
136+ X = X .copy ()
137+ for idx , hp in enumerate (self .configspace .get_hyperparameters ()):
138+ if idx not in self .impute_values :
139+ parents = self .configspace .get_parents_of (hp .name )
140+ if len (parents ) == 0 :
141+ self .impute_values [idx ] = None
142+ else :
143+ if isinstance (hp , CategoricalHyperparameter ):
144+ self .impute_values [idx ] = len (hp .choices )
145+ elif isinstance (hp , (UniformFloatHyperparameter , UniformIntegerHyperparameter )):
146+ self .impute_values [idx ] = - 1
147+ elif isinstance (hp , Constant ):
148+ self .impute_values [idx ] = 1
149+ else :
150+ raise ValueError
151+
152+ nonfinite_mask = ~ np .isfinite (X [:, idx ])
153+ X [nonfinite_mask , idx ] = self .impute_values [idx ]
154+
155+ return X
156+
118157 def _train (self , X : np .ndarray , y : np .ndarray , ** kwargs ):
119158 """Trains the random forest on X and y.
120159
@@ -130,9 +169,8 @@ def _train(self, X: np.ndarray, y: np.ndarray, **kwargs):
130169 self
131170 """
132171
133- self .X = X
172+ self .X = self . _impute_inactive ( X )
134173 self .y = y .flatten ()
135-
136174 if self .n_points_per_tree <= 0 :
137175 self .rf_opts .num_data_points_per_tree = self .X .shape [0 ]
138176 else :
@@ -141,6 +179,7 @@ def _train(self, X: np.ndarray, y: np.ndarray, **kwargs):
141179 self .rf .options = self .rf_opts
142180 data = self .__init_data_container (self .X , self .y )
143181 self .rf .fit (data , rng = self .rng )
182+
144183 return self
145184
146185 def __init_data_container (self , X : np .ndarray , y : np .ndarray ):
@@ -200,6 +239,9 @@ def _predict(self, X: np.ndarray):
200239 (self .types .shape [0 ], X .shape [1 ]))
201240
202241 means , vars_ = [], []
242+
243+ X = self ._impute_inactive (X )
244+
203245 for row_X in X :
204246 mean , var = self .rf .predict_mean_var (row_X )
205247 means .append (mean )
0 commit comments