def check_membership( self, parameterization: TParameterization, raise_error: bool = False ) -> bool: """Whether the given parameterization belongs in the search space. Checks that the given parameter values have the same name/type as search space parameters, are contained in the search space domain, and satisfy the parameter constraints. Args: parameterization: Dict from parameter name to value to validate. raise_error: If true parameterization does not belong, raises an error with detailed explanation of why. Returns: Whether the parameterization is contained in the search space. """ if len(parameterization) != len(self._parameters): if raise_error: raise ValueError( f"Parameterization has {len(parameterization)} parameters " f"but search space has {len(self._parameters)}." ) return False for name, value in parameterization.items(): if name not in self._parameters: if raise_error: raise ValueError( f"Parameter {name} not defined in search space" f"with parameters {self._parameters}" ) return False if not self._parameters[name].validate(value): if raise_error: raise ValueError( f"{value} is not a valid value for " f"parameter {self._parameters[name]}" ) return False # parameter constraints only accept numeric parameters numerical_param_dict = { # pyre-fixme[6]: Expected `typing.Union[...oat]` but got `unknown`. name: float(value) for name, value in parameterization.items() if self._parameters[name].is_numeric } for constraint in self._parameter_constraints: if not constraint.check(numerical_param_dict): if raise_error: raise ValueError(f"Parameter constraint {constraint} is violated.") return False return True
def check_types( self, parameterization: TParameterization, allow_none: bool = True, raise_error: bool = False, ) -> bool: """Checks that the given parameterization's types match the search space. Args: parameterization: Dict from parameter name to value to validate. allow_none: Whether None is a valid parameter value. raise_error: If true and parameterization does not belong, raises an error with detailed explanation of why. Returns: Whether the parameterization has valid types. """ for name, value in parameterization.items(): if name not in self._parameters: if raise_error: raise ValueError( f"Parameter {name} not defined in search space") return False if value is None and allow_none: continue if not self._parameters[name].is_valid_type(value): if raise_error: raise ValueError(f"{value} is not a valid value for " f"parameter {self._parameters[name]}") return False return True
def check_all_parameters_present( self, parameterization: TParameterization, raise_error: bool = False, ) -> bool: """Whether a given parameterization contains all the parameters in the search space. Args: parameterization: Dict from parameter name to value to validate. raise_error: If true parameterization does not belong, raises an error with detailed explanation of why. Returns: Whether the parameterization is contained in the search space. """ parameterization_params = set(parameterization.keys()) ss_params = set(self._parameters.keys()) if parameterization_params != ss_params: if raise_error: raise ValueError( f"Parameterization has parameters: {parameterization_params}, " f"but search space has parameters: {ss_params}.") return False return True
def _numpy_types_to_python_types( parameterization: TParameterization, ) -> TParameterization: """If applicable, coerce values of the parameterization from Numpy int/float to Python int/float. """ return { name: numpy_type_to_python_type(value) for name, value in parameterization.items() }
def randomized_round_parameters( parameters: TParameterization, transform_parameters: Set[str]) -> TParameterization: rounded_parameters = copy(parameters) for p_name in transform_parameters: # pyre: param is declared to have type `float` but is used as # pyre-fixme[9]: type `Optional[typing.Union[bool, float, str]]`. param: float = parameters.get(p_name) rounded_parameters[p_name] = randomized_round(param) return rounded_parameters
def check_membership( self, parameterization: TParameterization, raise_error: bool = False, check_all_parameters_present: bool = True, ) -> bool: """Whether the given parameterization belongs in the search space. Checks that the given parameter values have the same name/type as search space parameters, are contained in the search space domain, and satisfy the parameter constraints. Args: parameterization: Dict from parameter name to value to validate. raise_error: If true parameterization does not belong, raises an error with detailed explanation of why. check_all_parameters_present: Ensure that parameterization specifies values for all parameters as expected by the search space and its hierarchical structure. Returns: Whether the parameterization is contained in the search space. """ super().check_membership( parameterization=parameterization, raise_error=raise_error, check_all_parameters_present=False, ) # Check that each arm "belongs" in the hierarchical # search space; ensure that it only has the parameters that make sense # with each other (and does not contain dependent parameters if the # parameter they depend on does not have the correct value). cast_to_hss_params = set( self._cast_parameterization( parameters=parameterization, check_all_parameters_present=check_all_parameters_present, ).keys() ) parameterization_params = set(parameterization.keys()) if cast_to_hss_params != parameterization_params: if raise_error: raise ValueError( "Parameterization violates the hierarchical structure of the search" f"space; cast version would have parameters: {cast_to_hss_params}," f" but full version contains parameters: {parameterization_params}." ) return False return True
def _parameterization_probability( parameterization: TParameterization, coefficients: Dict[str, Dict[TParamValue, float]], noise_var: float = 0.0, ) -> float: z = 0.0 for factor, level in parameterization.items(): if factor not in coefficients.keys(): raise ValueError("{} not in supplied coefficients".format(factor)) if level not in coefficients[factor].keys(): raise ValueError("{} not a valid level of {}".format(level, factor)) z += coefficients[factor][level] z += np.sqrt(noise_var) * np.random.randn() return np.exp(z) / (1 + np.exp(z))
def md5hash(parameters: TParameterization) -> str: """Return unique identifier for arm's parameters. Args: parameters: Parameterization; mapping of param name to value. Returns: Hash of arm's parameters. """ for k, v in parameters.items(): parameters[k] = numpy_type_to_python_type(v) parameters_str = json.dumps(parameters, sort_keys=True) return hashlib.md5(parameters_str.encode("utf-8")).hexdigest()
def check_types( self, parameterization: TParameterization, allow_none: bool = True, raise_error: bool = False, ) -> bool: """Checks that the given parameterization's types match the search space. Checks that the names of the parameterization match those specified in the search space, and the given values are of the correct type. Args: parameterization: Dict from parameter name to value to validate. allow_none: Whether None is a valid parameter value. raise_error: If true and parameterization does not belong, raises an error with detailed explanation of why. Returns: Whether the parameterization has valid types. """ if len(parameterization) != len(self._parameters): if raise_error: raise ValueError( f"Parameterization has {len(parameterization)} parameters " f"but search space has {len(self._parameters)}.\n" f"Parameterization: {parameterization}.\n" f"Search Space: {self._parameters}." ) return False for name, value in parameterization.items(): if name not in self._parameters: if raise_error: raise ValueError(f"Parameter {name} not defined in search space") return False if value is None and allow_none: continue if not self._parameters[name].is_valid_type(value): if raise_error: raise ValueError( f"{value} is not a valid value for " f"parameter {self._parameters[name]}" ) return False return True
def _format_dict(param_dict: TParameterization, name: str = "Parameterization") -> str: """Format a dictionary for labels. Args: param_dict: Dictionary to be formatted name: String name of the thing being formatted. Returns: stringified blob. """ if len(param_dict) >= 10: blob = "{} has too many items to render on hover ({}).".format( name, len(param_dict)) else: blob = "<br><em>{}:</em><br>{}".format( name, "<br>".join("{}: {}".format(n, v) for n, v in param_dict.items())) return blob
def _cast_parameterization( self, parameters: TParameterization, check_all_parameters_present: bool = True, ) -> TParameterization: """Cast parameterization (of an arm, observation features, etc.) to the hierarchical structure of this search space. Args: parameters: Parameterization to cast to hierarchical structure. check_all_parameters_present: Whether to raise an error if a paramete that is expected to be present (according to values of other parameters and the hierarchical structure of the search space) is not specified. """ def _find_applicable_parameters(root: Parameter) -> Set[str]: applicable = {root.name} if check_all_parameters_present and root.name not in parameters: raise RuntimeError( f"Parameter '{root.name}' not in parameterization to cast." ) if not root.is_hierarchical: return applicable for val, deps in root.dependents.items(): if parameters[root.name] == val: for dep in deps: applicable.update( _find_applicable_parameters(root=self[dep])) return applicable applicable_paramers = _find_applicable_parameters(root=self.root) if not all(k in parameters for k in applicable_paramers): raise RuntimeError( f"Parameters {applicable_paramers- set(parameters.keys())} " "missing from the arm.") return { k: v for k, v in parameters.items() if k in applicable_paramers }
def _filter_dict(param_dict: TParameterization, subset_keys: List[str]) -> TParameterization: """Filter a dictionary to keys present in a given list.""" return {k: v for k, v in param_dict.items() if k in subset_keys}
def _get_sum(parameterization: TParameterization) -> float: param_names = list(parameterization.keys()) if any(param_name not in param_names for param_name in ["x1", "x2"]): raise ValueError("Parametrization does not contain x1 or x2") x1, x2 = parameterization["x1"], parameterization["x2"] return x1 + x2