From 430a75822dd80e8b2102e9061a3a646a2223d53c Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 13 Jan 2017 14:46:46 +0100 Subject: [PATCH 01/22] type annotate hyperparameter.py --- ConfigSpace/hyperparameters.py | 241 +++++++++++++++++---------------- 1 file changed, 126 insertions(+), 115 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 4c049fa6..2db04179 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -30,16 +30,16 @@ import warnings from collections import OrderedDict +from typing import List, Any, Dict, Union import numpy as np -import io -from functools import reduce + class Hyperparameter(object): __metaclass__ = ABCMeta @abstractmethod - def __init__(self, name): + def __init__(self, name: str) -> None: if not isinstance(name, str): raise TypeError( "The name of a hyperparameter must be an instance of" @@ -47,19 +47,19 @@ def __init__(self, name): self.name = name # http://stackoverflow.com/a/25176504/4636294 - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """Override the default Equals behavior""" if isinstance(other, self.__class__): return self.__dict__ == other.__dict__ return NotImplemented - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """Define a non-equality test""" if isinstance(other, self.__class__): return not self.__eq__(other) return NotImplemented - def __hash__(self): + def __hash__(self) -> int: """Override the default hash behavior (that returns the id or the object)""" return hash(tuple(sorted(self.__dict__.items()))) @@ -101,7 +101,7 @@ def get_num_neighbors(self): class Constant(Hyperparameter): - def __init__(self, name, value): + def __init__(self, name: str, value: Union[str, int, float]) -> None: super(Constant, self).__init__(name) allowed_types = [] allowed_types.extend(int) @@ -119,35 +119,36 @@ def __init__(self, name, value): self.value = value self.default = value - def __repr__(self): + def __repr__(self) -> str: repr_str = ["%s" % self.name, "Type: Constant", "Value: %s" % self.value] return ", ".join(repr_str) - def is_legal(self, value): + def is_legal(self, value: Union[str, int, float]) -> bool: return value == self.value - def _sample(self, rs, size=None): + def _sample(self, rs: None, size: int = None) -> Union[int, np.ndarray]: return 0 if size == 1 else np.zeros((size,)) - def _transform(self, vector): + # todo : recheck + def _transform(self, vector: np.ndarray) -> Union[None, int, float, str]: if not np.isfinite(vector): return None return self.value - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: np.ndarray) -> Union[int, np.NaN]: if vector != self.value: return np.NaN return 0 - def has_neighbors(self): + def has_neighbors(self) -> bool: return False - def get_num_neighbors(self): + def get_num_neighbors(self) -> int: return 0 - def get_neighbors(self, value, rs, number, transform=False): + def get_neighbors(self, value: Any, rs: Any, number: int, transform: bool = False) -> List: return [] @@ -156,35 +157,40 @@ class UnParametrizedHyperparameter(Constant): class NumericalHyperparameter(Hyperparameter): - def __init__(self, name, default): + # todo : type of name and default? + def __init__(self, name: str, default: Any) -> None: super(NumericalHyperparameter, self).__init__(name) self.default = default - def has_neighbors(self): + def has_neighbors(self) -> bool: return True - def get_num_neighbors(self): + def get_num_neighbors(self) -> np.inf: return np.inf + class FloatHyperparameter(NumericalHyperparameter): - def __init__(self, name, default): + # todo : type of name and default? + def __init__(self, name: str, default: Union[int, float]): super(FloatHyperparameter, self).__init__(name, default) - def is_legal(self, value): + def is_legal(self, value: Union[int, float]) -> bool: return isinstance(value, float) or isinstance(value, int) - def check_default(self, default): + # todo : recheck default + def check_default(self, default: Union[int, float]) -> float: return np.round(float(default), 10) class IntegerHyperparameter(NumericalHyperparameter): - def __init__(self, name, default): + # todo : type of name and default? + def __init__(self, name: str, default: int): super(IntegerHyperparameter, self).__init__(name, default) - def is_legal(self, value): + def is_legal(self, value: int) -> bool: return isinstance(value, (int, np.int, np.int32, np.int64)) - def check_int(self, parameter, name): + def check_int(self, parameter: int, name: str) -> int: if abs(int(parameter) - parameter) > 0.00000001 and \ type(parameter) is not int: raise ValueError("For the Integer parameter %s, the value must be " @@ -192,20 +198,15 @@ def check_int(self, parameter, name): " %s." % (name, type(parameter), str(parameter))) return int(parameter) - def check_default(self, default): + def check_default(self, default: int) -> int: return int(np.round(default, 0)) +# todo: find out purpose of mixin and annotate it? class UniformMixin(object): - def is_legal(self, value): + def is_legal(self, value) -> bool: if not super(UniformMixin, self).is_legal(value): return False - elif self.log: - # compute legality in log space due to rounding errors - if self._upper >= np.log(value) >= self._lower: - return True - else: - return False # Strange numerical issues! elif self.upper >= value >= (self.lower - 0.0000000001): return True @@ -236,7 +237,8 @@ def check_default(self, default): class UniformFloatHyperparameter(UniformMixin, FloatHyperparameter): - def __init__(self, name, lower, upper, default=None, q=None, log=False): + def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float], + default: Union[int, float, None] = None, q: [int, float, None] = None, log: bool = False): self.lower = float(lower) self.upper = float(upper) self.q = float(q) if q is not None else None @@ -251,6 +253,9 @@ def __init__(self, name, lower, upper, default=None, q=None, log=False): "hyperparameter %s is forbidden." % (self.lower, name)) + super(UniformFloatHyperparameter, self). \ + __init__(name, self.check_default(default)) + if self.log: if self.q is not None: lower = self.lower - (np.float64(self.q) / 2. - 0.0001) @@ -268,10 +273,7 @@ def __init__(self, name, lower, upper, default=None, q=None, log=False): self._lower = self.lower self._upper = self.upper - super(UniformFloatHyperparameter, self). \ - __init__(name, self.check_default(default)) - - def __repr__(self): + def __repr__(self) -> str: repr_str = io.StringIO() repr_str.write("%s, Type: UniformFloat, Range: [%s, %s], Default: %s" % (self.name, repr(self.lower), repr(self.upper), @@ -283,7 +285,7 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): return all([self.name == other.name, abs(self.lower - other.lower) < 0.00000001, @@ -295,18 +297,19 @@ def __eq__(self, other): else: return False - def to_integer(self): - # TODO check if conversion makes sense at all (at least two integer - # values possible!) + def to_integer(self) -> UniformIntegerHyperparameter: + # TODO check if conversion makes sense at all (at least two integer values possible!) return UniformIntegerHyperparameter(self.name, self.lower, self.upper, int(np.round(self.default)), self.q, self.log) - def _sample(self, rs, size=None): + # todo: rs? prabably numpy.random.uniform + def _sample(self, rs: np.random, size: Union[int, None] = None) -> float: return rs.uniform(size=size) - def _transform(self, vector): + # todo : recheck + def _transform(self, vector: np.ndarray) -> Union[np.ndarray, None]: if np.any(np.isnan(vector)): return None vector *= (self._upper - self._lower) @@ -317,14 +320,14 @@ def _transform(self, vector): vector = int(np.round(vector / self.q, 0)) * self.q return vector - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: Union[np.ndarray, None]) -> Union[np.NaN, np.ndarray]: if vector is None: return np.NaN if self.log: vector = np.log(vector) return (vector - self._lower) / (self._upper - self._lower) - def get_neighbors(self, value, rs, number=4, transform=False): + def get_neighbors(self, value: Any, rs: np.random, number: int = 4, transform: bool = False) -> List[float]: neighbors = [] while len(neighbors) < number: neighbor = rs.normal(value, 0.2) @@ -338,7 +341,8 @@ def get_neighbors(self, value, rs, number=4, transform=False): class NormalFloatHyperparameter(NormalMixin, FloatHyperparameter): - def __init__(self, name, mu, sigma, default=None, q=None, log=False): + def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], + default: Union[None, float] = None, q: Union[int, float, None] = None, log: bool = False): self.mu = float(mu) self.sigma = float(sigma) self.q = float(q) if q is not None else None @@ -346,7 +350,7 @@ def __init__(self, name, mu, sigma, default=None, q=None, log=False): super(NormalFloatHyperparameter, self). \ __init__(name, self.check_default(default)) - def __repr__(self): + def __repr__(self) -> str: repr_str = io.StringIO() repr_str.write("%s, Type: NormalFloat, Mu: %s Sigma: %s, Default: %s" % (self.name, repr(self.mu), repr(self.sigma), @@ -358,7 +362,7 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): return all([self.name == other.name, abs(self.mu - other.mu) < 0.00000001, @@ -370,7 +374,7 @@ def __eq__(self, other): else: return False - def to_uniform(self, z=3): + def to_uniform(self, z: int = 3) -> UniformFloatHyperparameter: return UniformFloatHyperparameter(self.name, self.mu - (z * self.sigma), self.mu + (z * self.sigma), @@ -378,24 +382,24 @@ def to_uniform(self, z=3): np.round(self.default, 0)), q=self.q, log=self.log) - def to_integer(self): + def to_integer(self) -> NormalIntegerHyperparameter: return NormalIntegerHyperparameter(self.name, self.mu, self.sigma, default=int( np.round(self.default, 0)), q=self.q, log=self.log) - def is_legal(self, value): + def is_legal(self, value: Union[float, int]) -> bool: if isinstance(value, (float, int)): return True else: return False - def _sample(self, rs, size=None): + def _sample(self, rs: np.random, size: Union[None, int] = None) -> np.ndarray: mu = self.mu sigma = self.sigma return rs.normal(mu, sigma, size=size) - def _transform(self, vector): + def _transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: if np.isnan(vector): return None if self.log: @@ -404,7 +408,7 @@ def _transform(self, vector): vector = int(np.round(vector / self.q, 0)) * self.q return vector - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: [None, np.ndarray]) -> Union(float, np.ndarray): if vector is None: return np.NaN @@ -412,7 +416,7 @@ def _inverse_transform(self, vector): vector = np.log(vector) return vector - def get_neighbors(self, value, rs, number=4): + def get_neighbors(self, value: float, rs: np.random, number: int = 4) -> List[np.ndarray]: neighbors = [] for i in range(number): neighbors.append(rs.normal(value, self.sigma)) @@ -420,7 +424,8 @@ def get_neighbors(self, value, rs, number=4): class UniformIntegerHyperparameter(UniformMixin, IntegerHyperparameter): - def __init__(self, name, lower, upper, default=None, q=None, log=False): + def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, + q: [float, None] = None, log: bool = False) -> None: self.lower = self.check_int(lower, "lower") self.upper = self.check_int(upper, "upper") if default is not None: @@ -437,10 +442,6 @@ def __init__(self, name, lower, upper, default=None, q=None, log=False): self.q = None self.log = bool(log) - if self.log: - self._lower = np.log(lower) - self._upper = np.log(upper) - if self.lower >= self.upper: raise ValueError("Upper bound %d must be larger than lower bound " "%d for hyperparameter %s" % @@ -459,7 +460,7 @@ def __init__(self, name, lower, upper, default=None, q=None, log=False): log=self.log, q=self.q, default=self.default) - def __repr__(self): + def __repr__(self) -> str: repr_str = io.StringIO() repr_str.write("%s, Type: UniformInteger, Range: [%s, %s], Default: %s" % (self.name, repr(self.lower), @@ -471,7 +472,8 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def _sample(self, rs, size=None): + # todo: recheck + def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: value = self.ufhp._sample(rs, size=size) # Map all floats which belong to the same integer value to the same # float value by first transforming it to an integer and then @@ -480,7 +482,7 @@ def _sample(self, rs, size=None): value = self._inverse_transform(value) return value - def _transform(self, vector): + def _transform(self, vector: np.ndarray) -> np.ndarray: if np.any(np.isnan(vector)): return None vector = self.ufhp._transform(vector) @@ -492,10 +494,10 @@ def _transform(self, vector): vector = int(vector) return vector - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: return self.ufhp._inverse_transform(vector) - def has_neighbors(self): + def has_neighbors(self) -> bool: if self.log: upper = np.exp(self.ufhp._upper) lower = np.exp(self.ufhp._lower) @@ -509,7 +511,7 @@ def has_neighbors(self): else: return False - def get_neighbors(self, value, rs, number=4, transform=False): + def get_neighbors(self, value: Union[int, float], number: int = 4, transform: bool = False) -> List[int]: neighbors = [] while len(neighbors) < number: rejected = True @@ -532,7 +534,8 @@ def get_neighbors(self, value, rs, number=4, transform=False): class NormalIntegerHyperparameter(NormalMixin, IntegerHyperparameter): - def __init__(self, name, mu, sigma, default=None, q=None, log=False): + def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], + default: Union[int, None] = None, q: Union[None, int, float] = None, log: bool = False): self.mu = mu self.sigma = sigma if default is not None: @@ -559,7 +562,7 @@ def __init__(self, name, mu, sigma, default=None, q=None, log=False): q=self.q, default=self.default) - def __repr__(self): + def __repr__(self) -> str: repr_str = io.StringIO() repr_str.write("%s, Type: NormalInteger, Mu: %s Sigma: %s, Default: " "%s" % (self.name, repr(self.mu), @@ -571,7 +574,7 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if isinstance(other, self.__class__): return all([self.name == other.name, abs(self.mu - other.mu) < 0.00000001, @@ -583,20 +586,20 @@ def __eq__(self, other): else: return False - def to_uniform(self, z=3): + def to_uniform(self, z: int = 3) -> UniformIntegerHyperparameter: return UniformIntegerHyperparameter(self.name, self.mu - (z * self.sigma), self.mu + (z * self.sigma), default=self.default, q=self.q, log=self.log) - def is_legal(self, value): + def is_legal(self, value: int) -> bool: if isinstance(value, int): return True else: return False - def _sample(self, rs, size=None): + def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: value = self.nfhp._sample(rs, size=size) # Map all floats which belong to the same integer value to the same # float value by first transforming it to an integer and then @@ -605,7 +608,7 @@ def _sample(self, rs, size=None): value = self._inverse_transform(value) return value - def _transform(self, vector): + def _transform(self, vector: np.ndarray) -> Union[None, np.ndarray]: if np.isnan(vector): return None vector = self.nfhp._transform(vector) @@ -614,13 +617,13 @@ def _transform(self, vector): vector = int(vector) return vector - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: return self.nfhp._inverse_transform(vector) - def has_neighbors(self): + def has_neighbors(self) -> bool: return True - def get_neighbors(self, value, rs, number=4, transform=False): + def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> None: neighbors = [] while len(neighbors) < number: rejected = True @@ -643,14 +646,14 @@ def get_neighbors(self, value, rs, number=4, transform=False): class CategoricalHyperparameter(Hyperparameter): # TODO add more magic for automated type recognition - def __init__(self, name, choices, default=None): + def __init__(self, name: str, choices: List[str], default: Union[str, None] = None) -> None: super(CategoricalHyperparameter, self).__init__(name) # TODO check that there is no bullshit in the choices! self.choices = choices self._num_choices = len(choices) self.default = self.check_default(default) - def __repr__(self): + def __repr__(self) -> str: repr_str = io.StringIO() repr_str.write("%s, Type: Categorical, Choices: {" % (self.name)) for idx, choice in enumerate(self.choices): @@ -663,13 +666,13 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def is_legal(self, value): + def is_legal(self, value: str) -> bool: if value in self.choices: return True else: return False - def check_default(self, default): + def check_default(self, default: Union[None, str]) -> str: if default is None: return self.choices[0] elif self.is_legal(default): @@ -677,10 +680,11 @@ def check_default(self, default): else: raise ValueError("Illegal default value %s" % str(default)) - def _sample(self, rs, size=None): + def _sample(self, rs: np.randint, size: int = None) -> np.ndarray: return rs.randint(0, self._num_choices, size=size) - def _transform(self, vector): + # todo recheck + def _transform(self, vector: np.ndarray) -> Union[None, List[str]]: if vector != vector: return None if np.equal(np.mod(vector, 1), 0): @@ -690,18 +694,20 @@ def _transform(self, vector): 'hyperparameter %s with an integer, but provided ' 'the following float: %f' % (self, vector)) - def _inverse_transform(self, vector): + # todo recheck + def _inverse_transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: if vector is None: return np.NaN return self.choices.index(vector) - def has_neighbors(self): + def has_neighbors(self) -> bool: return len(self.choices) > 1 - def get_num_neighbors(self): + def get_num_neighbors(self) -> int: return len(self.choices) - 1 - def get_neighbors(self, value, rs, number=np.inf, transform=False): + def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ + List[float]: neighbors = [] if number < len(self.choices): while len(neighbors) < number: @@ -734,26 +740,28 @@ def get_neighbors(self, value, rs, number=np.inf, transform=False): neighbors.append(candidate) return neighbors - + + class OrdinalHyperparameter(Hyperparameter): - def __init__(self, name, sequence, default=None): + def __init__(self, name: str, sequence: Union[List[float], List[int], List[str]], + default: Union[str, int, float, None] = None): """ since the sequence can consist of elements from different types we store them into a dictionary in order to handle them as a numeric sequence according to their order/position. """ super(OrdinalHyperparameter, self).__init__(name) - self.sequence= sequence + self.sequence = sequence self._num_elements = len(sequence) self.default = self.check_default(default) - + self.value_dict = OrderedDict() counter = 1 for element in self.sequence: self.value_dict[element] = counter counter += 1 - def __repr__(self): + def __repr__(self) -> str: """ writes out the parameter definition """ @@ -769,16 +777,16 @@ def __repr__(self): repr_str.seek(0) return repr_str.getvalue() - def is_legal(self, value): + def is_legal(self, value: Union[int, float, str]) -> bool: """ checks if a certain value is represented in the sequence """ return value in self.sequence - def check_default(self, default): + def check_default(self, default: Union[int, float, str, None]) -> Union[int, float, str]: """ checks if given default value is represented in the sequence. - If there's no default value we simply choose the + If there's no default value we simply choose the first element in our sequence as default. """ if default is None: @@ -787,8 +795,8 @@ def check_default(self, default): return default else: raise ValueError("Illegal default value %s" % str(default)) - - def _transform(self, vector): + + def _transform(self, vector: np.ndarray) -> Union[None, List[int], List[str], List[float]]: if vector != vector: return None if np.equal(np.mod(vector, 1), 0): @@ -798,31 +806,32 @@ def _transform(self, vector): 'hyperparameter %s with an integer, but provided ' 'the following float: %f' % (self, vector)) - def _inverse_transform(self, vector): + def _inverse_transform(self, vector: np.ndarray) -> Union[float, List[int], List[str], List[float]]: if vector is None: return np.NaN return self.sequence.index(vector) - - def get_seq_order(self): + + def get_seq_order(self) -> np.ndarray: """ - returns the ordinal sequence as numeric sequence + returns the ordinal sequence as numeric sequence (according to the the ordering) from 1 to length of our sequence. """ - return np.arange(1,self._num_elements+1) - - def get_order(self, value): + return np.arange(1, self._num_elements + 1) + + def get_order(self, value: Union[None, int, str, float]) -> int: """ returns the seuence position/order of a certain value from the sequence """ return self.value_dict[value] - - def get_value(self, idx): + + # todo : recheck + def get_value(self, idx: int) -> Union[int, str, float]: """ returns the sequence value of a given order/position """ return list(self.value_dict.keys())[list(self.value_dict.values()).index(idx)] - - def check_order(self,val1, val2): + + def check_order(self, val1: Union[int, str, float], val2: Union[int, str, float]) -> bool: """ checks whether value1 is smaller than value2. """ @@ -833,36 +842,38 @@ def check_order(self,val1, val2): else: return False - def _sample(self, rs, size=None): + def _sample(self, rs: np.random, size: Union[int, None] = None) -> int: """ returns a random sample from our sequence as order/position index """ return rs.randint(0, self._num_elements, size=size) - def has_neighbors(self): + def has_neighbors(self) -> bool: """ - checks if there are neighbors or we're only dealing with an + checks if there are neighbors or we're only dealing with an one-element sequence """ return len(self.sequence) > 1 - def get_num_neighbors(self, value): + def get_num_neighbors(self, value: Union[int, float, str]) -> int: """ returns the number of existing neighbors in the sequence """ - if value == self.sequence[0] or value ==self.sequence[-1]: + if value == self.sequence[0] or value == self.sequence[-1]: return 1 else: return 2 - def get_neighbors(self, value, number=2, transform = False): + # todo: recehck...added rs as param otherrwise mismatch with baseclass signature + def get_neighbors(self, value: Union[int, str, float], rs: None, number: int = 2, transform: bool = False) \ + -> Union[List[int], List[str], List[float]]: """ Returns the neighbors of a given value. """ neighbors = [] if number < len(self.sequence): index = self.get_order(value) - neighbor_idx1 = index -1 + neighbor_idx1 = index - 1 neighbor_idx2 = index + 1 seq = self.get_seq_order() if transform: @@ -871,7 +882,7 @@ def get_neighbors(self, value, number=2, transform = False): if self.check_order(candidate1, value): neighbors.append(candidate1) if neighbor_idx2 <= self._num_elements: - candidate2 = self.get_value(neighbor_idx2) + candidate2 = self.get_value(neighbor_idx2) if self.check_order(value, candidate2): neighbors.append(candidate2) else: From 7449e5d346592495728d1f9ab5fa7b33ae2208b3 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 13 Jan 2017 14:48:13 +0100 Subject: [PATCH 02/22] . --- ConfigSpace/hyperparameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 2db04179..6b8cb4df 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -408,7 +408,7 @@ def _transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: vector = int(np.round(vector / self.q, 0)) * self.q return vector - def _inverse_transform(self, vector: [None, np.ndarray]) -> Union(float, np.ndarray): + def _inverse_transform(self, vector: [None, np.ndarray]) -> Union[float, np.ndarray]: if vector is None: return np.NaN From 3ce095ab6ccdd4f7a4ddd20a2122eb97d01f864b Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 13 Jan 2017 15:41:48 +0100 Subject: [PATCH 03/22] commit --- ConfigSpace/hyperparameters.py | 46 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 6b8cb4df..76fff6a0 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -30,8 +30,8 @@ import warnings from collections import OrderedDict -from typing import List, Any, Dict, Union - +from typing import List, Any, Dict, Union, Tuple +import io import numpy as np @@ -103,14 +103,15 @@ def get_num_neighbors(self): class Constant(Hyperparameter): def __init__(self, name: str, value: Union[str, int, float]) -> None: super(Constant, self).__init__(name) - allowed_types = [] - allowed_types.extend(int) - allowed_types.append(float) - allowed_types.extend(str) - allowed_types.append(str) - allowed_types = tuple(allowed_types) - - if not isinstance(value, allowed_types) or \ + allowed_types_list = [] # type: List[str] + allowed_types_list.extend(str(int)) + allowed_types_list.append(str(float)) + allowed_types_list.extend(str(str)) + allowed_types_list.append(str(str)) + + allowed_types = tuple(allowed_types_list) + + if not isinstance(value, allowed_types_list) or \ isinstance(value, bool): raise TypeError("Constant value is of type %s, but only the " "following types are allowed: %s" % @@ -171,7 +172,7 @@ def get_num_neighbors(self) -> np.inf: class FloatHyperparameter(NumericalHyperparameter): # todo : type of name and default? - def __init__(self, name: str, default: Union[int, float]): + def __init__(self, name: str, default: Union[int, float]) -> None: super(FloatHyperparameter, self).__init__(name, default) def is_legal(self, value: Union[int, float]) -> bool: @@ -184,7 +185,7 @@ def check_default(self, default: Union[int, float]) -> float: class IntegerHyperparameter(NumericalHyperparameter): # todo : type of name and default? - def __init__(self, name: str, default: int): + def __init__(self, name: str, default: int) -> None: super(IntegerHyperparameter, self).__init__(name, default) def is_legal(self, value: int) -> bool: @@ -238,7 +239,7 @@ def check_default(self, default): class UniformFloatHyperparameter(UniformMixin, FloatHyperparameter): def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float], - default: Union[int, float, None] = None, q: [int, float, None] = None, log: bool = False): + default: Union[int, float, None] = None, q: [int, float, None] = None, log: bool = False) -> None: self.lower = float(lower) self.upper = float(upper) self.q = float(q) if q is not None else None @@ -299,8 +300,9 @@ def __eq__(self, other: Any) -> bool: def to_integer(self) -> UniformIntegerHyperparameter: # TODO check if conversion makes sense at all (at least two integer values possible!) - return UniformIntegerHyperparameter(self.name, self.lower, - self.upper, + # todo check if params should be converted to int while class initialization or inside class itself + return UniformIntegerHyperparameter(self.name, int(self.lower), + int(self.upper), int(np.round(self.default)), self.q, self.log) @@ -328,7 +330,7 @@ def _inverse_transform(self, vector: Union[np.ndarray, None]) -> Union[np.NaN, n return (vector - self._lower) / (self._upper - self._lower) def get_neighbors(self, value: Any, rs: np.random, number: int = 4, transform: bool = False) -> List[float]: - neighbors = [] + neighbors = [] # type: List[float] while len(neighbors) < number: neighbor = rs.normal(value, 0.2) if neighbor < 0 or neighbor > 1: @@ -342,7 +344,7 @@ def get_neighbors(self, value: Any, rs: np.random, number: int = 4, transform: b class NormalFloatHyperparameter(NormalMixin, FloatHyperparameter): def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], - default: Union[None, float] = None, q: Union[int, float, None] = None, log: bool = False): + default: Union[None, float] = None, q: Union[int, float, None] = None, log: bool = False) -> None: self.mu = float(mu) self.sigma = float(sigma) self.q = float(q) if q is not None else None @@ -408,7 +410,7 @@ def _transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: vector = int(np.round(vector / self.q, 0)) * self.q return vector - def _inverse_transform(self, vector: [None, np.ndarray]) -> Union[float, np.ndarray]: + def _inverse_transform(self, vector: Union[None, np.ndarray]) -> Union[float, np.ndarray]: if vector is None: return np.NaN @@ -416,7 +418,7 @@ def _inverse_transform(self, vector: [None, np.ndarray]) -> Union[float, np.ndar vector = np.log(vector) return vector - def get_neighbors(self, value: float, rs: np.random, number: int = 4) -> List[np.ndarray]: + def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: bool=False) -> List[float]: neighbors = [] for i in range(number): neighbors.append(rs.normal(value, self.sigma)) @@ -425,7 +427,7 @@ def get_neighbors(self, value: float, rs: np.random, number: int = 4) -> List[np class UniformIntegerHyperparameter(UniformMixin, IntegerHyperparameter): def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, - q: [float, None] = None, log: bool = False) -> None: + q: Union[int, None] = None, log: bool = False) -> None: self.lower = self.check_int(lower, "lower") self.upper = self.check_int(upper, "upper") if default is not None: @@ -511,8 +513,8 @@ def has_neighbors(self) -> bool: else: return False - def get_neighbors(self, value: Union[int, float], number: int = 4, transform: bool = False) -> List[int]: - neighbors = [] + def get_neighbors(self, value: Union[int, float], rs: None, number: int = 4, transform: bool = False) -> List[int]: + neighbors = [] # type: List[int] while len(neighbors) < number: rejected = True iteration = 0 From 04f4bb91b983f283049115a82c953892a5ea86b3 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 13 Jan 2017 15:51:19 +0100 Subject: [PATCH 04/22] commit --- ConfigSpace/hyperparameters.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 76fff6a0..25f31424 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -103,15 +103,14 @@ def get_num_neighbors(self): class Constant(Hyperparameter): def __init__(self, name: str, value: Union[str, int, float]) -> None: super(Constant, self).__init__(name) - allowed_types_list = [] # type: List[str] - allowed_types_list.extend(str(int)) - allowed_types_list.append(str(float)) - allowed_types_list.extend(str(str)) - allowed_types_list.append(str(str)) + allowed_types_list = [] # type: List[type] + allowed_types_list.append(int) + allowed_types_list.append(float) + allowed_types_list.append(str) allowed_types = tuple(allowed_types_list) - if not isinstance(value, allowed_types_list) or \ + if not isinstance(value, allowed_types) or \ isinstance(value, bool): raise TypeError("Constant value is of type %s, but only the " "following types are allowed: %s" % From 098ae6592ed91fa8ca52592d63e021e2b32dc58f Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 13 Jan 2017 18:44:38 +0100 Subject: [PATCH 05/22] annotation of hyperparameters.py --- ConfigSpace/hyperparameters.py | 72 ++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 25f31424..ba24d9c9 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -96,7 +96,7 @@ def get_neighbors(self, value, rs, number, transform=False): raise NotImplementedError() @abstractmethod - def get_num_neighbors(self): + def get_num_neighbors(self, value): raise NotImplementedError() @@ -145,7 +145,7 @@ def _inverse_transform(self, vector: np.ndarray) -> Union[int, np.NaN]: def has_neighbors(self) -> bool: return False - def get_num_neighbors(self) -> int: + def get_num_neighbors(self, value: None) -> int: return 0 def get_neighbors(self, value: Any, rs: Any, number: int, transform: bool = False) -> List: @@ -165,7 +165,7 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self) -> np.inf: + def get_num_neighbors(self, value: None) -> np.inf: return np.inf @@ -238,7 +238,7 @@ def check_default(self, default): class UniformFloatHyperparameter(UniformMixin, FloatHyperparameter): def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float], - default: Union[int, float, None] = None, q: [int, float, None] = None, log: bool = False) -> None: + default: Union[int, float, None] = None, q: Union[int, float, None] = None, log: bool = False) -> None: self.lower = float(lower) self.upper = float(upper) self.q = float(q) if q is not None else None @@ -302,7 +302,7 @@ def to_integer(self) -> UniformIntegerHyperparameter: # todo check if params should be converted to int while class initialization or inside class itself return UniformIntegerHyperparameter(self.name, int(self.lower), int(self.upper), - int(np.round(self.default)), self.q, + int(np.round(self.default)), int(self.q), self.log) # todo: rs? prabably numpy.random.uniform @@ -385,9 +385,8 @@ def to_uniform(self, z: int = 3) -> UniformFloatHyperparameter: def to_integer(self) -> NormalIntegerHyperparameter: return NormalIntegerHyperparameter(self.name, self.mu, self.sigma, - default=int( - np.round(self.default, 0)), - q=self.q, log=self.log) + default=int(np.round(self.default, 0)), + q=int(self.q), log=self.log) def is_legal(self, value: Union[float, int]) -> bool: if isinstance(value, (float, int)): @@ -512,13 +511,14 @@ def has_neighbors(self) -> bool: else: return False - def get_neighbors(self, value: Union[int, float], rs: None, number: int = 4, transform: bool = False) -> List[int]: + def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> List[int]: neighbors = [] # type: List[int] while len(neighbors) < number: rejected = True iteration = 0 while rejected: - new_value = np.max((0, min(1, rs.normal(value, 0.2)))) + new_min_value = np.min(1, rs.normal(value, 0.2)) + new_value = np.max((0, new_min_value)) int_value = self._transform(value) new_int_value = self._transform(new_value) if int_value != new_int_value: @@ -536,7 +536,7 @@ def get_neighbors(self, value: Union[int, float], rs: None, number: int = 4, tra class NormalIntegerHyperparameter(NormalMixin, IntegerHyperparameter): def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], - default: Union[int, None] = None, q: Union[None, int, float] = None, log: bool = False): + default: Union[int, None] = None, q: Union[None, int] = None, log: bool = False) -> None: self.mu = mu self.sigma = sigma if default is not None: @@ -586,11 +586,11 @@ def __eq__(self, other: Any) -> bool: self.q == other.q]) else: return False - + # todo check if conversion should be done in initiation call or inside class itsel def to_uniform(self, z: int = 3) -> UniformIntegerHyperparameter: return UniformIntegerHyperparameter(self.name, - self.mu - (z * self.sigma), - self.mu + (z * self.sigma), + int(self.mu - (z * self.sigma)), + int(self.mu + (z * self.sigma)), default=self.default, q=self.q, log=self.log) @@ -623,9 +623,10 @@ def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: def has_neighbors(self) -> bool: return True - - def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> None: - neighbors = [] + # todo : find whay doesnt this function return anything + def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) ->\ + List[Union[np.ndarray, float, int]]: + neighbors = [] # type: List[Union[np.ndarray, float, int]] while len(neighbors) < number: rejected = True iteration = 0 @@ -643,11 +644,13 @@ def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4 neighbors.append(self._transform(new_value)) else: neighbors.append(new_value) + return neighbors class CategoricalHyperparameter(Hyperparameter): # TODO add more magic for automated type recognition - def __init__(self, name: str, choices: List[str], default: Union[str, None] = None) -> None: + def __init__(self, name: str, choices: List[Union[str, float, int]], default: Union[int, float, str, None] = None)\ + -> None: super(CategoricalHyperparameter, self).__init__(name) # TODO check that there is no bullshit in the choices! self.choices = choices @@ -667,13 +670,13 @@ def __repr__(self) -> str: repr_str.seek(0) return repr_str.getvalue() - def is_legal(self, value: str) -> bool: + def is_legal(self, value: Union[None, str, float, int]) -> bool: if value in self.choices: return True else: return False - def check_default(self, default: Union[None, str]) -> str: + def check_default(self, default: Union[None, str, float, int]) -> Union[str, float, int]: if default is None: return self.choices[0] elif self.is_legal(default): @@ -681,12 +684,12 @@ def check_default(self, default: Union[None, str]) -> str: else: raise ValueError("Illegal default value %s" % str(default)) - def _sample(self, rs: np.randint, size: int = None) -> np.ndarray: + def _sample(self, rs: np.randint, size: int = None) -> Union[int, np.ndarray]: return rs.randint(0, self._num_choices, size=size) # todo recheck - def _transform(self, vector: np.ndarray) -> Union[None, List[str]]: - if vector != vector: + def _transform(self, vector: np.ndarray) -> Union[None, str, int, float]: + if not np.isfinite(vector): return None if np.equal(np.mod(vector, 1), 0): return self.choices[int(vector)] @@ -696,7 +699,7 @@ def _transform(self, vector: np.ndarray) -> Union[None, List[str]]: 'the following float: %f' % (self, vector)) # todo recheck - def _inverse_transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: + def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, float] : if vector is None: return np.NaN return self.choices.index(vector) @@ -704,12 +707,12 @@ def _inverse_transform(self, vector: Union[None, np.ndarray]) -> np.ndarray: def has_neighbors(self) -> bool: return len(self.choices) > 1 - def get_num_neighbors(self) -> int: + def get_num_neighbors(self, value: None) -> int: return len(self.choices) - 1 def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ - List[float]: - neighbors = [] + List[Union[float, int, str]]: + neighbors = [] # type: List[Union[float, int, str]] if number < len(self.choices): while len(neighbors) < number: rejected = True @@ -744,8 +747,8 @@ def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = n class OrdinalHyperparameter(Hyperparameter): - def __init__(self, name: str, sequence: Union[List[float], List[int], List[str]], - default: Union[str, int, float, None] = None): + def __init__(self, name: str, sequence: List[Union[float, int, str]], + default: Union[str, int, float, None] = None) -> None: """ since the sequence can consist of elements from different types we store them into a dictionary in order to handle them as a @@ -755,11 +758,11 @@ def __init__(self, name: str, sequence: Union[List[float], List[int], List[str]] self.sequence = sequence self._num_elements = len(sequence) self.default = self.check_default(default) - - self.value_dict = OrderedDict() + # todo recheck type of sequence + self.value_dict = OrderedDict() # type : OrderedDict[Union[int, float, str], int]] counter = 1 for element in self.sequence: - self.value_dict[element] = counter + self.value_dict[element] = counter # type : OrderedDict[Union[int, float, str], int]] counter += 1 def __repr__(self) -> str: @@ -797,7 +800,8 @@ def check_default(self, default: Union[int, float, str, None]) -> Union[int, flo else: raise ValueError("Illegal default value %s" % str(default)) - def _transform(self, vector: np.ndarray) -> Union[None, List[int], List[str], List[float]]: + # todo recheck return type...is it list or normal + def _transform(self, vector: np.ndarray) -> Union[None, int, str, float]: if vector != vector: return None if np.equal(np.mod(vector, 1), 0): @@ -867,7 +871,7 @@ def get_num_neighbors(self, value: Union[int, float, str]) -> int: # todo: recehck...added rs as param otherrwise mismatch with baseclass signature def get_neighbors(self, value: Union[int, str, float], rs: None, number: int = 2, transform: bool = False) \ - -> Union[List[int], List[str], List[float]]: + -> List[Union[str, float, int]]: """ Returns the neighbors of a given value. """ From ff277b72c6428487342f9e05f6835f318a4b4b80 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 15:41:01 +0100 Subject: [PATCH 06/22] fix return type np.NaN to float --- ConfigSpace/hyperparameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index ba24d9c9..f25b4ed0 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -137,7 +137,7 @@ def _transform(self, vector: np.ndarray) -> Union[None, int, float, str]: return None return self.value - def _inverse_transform(self, vector: np.ndarray) -> Union[int, np.NaN]: + def _inverse_transform(self, vector: np.ndarray) -> Union[int, float]: if vector != self.value: return np.NaN return 0 From 64dc68138a2e75385fc8a17a89967706787daa6d Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 15:49:46 +0100 Subject: [PATCH 07/22] UniformIntegerHyperparameter -> 'UniformIntegerHyperparameter' --- ConfigSpace/hyperparameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index f25b4ed0..279ed1d7 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -297,7 +297,7 @@ def __eq__(self, other: Any) -> bool: else: return False - def to_integer(self) -> UniformIntegerHyperparameter: + def to_integer(self) -> 'UniformIntegerHyperparameter': # TODO check if conversion makes sense at all (at least two integer values possible!) # todo check if params should be converted to int while class initialization or inside class itself return UniformIntegerHyperparameter(self.name, int(self.lower), From cd58555c241f94fbc885a7ca095bb057b1a0452f Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 15:55:43 +0100 Subject: [PATCH 08/22] np.NaN -> float --- ConfigSpace/hyperparameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 279ed1d7..8f31ca78 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -321,7 +321,7 @@ def _transform(self, vector: np.ndarray) -> Union[np.ndarray, None]: vector = int(np.round(vector / self.q, 0)) * self.q return vector - def _inverse_transform(self, vector: Union[np.ndarray, None]) -> Union[np.NaN, np.ndarray]: + def _inverse_transform(self, vector: Union[np.ndarray, None]) -> Union[float, np.ndarray]: if vector is None: return np.NaN if self.log: From 0d538da80a979cb48df49e2e0b8d035af1317f6c Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 16:02:28 +0100 Subject: [PATCH 09/22] fix --- ConfigSpace/hyperparameters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 8f31ca78..62342c43 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -375,7 +375,7 @@ def __eq__(self, other: Any) -> bool: else: return False - def to_uniform(self, z: int = 3) -> UniformFloatHyperparameter: + def to_uniform(self, z: int = 3) -> 'UniformFloatHyperparameter': return UniformFloatHyperparameter(self.name, self.mu - (z * self.sigma), self.mu + (z * self.sigma), @@ -383,7 +383,7 @@ def to_uniform(self, z: int = 3) -> UniformFloatHyperparameter: np.round(self.default, 0)), q=self.q, log=self.log) - def to_integer(self) -> NormalIntegerHyperparameter: + def to_integer(self) -> 'NormalIntegerHyperparameter': return NormalIntegerHyperparameter(self.name, self.mu, self.sigma, default=int(np.round(self.default, 0)), q=int(self.q), log=self.log) @@ -587,7 +587,7 @@ def __eq__(self, other: Any) -> bool: else: return False # todo check if conversion should be done in initiation call or inside class itsel - def to_uniform(self, z: int = 3) -> UniformIntegerHyperparameter: + def to_uniform(self, z: int = 3) -> 'UniformIntegerHyperparameter': return UniformIntegerHyperparameter(self.name, int(self.mu - (z * self.sigma)), int(self.mu + (z * self.sigma)), From 0e8bb3de86bc2b647655cc265ec0f04eb1a5312e Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 16:13:18 +0100 Subject: [PATCH 10/22] np.randint -> np.random --- ConfigSpace/hyperparameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 62342c43..af7eef3e 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -684,7 +684,7 @@ def check_default(self, default: Union[None, str, float, int]) -> Union[str, flo else: raise ValueError("Illegal default value %s" % str(default)) - def _sample(self, rs: np.randint, size: int = None) -> Union[int, np.ndarray]: + def _sample(self, rs: np.random, size: int = None) -> Union[int, np.ndarray]: return rs.randint(0, self._num_choices, size=size) # todo recheck From 1b4e37304242d6abe78ded93625f0743659c981d Mon Sep 17 00:00:00 2001 From: smohsinali Date: Sun, 15 Jan 2017 17:22:30 +0100 Subject: [PATCH 11/22] io.reduce->reduce --- ConfigSpace/conditions.py | 2 +- ConfigSpace/hyperparameters.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ConfigSpace/conditions.py b/ConfigSpace/conditions.py index 7a7b9860..7c592aeb 100644 --- a/ConfigSpace/conditions.py +++ b/ConfigSpace/conditions.py @@ -302,7 +302,7 @@ def __repr__(self): return retval.getvalue() def _evaluate(self, evaluations): - return io.reduce(operator.and_, evaluations) + return reduce(operator.and_, evaluations) class OrConjunction(AbstractConjunction): diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index af7eef3e..b9a59e0f 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -145,7 +145,7 @@ def _inverse_transform(self, vector: np.ndarray) -> Union[int, float]: def has_neighbors(self) -> bool: return False - def get_num_neighbors(self, value: None) -> int: + def get_num_neighbors(self, value: None=None) -> int: return 0 def get_neighbors(self, value: Any, rs: Any, number: int, transform: bool = False) -> List: @@ -165,7 +165,7 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self, value: None) -> np.inf: + def get_num_neighbors(self, value: None=None) -> np.inf: return np.inf @@ -707,7 +707,7 @@ def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, def has_neighbors(self) -> bool: return len(self.choices) > 1 - def get_num_neighbors(self, value: None) -> int: + def get_num_neighbors(self, value: None=None) -> int: return len(self.choices) - 1 def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ From 332f5f82e3b3f79504b4be780cb476a15377fdc3 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Thu, 19 Jan 2017 14:29:19 +0100 Subject: [PATCH 12/22] create class baseuniformfloathyparameter --- ConfigSpace/hyperparameters.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index b9a59e0f..79a75053 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -145,7 +145,7 @@ def _inverse_transform(self, vector: np.ndarray) -> Union[int, float]: def has_neighbors(self) -> bool: return False - def get_num_neighbors(self, value: None=None) -> int: + def get_num_neighbors(self, value: None) -> int: return 0 def get_neighbors(self, value: Any, rs: Any, number: int, transform: bool = False) -> List: @@ -165,10 +165,22 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self, value: None=None) -> np.inf: + def get_num_neighbors(self, value: None) -> float: return np.inf +class BaseUniformFloatHyperparameter(object): + __metaclass__ = ABCMeta + + @abstractmethod + def is_legal(self, value): + raise NotImplementedError() + + @abstractmethod + def check_default(self, default): + raise NotImplementedError() + + class FloatHyperparameter(NumericalHyperparameter): # todo : type of name and default? def __init__(self, name: str, default: Union[int, float]) -> None: From 4003448dfed65e40623309d846377ba4ae91a6d1 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 20 Jan 2017 11:46:47 +0100 Subject: [PATCH 13/22] Merge branch 'master' of https://github.com/automl/ConfigSpace into typecheck_hyperparameter_p3 # Conflicts: # ConfigSpace/hyperparameters.py --- ConfigSpace/hyperparameters.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index bff0a0a9..30444f1f 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -390,9 +390,13 @@ def to_uniform(self, z: int = 3) -> 'UniformFloatHyperparameter': q=self.q, log=self.log) def to_integer(self) -> 'NormalIntegerHyperparameter': + if self.q is None: + q_int = self.q + else: + q_int = int(self.q) return NormalIntegerHyperparameter(self.name, self.mu, self.sigma, default=int(np.round(self.default, 0)), - q=int(self.q), log=self.log) + q=q_int, log=self.log) def is_legal(self, value: Union[float, int]) -> bool: if isinstance(value, (float, int)): @@ -431,7 +435,7 @@ def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: class UniformIntegerHyperparameter(UniformMixin, IntegerHyperparameter): def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, - q: Union[int, None] = None, log: bool = False) -> None: + q: Union[int, float, None] = None, log: bool = False) -> None: self.lower = self.check_int(lower, "lower") self.upper = self.check_int(upper, "upper") if default is not None: @@ -876,7 +880,7 @@ def get_num_neighbors(self, value: Union[int, float, str]) -> int: return 2 # todo: recehck...added rs as param otherrwise mismatch with baseclass signature - def get_neighbors(self, value: Union[int, str, float], rs: None, number: int = 2, transform: bool = False) \ + def get_neighbors(self, value: Union[int, str, float], rs: None=None, number: int = 2, transform: bool = False) \ -> List[Union[str, float, int]]: """ Returns the neighbors of a given value. From 1c1234759ae83708ba1bda516ce3604c951a41a4 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Fri, 20 Jan 2017 15:09:00 +0100 Subject: [PATCH 14/22] make changes according to comments on pull request --- ConfigSpace/hyperparameters.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 30444f1f..3685d384 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -108,7 +108,7 @@ def __init__(self, name: str, value: Union[str, int, float]) -> None: isinstance(value, bool): raise TypeError("Constant value is of type %s, but only the " "following types are allowed: %s" % - (type(value), allowed_types)) + (type(value), allowed_types)) # type: ignore self.value = value self.default = value @@ -435,7 +435,7 @@ def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: class UniformIntegerHyperparameter(UniformMixin, IntegerHyperparameter): def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, - q: Union[int, float, None] = None, log: bool = False) -> None: + q: Union[int, None] = None, log: bool = False) -> None: self.lower = self.check_int(lower, "lower") self.upper = self.check_int(upper, "upper") if default is not None: @@ -717,7 +717,7 @@ def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, def has_neighbors(self) -> bool: return len(self.choices) > 1 - def get_num_neighbors(self, value: None=None) -> int: + def get_num_neighbors(self, value=None) -> int: return len(self.choices) - 1 def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ @@ -769,10 +769,10 @@ def __init__(self, name: str, sequence: List[Union[float, int, str]], self._num_elements = len(sequence) self.default = self.check_default(default) # todo recheck type of sequence - self.value_dict = OrderedDict() # type : OrderedDict[Union[int, float, str], int]] + self.value_dict = OrderedDict() # type: OrderedDict[Union[int, float, str], int] counter = 1 for element in self.sequence: - self.value_dict[element] = counter # type : OrderedDict[Union[int, float, str], int]] + self.value_dict[element] = counter counter += 1 def __repr__(self) -> str: @@ -880,7 +880,7 @@ def get_num_neighbors(self, value: Union[int, float, str]) -> int: return 2 # todo: recehck...added rs as param otherrwise mismatch with baseclass signature - def get_neighbors(self, value: Union[int, str, float], rs: None=None, number: int = 2, transform: bool = False) \ + def get_neighbors(self, value: Union[int, str, float], rs=None, number: int = 2, transform: bool = False) \ -> List[Union[str, float, int]]: """ Returns the neighbors of a given value. From 1677cc827cc9105b08dae139ecd01f3dc5295a77 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Wed, 25 Jan 2017 02:10:57 +0100 Subject: [PATCH 15/22] is_leagal and check_default functions --- ConfigSpace/configuration_space.py | 2 +- ConfigSpace/hyperparameters.py | 258 ++++++++++++++++++++--------- test/test_hyperparameters.py | 33 ++-- 3 files changed, 205 insertions(+), 88 deletions(-) diff --git a/ConfigSpace/configuration_space.py b/ConfigSpace/configuration_space.py index a8fd0afd..665a9e89 100644 --- a/ConfigSpace/configuration_space.py +++ b/ConfigSpace/configuration_space.py @@ -33,7 +33,7 @@ import io from functools import reduce import ConfigSpace.nx -from ConfigSpace.hyperparameters import Hyperparameter, Constant, FloatHyperparameter +from ConfigSpace.hyperparameters import Hyperparameter, Constant from ConfigSpace.conditions import ConditionComponent, \ AbstractCondition, AbstractConjunction, EqualsCondition from ConfigSpace.forbidden import AbstractForbiddenComponent diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 3685d384..58fbe4f7 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -35,6 +35,94 @@ import numpy as np +def is_legal_uniformfloat(value: Union[float], upper: Union[float], lower: Union[int, float]) -> bool: + if not (isinstance(value, float) or isinstance(value, int)): + return False + # Strange numerical issues! + elif upper >= value >= (lower - 0.0000000001): + return True + else: + return False + + +def is_legal_normalfloat(value: Union[int, float]) -> bool: + return isinstance(value, float) or isinstance(value, int) + + +def check_default_uniformfloat(default: float, upper: float, lower: float, + log: bool) -> Union[int, float]: + if default is None: + if log: + default = np.exp((np.log(lower) + np.log(upper)) / 2.) + else: + default = (lower + upper) / 2. + default = np.round(float(default), 10) + + if is_legal_uniformfloat(default, upper, lower): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) + + +def check_default_normalfloat(default: Union[int, float], mu: Union[int, float]) -> Union[int, float]: + if default is None: + return mu + + elif is_legal_normalfloat(default): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) + +#################################################################### + +def is_legal_uniforminteger(value: int, upper: float, lower: float) -> bool: + if not (isinstance(value, (int, np.int, np.int32, np.int64))): + return False + # Strange numerical issues! + elif upper >= value >= (lower - 0.0000000001): + return True + else: + return False + + +def is_legal_normalinteger(value: int) -> bool: + return isinstance(value, (int, np.int, np.int32, np.int64)) + + +def check_default_uniforminteger(default: Union[int, float], upper: float, lower: float, log: bool) -> int: + if default is None: + if log: + default = np.exp((np.log(lower) + np.log(upper)) / 2.) + else: + default = (lower + upper) / 2. + default = int(np.round(default, 0)) + + if is_legal_uniforminteger(default, upper, lower): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) + + +def check_default_normalinteger(default: int, mu: int) -> int: + if default is None: + return mu + + elif is_legal_normalinteger(default): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) + + +def check_int(parameter: int, name: str) -> int: + if abs(int(parameter) - parameter) > 0.00000001 and \ + type(parameter) is not int: + raise ValueError("For the Integer parameter %s, the value must be " + "an Integer, too. Right now it is a %s with value" + " %s." % (name, type(parameter), str(parameter))) + return int(parameter) + +######################################################################### + class Hyperparameter(object): __metaclass__ = ABCMeta @@ -108,7 +196,7 @@ def __init__(self, name: str, value: Union[str, int, float]) -> None: isinstance(value, bool): raise TypeError("Constant value is of type %s, but only the " "following types are allowed: %s" % - (type(value), allowed_types)) # type: ignore + (type(value), allowed_types)) # type: ignore self.value = value self.default = value @@ -139,7 +227,7 @@ def _inverse_transform(self, vector: np.ndarray) -> Union[int, float]: def has_neighbors(self) -> bool: return False - def get_num_neighbors(self, value: None) -> int: + def get_num_neighbors(self, value=None) -> int: return 0 def get_neighbors(self, value: Any, rs: Any, number: int, transform: bool = False) -> List: @@ -159,7 +247,7 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self, value: None) -> float: + def get_num_neighbors(self, value=None) -> float: return np.inf @@ -178,15 +266,17 @@ def check_default(self, default): class FloatHyperparameter(NumericalHyperparameter): # todo : type of name and default? def __init__(self, name: str, default: Union[int, float]) -> None: - super(FloatHyperparameter, self).__init__(name, default) + # super(FloatHyperparameter, self).__init__(name, default) + pass def is_legal(self, value: Union[int, float]) -> bool: - return isinstance(value, float) or isinstance(value, int) + # return isinstance(value, float) or isinstance(value, int) + pass # todo : recheck default def check_default(self, default: Union[int, float]) -> float: - return np.round(float(default), 10) - + # return np.round(float(default), 10) + pass class IntegerHyperparameter(NumericalHyperparameter): # todo : type of name and default? @@ -194,61 +284,64 @@ def __init__(self, name: str, default: int) -> None: super(IntegerHyperparameter, self).__init__(name, default) def is_legal(self, value: int) -> bool: - return isinstance(value, (int, np.int, np.int32, np.int64)) + # return isinstance(value, (int, np.int, np.int32, np.int64)) + pass def check_int(self, parameter: int, name: str) -> int: - if abs(int(parameter) - parameter) > 0.00000001 and \ - type(parameter) is not int: - raise ValueError("For the Integer parameter %s, the value must be " - "an Integer, too. Right now it is a %s with value" - " %s." % (name, type(parameter), str(parameter))) - return int(parameter) + # if abs(int(parameter) - parameter) > 0.00000001 and \ + # type(parameter) is not int: + # raise ValueError("For the Integer parameter %s, the value must be " + # "an Integer, too. Right now it is a %s with value" + # " %s." % (name, type(parameter), str(parameter))) + # return int(parameter) + pass def check_default(self, default: int) -> int: - return int(np.round(default, 0)) - - -# todo: find out purpose of mixin and annotate it? -class UniformMixin(object): - def is_legal(self, value) -> bool: - if not super(UniformMixin, self).is_legal(value): - return False - # Strange numerical issues! - elif self.upper >= value >= (self.lower - 0.0000000001): - return True - else: - return False - - def check_default(self, default): - if default is None: - if self.log: - default = np.exp((np.log(self.lower) + np.log(self.upper)) / 2.) - else: - default = (self.lower + self.upper) / 2. - default = super(UniformMixin, self).check_default(default) - if self.is_legal(default): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) - - -class NormalMixin(object): - def check_default(self, default): - if default is None: - return self.mu - elif self.is_legal(default): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) + # return int(np.round(default, 0)) + pass + +# # todo: find out purpose of mixin and annotate it? +# class UniformMixin(object): +# def is_legal(self, value) -> bool: +# if not super(UniformMixin, self).is_legal(value): +# return False +# # Strange numerical issues! +# elif self.upper >= value >= (self.lower - 0.0000000001): +# return True +# else: +# return False +# +# def check_default(self, default): +# if default is None: +# if self.log: +# default = np.exp((np.log(self.lower) + np.log(self.upper)) / 2.) +# else: +# default = (self.lower + self.upper) / 2. +# default = super(UniformMixin, self).check_default(default) +# if self.is_legal(default): +# return default +# else: +# raise ValueError("Illegal default value %s" % str(default)) +# +# +# class NormalMixin(object): +# def check_default(self, default): +# if default is None: +# return self.mu +# elif self.is_legal(default): +# return default +# else: +# raise ValueError("Illegal default value %s" % str(default)) -class UniformFloatHyperparameter(UniformMixin, FloatHyperparameter): +class UniformFloatHyperparameter(FloatHyperparameter): def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float], default: Union[int, float, None] = None, q: Union[int, float, None] = None, log: bool = False) -> None: self.lower = float(lower) self.upper = float(upper) self.q = float(q) if q is not None else None self.log = bool(log) + self.name = name if self.lower >= self.upper: raise ValueError("Upper bound %f must be larger than lower bound " @@ -259,8 +352,9 @@ def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float] "hyperparameter %s is forbidden." % (self.lower, name)) - super(UniformFloatHyperparameter, self). \ - __init__(name, self.check_default(default)) + # super(UniformFloatHyperparameter, self). \ + # __init__(name, self.check_default(default)) + self.default = check_default_uniformfloat(default, self.upper, self.lower, self.log) if self.log: if self.q is not None: @@ -347,15 +441,17 @@ def get_neighbors(self, value: Any, rs: np.random, number: int = 4, transform: b return neighbors -class NormalFloatHyperparameter(NormalMixin, FloatHyperparameter): +class NormalFloatHyperparameter(FloatHyperparameter): def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], default: Union[None, float] = None, q: Union[int, float, None] = None, log: bool = False) -> None: self.mu = float(mu) self.sigma = float(sigma) self.q = float(q) if q is not None else None self.log = bool(log) - super(NormalFloatHyperparameter, self). \ - __init__(name, self.check_default(default)) + self.name = name + # super(NormalFloatHyperparameter, self). \ + # __init__(name, self.check_default(default)) + self.default = check_default_normalfloat(default, self.mu) def __repr__(self) -> str: repr_str = io.StringIO() @@ -391,10 +487,10 @@ def to_uniform(self, z: int = 3) -> 'UniformFloatHyperparameter': def to_integer(self) -> 'NormalIntegerHyperparameter': if self.q is None: - q_int = self.q + q_int = None else: q_int = int(self.q) - return NormalIntegerHyperparameter(self.name, self.mu, self.sigma, + return NormalIntegerHyperparameter(self.name, int(self.mu), self.sigma, default=int(np.round(self.default, 0)), q=q_int, log=self.log) @@ -426,20 +522,22 @@ def _inverse_transform(self, vector: Union[None, np.ndarray]) -> Union[float, np vector = np.log(vector) return vector - def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: bool=False) -> List[float]: + def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: bool = False) -> List[float]: neighbors = [] for i in range(number): neighbors.append(rs.normal(value, self.sigma)) return neighbors -class UniformIntegerHyperparameter(UniformMixin, IntegerHyperparameter): +class UniformIntegerHyperparameter(IntegerHyperparameter): def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, q: Union[int, None] = None, log: bool = False) -> None: - self.lower = self.check_int(lower, "lower") - self.upper = self.check_int(upper, "upper") + self.lower = check_int(lower, "lower") + self.upper = check_int(upper, "upper") + self.name = name if default is not None: - default = self.check_int(default, name) + default = check_int(default, name) + if q is not None: if q < 1: warnings.warn("Setting quantization < 1 for Integer " @@ -461,8 +559,9 @@ def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] "hyperparameter %s is forbidden." % (self.lower, name)) - super(UniformIntegerHyperparameter, self). \ - __init__(name, self.check_default(default)) + # super(UniformIntegerHyperparameter, self). \ + # __init__(name, self.check_default(default)) + self.default = check_default_uniforminteger(default, upper, lower, log) self.ufhp = UniformFloatHyperparameter(self.name, self.lower - 0.49999, @@ -521,7 +620,8 @@ def has_neighbors(self) -> bool: else: return False - def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> List[int]: + def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> List[ + int]: neighbors = [] # type: List[int] while len(neighbors) < number: rejected = True @@ -544,13 +644,15 @@ def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4 return neighbors -class NormalIntegerHyperparameter(NormalMixin, IntegerHyperparameter): - def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], +class NormalIntegerHyperparameter(IntegerHyperparameter): + def __init__(self, name: str, mu: int, sigma: Union[int, float], default: Union[int, None] = None, q: Union[None, int] = None, log: bool = False) -> None: self.mu = mu self.sigma = sigma + self.name = name if default is not None: - default = self.check_int(default, name) + default = check_int(default, self.name) + if q is not None: if q < 1: warnings.warn("Setting quantization < 1 for Integer " @@ -558,13 +660,14 @@ def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], name) self.q = None else: - self.q = self.check_int(q, "q") + self.q = check_int(q, "q") else: self.q = None self.log = bool(log) - super(NormalIntegerHyperparameter, self). \ - __init__(name, self.check_default(default)) + # super(NormalIntegerHyperparameter, self). \ + # __init__(name, self.check_default(default)) + self.default = check_default_normalinteger(default, self.mu) self.nfhp = NormalFloatHyperparameter(self.name, self.mu, @@ -596,6 +699,7 @@ def __eq__(self, other: Any) -> bool: self.q == other.q]) else: return False + # todo check if conversion should be done in initiation call or inside class itsel def to_uniform(self, z: int = 3) -> 'UniformIntegerHyperparameter': return UniformIntegerHyperparameter(self.name, @@ -633,8 +737,9 @@ def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: def has_neighbors(self) -> bool: return True + # todo : find whay doesnt this function return anything - def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) ->\ + def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> \ List[Union[np.ndarray, float, int]]: neighbors = [] # type: List[Union[np.ndarray, float, int]] while len(neighbors) < number: @@ -659,7 +764,7 @@ def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4 class CategoricalHyperparameter(Hyperparameter): # TODO add more magic for automated type recognition - def __init__(self, name: str, choices: List[Union[str, float, int]], default: Union[int, float, str, None] = None)\ + def __init__(self, name: str, choices: List[Union[str, float, int]], default: Union[int, float, str, None] = None) \ -> None: super(CategoricalHyperparameter, self).__init__(name) # TODO check that there is no bullshit in the choices! @@ -709,7 +814,7 @@ def _transform(self, vector: np.ndarray) -> Union[None, str, int, float]: 'the following float: %f' % (self, vector)) # todo recheck - def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, float] : + def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, float]: if vector is None: return np.NaN return self.choices.index(vector) @@ -721,8 +826,8 @@ def get_num_neighbors(self, value=None) -> int: return len(self.choices) - 1 def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ - List[Union[float, int, str]]: - neighbors = [] # type: List[Union[float, int, str]] + List[Union[float, int, str]]: + neighbors = [] # type: List[Union[float, int, str]] if number < len(self.choices): while len(neighbors) < number: rejected = True @@ -907,4 +1012,3 @@ def get_neighbors(self, value: Union[int, str, float], rs=None, number: int = 2, neighbors.append(neighbor_idx2) return neighbors - diff --git a/test/test_hyperparameters.py b/test/test_hyperparameters.py index a5aae0d6..b9f90e67 100644 --- a/test/test_hyperparameters.py +++ b/test/test_hyperparameters.py @@ -35,7 +35,9 @@ from ConfigSpace.hyperparameters import Constant, \ UniformFloatHyperparameter, NormalFloatHyperparameter, \ UniformIntegerHyperparameter, NormalIntegerHyperparameter, \ - CategoricalHyperparameter, OrdinalHyperparameter + CategoricalHyperparameter, OrdinalHyperparameter, \ + check_default_normalinteger, check_default_uniforminteger, check_default_normalfloat, check_default_uniformfloat, \ + check_int, is_legal_normalfloat, is_legal_uniformfloat, is_legal_normalinteger, is_legal_uniforminteger class TestHyperparameters(unittest.TestCase): @@ -125,13 +127,22 @@ def test_uniformfloat_to_integer(self): "Default: 3, on log-scale", str(f2)) def test_uniformfloat_is_legal(self): - f1 = UniformFloatHyperparameter("param", 0.1, 10, q=0.1, log=True) - self.assertTrue(f1.is_legal(3.0)) - self.assertTrue(f1.is_legal(3)) - self.assertFalse(f1.is_legal(-0.1)) - self.assertFalse(f1.is_legal(10.1)) - self.assertFalse(f1.is_legal("AAA")) - self.assertFalse(f1.is_legal(dict())) + lower = 0.1 + upper = 10 + f1 = UniformFloatHyperparameter("param", lower, upper, q=0.1, log=True) + + # self.assertTrue(f1.is_legal(3.0)) + # self.assertTrue(f1.is_legal(3)) + # self.assertFalse(f1.is_legal(-0.1)) + # self.assertFalse(f1.is_legal(10.1)) + # self.assertFalse(f1.is_legal("AAA")) + # self.assertFalse(f1.is_legal(dict())) + self.assertTrue(is_legal_uniformfloat(3.0, upper, lower)) + self.assertTrue(is_legal_uniformfloat(3, upper, lower)) + self.assertFalse(is_legal_uniformfloat(-0.1, upper, lower)) + self.assertFalse(is_legal_uniformfloat(10.1, upper, lower)) + self.assertFalse(is_legal_uniformfloat("AAA", upper, lower)) + self.assertFalse(is_legal_uniformfloat(dict(), upper, lower)) def test_uniformfloat_illegal_bounds(self): self.assertRaisesRegexp(ValueError, @@ -560,12 +571,14 @@ def test_log_space_conversion(self): lower, upper = 1e-5, 1e5 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - self.assertTrue(hyper.is_legal(hyper._transform(1.))) + # self.assertTrue(hyper.is_legal(hyper._transform(1.))) + self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper._upper, hyper._lower)) lower, upper = 1e-10, 1e10 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - self.assertTrue(hyper.is_legal(hyper._transform(1.))) + # self.assertTrue(hyper.is_legal(hyper._transform(1.))) + self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper._upper, hyper._lower)) def test_ordinal_is_legal(self): f1 = OrdinalHyperparameter("temp", From 3b1306ac147dbba89049b8f179a0f12239c93b1f Mon Sep 17 00:00:00 2001 From: smohsinali Date: Wed, 25 Jan 2017 02:15:47 +0100 Subject: [PATCH 16/22] is_leagal and check_default functions --- ConfigSpace/hyperparameters.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 417810f5..bad228a6 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -122,7 +122,6 @@ def check_int(parameter: int, name: str) -> int: return int(parameter) ######################################################################### - class Hyperparameter(object): __metaclass__ = ABCMeta From d14a9e18d791429bb3360eb07ec5f1f2deea1f8a Mon Sep 17 00:00:00 2001 From: smohsinali Date: Wed, 25 Jan 2017 02:33:45 +0100 Subject: [PATCH 17/22] is_leagal and check_default functions --- ConfigSpace/configuration_space.py | 2 +- ConfigSpace/hyperparameters.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ConfigSpace/configuration_space.py b/ConfigSpace/configuration_space.py index 665a9e89..a8fd0afd 100644 --- a/ConfigSpace/configuration_space.py +++ b/ConfigSpace/configuration_space.py @@ -33,7 +33,7 @@ import io from functools import reduce import ConfigSpace.nx -from ConfigSpace.hyperparameters import Hyperparameter, Constant +from ConfigSpace.hyperparameters import Hyperparameter, Constant, FloatHyperparameter from ConfigSpace.conditions import ConditionComponent, \ AbstractCondition, AbstractConjunction, EqualsCondition from ConfigSpace.forbidden import AbstractForbiddenComponent diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index bad228a6..46a9dc69 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -328,6 +328,7 @@ def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float] self.upper = float(upper) self.q = float(q) if q is not None else None self.log = bool(log) + self.name = name if self.lower >= self.upper: raise ValueError("Upper bound %f must be larger than lower bound " From aea22591be2e6b865d75d386a8456fb17a1538d7 Mon Sep 17 00:00:00 2001 From: Matthias Feurer Date: Wed, 25 Jan 2017 21:03:30 +0100 Subject: [PATCH 18/22] FIX some bugs introduced in previous restructuring --- ConfigSpace/hyperparameters.py | 14 +++++++++++--- test/test_hyperparameters.py | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 46a9dc69..6bc66131 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -122,8 +122,7 @@ def check_int(parameter: int, name: str) -> int: return int(parameter) ######################################################################### -class Hyperparameter(object): - __metaclass__ = ABCMeta +class Hyperparameter(object, metaclass=ABCMeta): @abstractmethod def __init__(self, name: str) -> None: @@ -256,6 +255,7 @@ class FloatHyperparameter(NumericalHyperparameter): def __init__(self, name: str, default: Union[int, float]) -> None: super(FloatHyperparameter, self).__init__(name, default) + @abstractmethod def is_legal(self, value: Union[int, float]) -> bool: # return isinstance(value, float) or isinstance(value, int) pass @@ -270,6 +270,7 @@ class IntegerHyperparameter(NumericalHyperparameter): def __init__(self, name: str, default: int) -> None: super(IntegerHyperparameter, self).__init__(name, default) + @abstractmethod def is_legal(self, value: int) -> bool: # return isinstance(value, (int, np.int, np.int32, np.int64)) pass @@ -384,6 +385,9 @@ def __eq__(self, other: Any) -> bool: else: return False + def is_legal(self, value: Union[int, float]): + return is_legal_uniformfloat(value, lower=self.lower, upper=self.upper) + def to_integer(self) -> 'UniformIntegerHyperparameter': # TODO check if conversion makes sense at all (at least two integer values possible!) # todo check if params should be converted to int while class initialization or inside class itself @@ -593,6 +597,10 @@ def _transform(self, vector: np.ndarray) -> np.ndarray: def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: return self.ufhp._inverse_transform(vector) + def is_legal(self, value: int) -> bool: + return is_legal_uniforminteger(value, self.upper, self.lower) + + def has_neighbors(self) -> bool: if self.log: upper = np.exp(self.ufhp._upper) @@ -614,7 +622,7 @@ def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4 rejected = True iteration = 0 while rejected: - new_min_value = np.min(1, rs.normal(value, 0.2)) + new_min_value = np.min([1, rs.normal(loc=value, scale=0.2)]) new_value = np.max((0, new_min_value)) int_value = self._transform(value) new_int_value = self._transform(new_value) diff --git a/test/test_hyperparameters.py b/test/test_hyperparameters.py index b9f90e67..5f1949f5 100644 --- a/test/test_hyperparameters.py +++ b/test/test_hyperparameters.py @@ -617,10 +617,10 @@ def test_ordinal_get_seq_order(self): def test_ordinal_get_neighbors(self): f1 = OrdinalHyperparameter("temp", ["freezing", "cold", "warm", "hot"]) - self.assertEqual(f1.get_neighbors("freezing"), [2]) - self.assertEqual(f1.get_neighbors("cold", transform =True), ["freezing", "warm"]) - self.assertEqual(f1.get_neighbors("hot"), [3]) - self.assertEqual(f1.get_neighbors("hot", transform =True), ["warm"]) + self.assertEqual(f1.get_neighbors("freezing", rs=None), [2]) + self.assertEqual(f1.get_neighbors("cold", transform=True, rs=None), ["freezing", "warm"]) + self.assertEqual(f1.get_neighbors("hot", rs=None), [3]) + self.assertEqual(f1.get_neighbors("hot", transform =True, rs=None), ["warm"]) def test_get_num_neighbors(self): f1 = OrdinalHyperparameter("temp", From f921ef50b87e420bd3800eb28fe3cb1696b56131 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Thu, 26 Jan 2017 16:25:51 +0100 Subject: [PATCH 19/22] change tolerance for floats change self.check_int to check_int --- ConfigSpace/hyperparameters.py | 25 +++++++++---------------- test/test_hyperparameters.py | 6 ++---- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 6bc66131..490afbee 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -39,7 +39,8 @@ def is_legal_uniformfloat(value: Union[float], upper: Union[float], lower: Union if not (isinstance(value, float) or isinstance(value, int)): return False # Strange numerical issues! - elif upper >= value >= (lower - 0.0000000001): + # todo: discuss acceptable tolerance (without big tolerance for upper the test fails) + elif (upper + 0.00001) >= value >= (lower - 0.0000000001): return True else: return False @@ -258,12 +259,12 @@ def __init__(self, name: str, default: Union[int, float]) -> None: @abstractmethod def is_legal(self, value: Union[int, float]) -> bool: # return isinstance(value, float) or isinstance(value, int) - pass + raise NotImplemented # todo : recheck default def check_default(self, default: Union[int, float]) -> float: # return np.round(float(default), 10) - pass + raise NotImplemented class IntegerHyperparameter(NumericalHyperparameter): # todo : type of name and default? @@ -272,21 +273,13 @@ def __init__(self, name: str, default: int) -> None: @abstractmethod def is_legal(self, value: int) -> bool: - # return isinstance(value, (int, np.int, np.int32, np.int64)) - pass + raise NotImplemented def check_int(self, parameter: int, name: str) -> int: - # if abs(int(parameter) - parameter) > 0.00000001 and \ - # type(parameter) is not int: - # raise ValueError("For the Integer parameter %s, the value must be " - # "an Integer, too. Right now it is a %s with value" - # " %s." % (name, type(parameter), str(parameter))) - # return int(parameter) - pass + raise NotImplemented def check_default(self, default: int) -> int: - # return int(np.round(default, 0)) - pass + raise NotImplemented # # todo: find out purpose of mixin and annotate it? # class UniformMixin(object): @@ -536,7 +529,7 @@ def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] name) self.q = None else: - self.q = self.check_int(q, "q") + self.q = check_int(q, "q") else: self.q = None self.log = bool(log) @@ -655,7 +648,7 @@ def __init__(self, name: str, mu: int, sigma: Union[int, float], name) self.q = None else: - self.q = self.check_int(q, "q") + self.q = check_int(q, "q") else: self.q = None self.log = bool(log) diff --git a/test/test_hyperparameters.py b/test/test_hyperparameters.py index 5f1949f5..a7491cd0 100644 --- a/test/test_hyperparameters.py +++ b/test/test_hyperparameters.py @@ -571,14 +571,12 @@ def test_log_space_conversion(self): lower, upper = 1e-5, 1e5 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - # self.assertTrue(hyper.is_legal(hyper._transform(1.))) - self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper._upper, hyper._lower)) + self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper.upper, hyper.lower)) lower, upper = 1e-10, 1e10 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - # self.assertTrue(hyper.is_legal(hyper._transform(1.))) - self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper._upper, hyper._lower)) + self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper.upper, hyper.lower)) def test_ordinal_is_legal(self): f1 = OrdinalHyperparameter("temp", From 284068391d93a1358113208475b9685d8de868d7 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Mon, 30 Jan 2017 12:09:51 +0100 Subject: [PATCH 20/22] put is_legal and check_defalut functions to specific classes --- ConfigSpace/hyperparameters.py | 259 +++++++++++---------------------- 1 file changed, 87 insertions(+), 172 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 490afbee..223da1ec 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -35,94 +35,6 @@ import numpy as np -def is_legal_uniformfloat(value: Union[float], upper: Union[float], lower: Union[int, float]) -> bool: - if not (isinstance(value, float) or isinstance(value, int)): - return False - # Strange numerical issues! - # todo: discuss acceptable tolerance (without big tolerance for upper the test fails) - elif (upper + 0.00001) >= value >= (lower - 0.0000000001): - return True - else: - return False - - -def is_legal_normalfloat(value: Union[int, float]) -> bool: - return isinstance(value, float) or isinstance(value, int) - - -def check_default_uniformfloat(default: float, upper: float, lower: float, - log: bool) -> Union[int, float]: - if default is None: - if log: - default = np.exp((np.log(lower) + np.log(upper)) / 2.) - else: - default = (lower + upper) / 2. - default = np.round(float(default), 10) - - if is_legal_uniformfloat(default, upper, lower): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) - - -def check_default_normalfloat(default: Union[int, float], mu: Union[int, float]) -> Union[int, float]: - if default is None: - return mu - - elif is_legal_normalfloat(default): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) - -#################################################################### - -def is_legal_uniforminteger(value: int, upper: float, lower: float) -> bool: - if not (isinstance(value, (int, np.int, np.int32, np.int64))): - return False - # Strange numerical issues! - elif upper >= value >= (lower - 0.0000000001): - return True - else: - return False - - -def is_legal_normalinteger(value: int) -> bool: - return isinstance(value, (int, np.int, np.int32, np.int64)) - - -def check_default_uniforminteger(default: Union[int, float], upper: float, lower: float, log: bool) -> int: - if default is None: - if log: - default = np.exp((np.log(lower) + np.log(upper)) / 2.) - else: - default = (lower + upper) / 2. - default = int(np.round(default, 0)) - - if is_legal_uniforminteger(default, upper, lower): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) - - -def check_default_normalinteger(default: int, mu: int) -> int: - if default is None: - return mu - - elif is_legal_normalinteger(default): - return default - else: - raise ValueError("Illegal default value %s" % str(default)) - - -def check_int(parameter: int, name: str) -> int: - if abs(int(parameter) - parameter) > 0.00000001 and \ - type(parameter) is not int: - raise ValueError("For the Integer parameter %s, the value must be " - "an Integer, too. Right now it is a %s with value" - " %s." % (name, type(parameter), str(parameter))) - return int(parameter) - -######################################################################### class Hyperparameter(object, metaclass=ABCMeta): @abstractmethod @@ -213,7 +125,6 @@ def is_legal(self, value: Union[str, int, float]) -> bool: def _sample(self, rs: None, size: int = None) -> Union[int, np.ndarray]: return 0 if size == 1 else np.zeros((size,)) - # todo : recheck def _transform(self, vector: np.ndarray) -> Union[None, int, float, str]: if not np.isfinite(vector): return None @@ -239,7 +150,6 @@ class UnParametrizedHyperparameter(Constant): class NumericalHyperparameter(Hyperparameter): - # todo : type of name and default? def __init__(self, name: str, default: Any) -> None: super(NumericalHyperparameter, self).__init__(name) self.default = default @@ -247,27 +157,24 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self, value: None=None) -> np.inf: + def get_num_neighbors(self, value: None) -> np.inf: return np.inf class FloatHyperparameter(NumericalHyperparameter): - # todo : type of name and default? def __init__(self, name: str, default: Union[int, float]) -> None: super(FloatHyperparameter, self).__init__(name, default) @abstractmethod def is_legal(self, value: Union[int, float]) -> bool: - # return isinstance(value, float) or isinstance(value, int) raise NotImplemented - # todo : recheck default + @abstractmethod def check_default(self, default: Union[int, float]) -> float: - # return np.round(float(default), 10) raise NotImplemented + class IntegerHyperparameter(NumericalHyperparameter): - # todo : type of name and default? def __init__(self, name: str, default: int) -> None: super(IntegerHyperparameter, self).__init__(name, default) @@ -275,49 +182,24 @@ def __init__(self, name: str, default: int) -> None: def is_legal(self, value: int) -> bool: raise NotImplemented - def check_int(self, parameter: int, name: str) -> int: - raise NotImplemented - - def check_default(self, default: int) -> int: + @abstractmethod + def check_default(self, default) -> int: raise NotImplemented -# # todo: find out purpose of mixin and annotate it? -# class UniformMixin(object): -# def is_legal(self, value) -> bool: -# if not super(UniformMixin, self).is_legal(value): -# return False -# # Strange numerical issues! -# elif self.upper >= value >= (self.lower - 0.0000000001): -# return True -# else: -# return False -# -# def check_default(self, default): -# if default is None: -# if self.log: -# default = np.exp((np.log(self.lower) + np.log(self.upper)) / 2.) -# else: -# default = (self.lower + self.upper) / 2. -# default = super(UniformMixin, self).check_default(default) -# if self.is_legal(default): -# return default -# else: -# raise ValueError("Illegal default value %s" % str(default)) -# -# -# class NormalMixin(object): -# def check_default(self, default): -# if default is None: -# return self.mu -# elif self.is_legal(default): -# return default -# else: -# raise ValueError("Illegal default value %s" % str(default)) + def check_int(self, parameter: int, name: str) -> int: + if abs(int(parameter) - parameter) > 0.00000001 and \ + type(parameter) is not int: + raise ValueError("For the Integer parameter %s, the value must be " + "an Integer, too. Right now it is a %s with value" + " %s." % (name, type(parameter), str(parameter))) + return int(parameter) class UniformFloatHyperparameter(FloatHyperparameter): def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float], default: Union[int, float, None] = None, q: Union[int, float, None] = None, log: bool = False) -> None: + + super(UniformFloatHyperparameter, self).__init__(name, default) self.lower = float(lower) self.upper = float(upper) self.q = float(q) if q is not None else None @@ -333,9 +215,7 @@ def __init__(self, name: str, lower: Union[int, float], upper: Union[int, float] "hyperparameter %s is forbidden." % (self.lower, name)) - # super(UniformFloatHyperparameter, self). \ - # __init__(name, self.check_default(default)) - self.default = check_default_uniformfloat(default, self.upper, self.lower, self.log) + self.default = self.check_default(default) if self.log: if self.q is not None: @@ -378,8 +258,26 @@ def __eq__(self, other: Any) -> bool: else: return False - def is_legal(self, value: Union[int, float]): - return is_legal_uniformfloat(value, lower=self.lower, upper=self.upper) + def is_legal(self, value: Union[float]) -> bool: + if not (isinstance(value, float) or isinstance(value, int)): + return False + elif (self.upper + 0.00001) >= value >= (self.lower - 0.0000000001): + return True + else: + return False + + def check_default(self, default: float) -> Union[int, float]: + if default is None: + if self.log: + default = np.exp((np.log(self.lower) + np.log(self.upper)) / 2.) + else: + default = (self.lower + self.upper) / 2. + default = np.round(float(default), 10) + + if self.is_legal(default): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) def to_integer(self) -> 'UniformIntegerHyperparameter': # TODO check if conversion makes sense at all (at least two integer values possible!) @@ -389,11 +287,9 @@ def to_integer(self) -> 'UniformIntegerHyperparameter': int(np.round(self.default)), int(self.q), self.log) - # todo: rs? prabably numpy.random.uniform def _sample(self, rs: np.random, size: Union[int, None] = None) -> float: return rs.uniform(size=size) - # todo : recheck def _transform(self, vector: np.ndarray) -> Union[np.ndarray, None]: if np.any(np.isnan(vector)): return None @@ -428,14 +324,13 @@ def get_neighbors(self, value: Any, rs: np.random, number: int = 4, transform: b class NormalFloatHyperparameter(FloatHyperparameter): def __init__(self, name: str, mu: Union[int, float], sigma: Union[int, float], default: Union[None, float] = None, q: Union[int, float, None] = None, log: bool = False) -> None: + super(NormalFloatHyperparameter, self).__init__(name, default) self.mu = float(mu) self.sigma = float(sigma) self.q = float(q) if q is not None else None self.log = bool(log) self.name = name - # super(NormalFloatHyperparameter, self). \ - # __init__(name, self.check_default(default)) - self.default = check_default_normalfloat(default, self.mu) + self.default = self.check_default(default) def __repr__(self) -> str: repr_str = io.StringIO() @@ -469,6 +364,15 @@ def to_uniform(self, z: int = 3) -> 'UniformFloatHyperparameter': np.round(self.default, 0)), q=self.q, log=self.log) + def check_default(self, default: Union[int, float]) -> Union[int, float]: + if default is None: + return self.mu + + elif self.is_legal(default): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) + def to_integer(self) -> 'NormalIntegerHyperparameter': if self.q is None: q_int = None @@ -478,11 +382,8 @@ def to_integer(self) -> 'NormalIntegerHyperparameter': default=int(np.round(self.default, 0)), q=q_int, log=self.log) - def is_legal(self, value: Union[float, int]) -> bool: - if isinstance(value, (float, int)): - return True - else: - return False + def is_legal(self, value: Union[float]) -> bool: + return isinstance(value, float) or isinstance(value, int) def _sample(self, rs: np.random, size: Union[None, int] = None) -> np.ndarray: mu = self.mu @@ -516,11 +417,12 @@ def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: class UniformIntegerHyperparameter(IntegerHyperparameter): def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] = None, q: Union[int, None] = None, log: bool = False) -> None: - self.lower = check_int(lower, "lower") - self.upper = check_int(upper, "upper") + super(UniformIntegerHyperparameter, self).__init__(name, default) + self.lower = self.check_int(lower, "lower") + self.upper = self.check_int(upper, "upper") self.name = name if default is not None: - default = check_int(default, name) + default = self.check_int(default, name) if q is not None: if q < 1: @@ -529,7 +431,7 @@ def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] name) self.q = None else: - self.q = check_int(q, "q") + self.q = self.check_int(q, "q") else: self.q = None self.log = bool(log) @@ -543,9 +445,7 @@ def __init__(self, name: str, lower: int, upper: int, default: Union[int, None] "hyperparameter %s is forbidden." % (self.lower, name)) - # super(UniformIntegerHyperparameter, self). \ - # __init__(name, self.check_default(default)) - self.default = check_default_uniforminteger(default, upper, lower, log) + self.default = self.check_default(default) self.ufhp = UniformFloatHyperparameter(self.name, self.lower - 0.49999, @@ -565,7 +465,6 @@ def __repr__(self) -> str: repr_str.seek(0) return repr_str.getvalue() - # todo: recheck def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: value = self.ufhp._sample(rs, size=size) # Map all floats which belong to the same integer value to the same @@ -591,8 +490,25 @@ def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: return self.ufhp._inverse_transform(vector) def is_legal(self, value: int) -> bool: - return is_legal_uniforminteger(value, self.upper, self.lower) + if not (isinstance(value, (int, np.int, np.int32, np.int64))): + return False + elif self.upper >= value >= (self.lower - 0.0000000001): + return True + else: + return False + def check_default(self, default: Union[int, float]) -> int: + if default is None: + if self.log: + default = np.exp((np.log(self.lower) + np.log(self.upper)) / 2.) + else: + default = (self.lower + self.upper) / 2. + default = int(np.round(default, 0)) + + if self.is_legal(default): + return default + else: + raise ValueError("Illegal default value %s" % str(default)) def has_neighbors(self) -> bool: if self.log: @@ -635,11 +551,13 @@ def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4 class NormalIntegerHyperparameter(IntegerHyperparameter): def __init__(self, name: str, mu: int, sigma: Union[int, float], default: Union[int, None] = None, q: Union[None, int] = None, log: bool = False) -> None: + super(NormalIntegerHyperparameter, self).__init__(name, default) self.mu = mu self.sigma = sigma self.name = name + if default is not None: - default = check_int(default, self.name) + default = self.check_int(default, self.name) if q is not None: if q < 1: @@ -648,14 +566,12 @@ def __init__(self, name: str, mu: int, sigma: Union[int, float], name) self.q = None else: - self.q = check_int(q, "q") + self.q = self.check_int(q, "q") else: self.q = None self.log = bool(log) - # super(NormalIntegerHyperparameter, self). \ - # __init__(name, self.check_default(default)) - self.default = check_default_normalinteger(default, self.mu) + self.default = self.check_default(default) self.nfhp = NormalFloatHyperparameter(self.name, self.mu, @@ -689,7 +605,7 @@ def __eq__(self, other: Any) -> bool: return False # todo check if conversion should be done in initiation call or inside class itsel - def to_uniform(self, z: int = 3) -> 'UniformIntegerHyperparameter': + def to_uniform(self, z: np.round = 3) -> 'UniformIntegerHyperparameter': return UniformIntegerHyperparameter(self.name, int(self.mu - (z * self.sigma)), int(self.mu + (z * self.sigma)), @@ -697,10 +613,16 @@ def to_uniform(self, z: int = 3) -> 'UniformIntegerHyperparameter': q=self.q, log=self.log) def is_legal(self, value: int) -> bool: - if isinstance(value, int): - return True + return isinstance(value, (int, np.int, np.int32, np.int64)) + + def check_default(self, default: int) -> int: + if default is None: + return self.mu + + elif self.is_legal(default): + return default else: - return False + raise ValueError("Illegal default value %s" % str(default)) def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: value = self.nfhp._sample(rs, size=size) @@ -726,7 +648,6 @@ def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: def has_neighbors(self) -> bool: return True - # todo : find whay doesnt this function return anything def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> \ List[Union[np.ndarray, float, int]]: neighbors = [] # type: List[Union[np.ndarray, float, int]] @@ -790,7 +711,6 @@ def check_default(self, default: Union[None, str, float, int]) -> Union[str, flo def _sample(self, rs: np.random, size: int = None) -> Union[int, np.ndarray]: return rs.randint(0, self._num_choices, size=size) - # todo recheck def _transform(self, vector: np.ndarray) -> Union[None, str, int, float]: if not np.isfinite(vector): return None @@ -801,7 +721,6 @@ def _transform(self, vector: np.ndarray) -> Union[None, str, int, float]: 'hyperparameter %s with an integer, but provided ' 'the following float: %f' % (self, vector)) - # todo recheck def _inverse_transform(self, vector: Union[None, str, float, int]) -> Union[int, float]: if vector is None: return np.NaN @@ -861,7 +780,6 @@ def __init__(self, name: str, sequence: List[Union[float, int, str]], self.sequence = sequence self._num_elements = len(sequence) self.default = self.check_default(default) - # todo recheck type of sequence self.value_dict = OrderedDict() # type: OrderedDict[Union[int, float, str], int] counter = 1 for element in self.sequence: @@ -903,7 +821,6 @@ def check_default(self, default: Union[int, float, str, None]) -> Union[int, flo else: raise ValueError("Illegal default value %s" % str(default)) - # todo recheck return type...is it list or normal def _transform(self, vector: np.ndarray) -> Union[None, int, str, float]: if vector != vector: return None @@ -932,7 +849,6 @@ def get_order(self, value: Union[None, int, str, float]) -> int: """ return self.value_dict[value] - # todo : recheck def get_value(self, idx: int) -> Union[int, str, float]: """ returns the sequence value of a given order/position @@ -972,7 +888,6 @@ def get_num_neighbors(self, value: Union[int, float, str]) -> int: else: return 2 - # todo: recehck...added rs as param otherrwise mismatch with baseclass signature def get_neighbors(self, value: Union[int, str, float], rs: None, number: int = 2, transform: bool = False) \ -> List[Union[str, float, int]]: """ From 18f5620524f7d8b130b7184560ccbb677b2b5ef1 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Mon, 30 Jan 2017 12:16:04 +0100 Subject: [PATCH 21/22] put is_legal and check_defalut functions to specific classes --- ConfigSpace/hyperparameters.py | 2 +- test/test_hyperparameters.py | 26 +++++++++----------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 223da1ec..774ee2e0 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -157,7 +157,7 @@ def __init__(self, name: str, default: Any) -> None: def has_neighbors(self) -> bool: return True - def get_num_neighbors(self, value: None) -> np.inf: + def get_num_neighbors(self, value= None) -> np.inf: return np.inf diff --git a/test/test_hyperparameters.py b/test/test_hyperparameters.py index a7491cd0..defa1071 100644 --- a/test/test_hyperparameters.py +++ b/test/test_hyperparameters.py @@ -35,9 +35,7 @@ from ConfigSpace.hyperparameters import Constant, \ UniformFloatHyperparameter, NormalFloatHyperparameter, \ UniformIntegerHyperparameter, NormalIntegerHyperparameter, \ - CategoricalHyperparameter, OrdinalHyperparameter, \ - check_default_normalinteger, check_default_uniforminteger, check_default_normalfloat, check_default_uniformfloat, \ - check_int, is_legal_normalfloat, is_legal_uniformfloat, is_legal_normalinteger, is_legal_uniforminteger + CategoricalHyperparameter, OrdinalHyperparameter class TestHyperparameters(unittest.TestCase): @@ -131,18 +129,12 @@ def test_uniformfloat_is_legal(self): upper = 10 f1 = UniformFloatHyperparameter("param", lower, upper, q=0.1, log=True) - # self.assertTrue(f1.is_legal(3.0)) - # self.assertTrue(f1.is_legal(3)) - # self.assertFalse(f1.is_legal(-0.1)) - # self.assertFalse(f1.is_legal(10.1)) - # self.assertFalse(f1.is_legal("AAA")) - # self.assertFalse(f1.is_legal(dict())) - self.assertTrue(is_legal_uniformfloat(3.0, upper, lower)) - self.assertTrue(is_legal_uniformfloat(3, upper, lower)) - self.assertFalse(is_legal_uniformfloat(-0.1, upper, lower)) - self.assertFalse(is_legal_uniformfloat(10.1, upper, lower)) - self.assertFalse(is_legal_uniformfloat("AAA", upper, lower)) - self.assertFalse(is_legal_uniformfloat(dict(), upper, lower)) + self.assertTrue(f1.is_legal(3.0)) + self.assertTrue(f1.is_legal(3)) + self.assertFalse(f1.is_legal(-0.1)) + self.assertFalse(f1.is_legal(10.1)) + self.assertFalse(f1.is_legal("AAA")) + self.assertFalse(f1.is_legal(dict())) def test_uniformfloat_illegal_bounds(self): self.assertRaisesRegexp(ValueError, @@ -571,12 +563,12 @@ def test_log_space_conversion(self): lower, upper = 1e-5, 1e5 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper.upper, hyper.lower)) + self.assertTrue(hyper.is_legal(hyper._transform(1.))) lower, upper = 1e-10, 1e10 hyper = UniformFloatHyperparameter('test', lower=lower, upper=upper, log=True) - self.assertTrue(is_legal_uniformfloat(hyper._transform(1.), hyper.upper, hyper.lower)) + self.assertTrue(hyper.is_legal(hyper._transform(1.))) def test_ordinal_is_legal(self): f1 = OrdinalHyperparameter("temp", From d06086c0fdcb35b1ad8eaa4052b06a69ef95dc99 Mon Sep 17 00:00:00 2001 From: smohsinali Date: Tue, 31 Jan 2017 01:50:35 +0100 Subject: [PATCH 22/22] rs.random to rs.random.randomstate --- ConfigSpace/hyperparameters.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ConfigSpace/hyperparameters.py b/ConfigSpace/hyperparameters.py index 774ee2e0..75ab831f 100644 --- a/ConfigSpace/hyperparameters.py +++ b/ConfigSpace/hyperparameters.py @@ -407,7 +407,7 @@ def _inverse_transform(self, vector: Union[None, np.ndarray]) -> Union[float, np vector = np.log(vector) return vector - def get_neighbors(self, value: float, rs: np.random, number: int = 4, transform: bool = False) -> List[float]: + def get_neighbors(self, value: float, rs: np.random.RandomState, number: int = 4, transform: bool = False) -> List[float]: neighbors = [] for i in range(number): neighbors.append(rs.normal(value, self.sigma)) @@ -465,7 +465,7 @@ def __repr__(self) -> str: repr_str.seek(0) return repr_str.getvalue() - def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: + def _sample(self, rs: np.random.RandomState, size: Union[int, None] = None) -> np.ndarray: value = self.ufhp._sample(rs, size=size) # Map all floats which belong to the same integer value to the same # float value by first transforming it to an integer and then @@ -524,7 +524,7 @@ def has_neighbors(self) -> bool: else: return False - def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> List[ + def get_neighbors(self, value: Union[int, float], rs: np.random.RandomState, number: int = 4, transform: bool = False) -> List[ int]: neighbors = [] # type: List[int] while len(neighbors) < number: @@ -605,10 +605,10 @@ def __eq__(self, other: Any) -> bool: return False # todo check if conversion should be done in initiation call or inside class itsel - def to_uniform(self, z: np.round = 3) -> 'UniformIntegerHyperparameter': + def to_uniform(self, z: int = 3) -> 'UniformIntegerHyperparameter': return UniformIntegerHyperparameter(self.name, - int(self.mu - (z * self.sigma)), - int(self.mu + (z * self.sigma)), + np.round(int(self.mu - (z * self.sigma))), + np.round(int(self.mu + (z * self.sigma))), default=self.default, q=self.q, log=self.log) @@ -624,7 +624,7 @@ def check_default(self, default: int) -> int: else: raise ValueError("Illegal default value %s" % str(default)) - def _sample(self, rs: np.random, size: Union[int, None] = None) -> np.ndarray: + def _sample(self, rs: np.random.RandomState, size: Union[int, None] = None) -> np.ndarray: value = self.nfhp._sample(rs, size=size) # Map all floats which belong to the same integer value to the same # float value by first transforming it to an integer and then @@ -648,7 +648,7 @@ def _inverse_transform(self, vector: np.ndarray) -> np.ndarray: def has_neighbors(self) -> bool: return True - def get_neighbors(self, value: Union[int, float], rs: np.random, number: int = 4, transform: bool = False) -> \ + def get_neighbors(self, value: Union[int, float], rs: np.random.RandomState, number: int = 4, transform: bool = False) -> \ List[Union[np.ndarray, float, int]]: neighbors = [] # type: List[Union[np.ndarray, float, int]] while len(neighbors) < number: @@ -708,7 +708,7 @@ def check_default(self, default: Union[None, str, float, int]) -> Union[str, flo else: raise ValueError("Illegal default value %s" % str(default)) - def _sample(self, rs: np.random, size: int = None) -> Union[int, np.ndarray]: + def _sample(self, rs: np.random.RandomState, size: int = None) -> Union[int, np.ndarray]: return rs.randint(0, self._num_choices, size=size) def _transform(self, vector: np.ndarray) -> Union[None, str, int, float]: @@ -732,7 +732,7 @@ def has_neighbors(self) -> bool: def get_num_neighbors(self, value=None) -> int: return len(self.choices) - 1 - def get_neighbors(self, value: int, rs: np.random, number: Union[int, float] = np.inf, transform: bool = False) -> \ + def get_neighbors(self, value: int, rs: np.random.RandomState, number: Union[int, float] = np.inf, transform: bool = False) -> \ List[Union[float, int, str]]: neighbors = [] # type: List[Union[float, int, str]] if number < len(self.choices): @@ -866,7 +866,7 @@ def check_order(self, val1: Union[int, str, float], val2: Union[int, str, float] else: return False - def _sample(self, rs: np.random, size: Union[int, None] = None) -> int: + def _sample(self, rs: np.random.RandomState, size: Union[int, None] = None) -> int: """ returns a random sample from our sequence as order/position index """