def test_process_selectors_numpy_array_invalid_fields(field, np_params_converter): with pytest.raises(InvalidConstraintError): process_selectors( constraints=[{"type": "equality", field: None}], params=np.arange(6), tree_converter=np_params_converter, param_names=list("abcdefg"), )
def test_process_selectors_tree_invalid_fields( field, tree_params, tree_params_converter ): with pytest.raises(InvalidConstraintError): process_selectors( constraints=[{"type": "equality", field: None}], params=tree_params, tree_converter=tree_params_converter, param_names=list("abcdefg"), )
def test_process_selectors_differen_length_in_multiple_selectors(np_params_converter): constraints = [ { "type": "pairwise_equality", "locs": [[1, 4], [0, 3, 5]], } ] with pytest.raises(InvalidConstraintError): process_selectors( constraints=constraints, params=np.arange(6) + 10, tree_converter=np_params_converter, param_names=list("abcdefg"), )
def test_process_selectors_tree_selector(tree_params, tree_params_converter): calculated = process_selectors( constraints=[{"type": "equality", "selector": lambda x: x[1]}], params=tree_params, tree_converter=tree_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["index"], np.array([6]))
def test_process_selectors_no_constraint(constraints): calculated = process_selectors( constraints=constraints, params=np.arange(5), tree_converter=None, param_names=list("abcde"), ) assert calculated == []
def test_process_selectors_numpy_array_loc(np_params_converter): calculated = process_selectors( constraints=[{"type": "equality", "loc": [1, 4]}], params=np.arange(6) + 10, tree_converter=np_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["index"], np.array([1, 4]))
def test_process_selectors_dataframe_loc(df_params, df_params_converter): constraints = [{"type": "equality", "loc": ["b", "e"]}] calculated = process_selectors( constraints=constraints, params=df_params, tree_converter=df_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["index"], np.array([1, 4]))
def test_process_selectors_dataframe_locs(df_params, df_params_converter): constraints = [{"type": "pairwise_equality", "locs": [["b", "e"], ["a", "d"]]}] calculated = process_selectors( constraints=constraints, params=df_params, tree_converter=df_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["indices"][0], np.array([1, 4])) aae(calculated[0]["indices"][1], np.array([0, 3]))
def test_process_selectors_dataframe_query(df_params, df_params_converter): q = "name == 'b' | name == 'e'" constraints = [{"type": "equality", "query": q}] calculated = process_selectors( constraints=constraints, params=df_params, tree_converter=df_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["index"], np.array([1, 4]))
def test_process_selectors_dataframe_queries(df_params, df_params_converter): queries = ["name == 'b' | name == 'e'", "name == 'a' | name == 'd'"] constraints = [{"type": "pairwise_equality", "queries": queries}] calculated = process_selectors( constraints=constraints, params=df_params, tree_converter=df_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["indices"][0], np.array([1, 4])) aae(calculated[0]["indices"][1], np.array([0, 3]))
def test_process_selectors_tree_selectors(tree_params, tree_params_converter): constraints = [ { "type": "pairwise_equality", "selectors": [lambda x: x[1], lambda x: x[0][1][0]], } ] calculated = process_selectors( constraints=constraints, params=tree_params, tree_converter=tree_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["indices"][0], np.array([6])) aae(calculated[0]["indices"][1], np.array([1]))
def test_process_selectors_numpy_array_locs(np_params_converter): constraints = [ { "type": "pairwise_equality", "locs": [[1, 4], [0, 3]], } ] calculated = process_selectors( constraints=constraints, params=np.arange(6) + 10, tree_converter=np_params_converter, param_names=list("abcdefg"), ) aae(calculated[0]["indices"][0], np.array([1, 4])) aae(calculated[0]["indices"][1], np.array([0, 3]))
def get_converter( params, constraints, lower_bounds, upper_bounds, func_eval, primary_key, scaling, scaling_options, derivative_eval=None, soft_lower_bounds=None, soft_upper_bounds=None, add_soft_bounds=False, ): """Get a converter between external and internal params and internal params. This combines the following conversions: - Flattening parameters provided as pytrees (tree_conversion) - Enforcing constraints via reparametrizations (space_conversion) - Scaling of the parameter space (scale_conversion) The resulting converter can transform parameters, function outputs and derivatives. If possible, fast paths for some or all transformations are chosen. Args: params (pytree): The user provided parameters. constraints (list): The user provided constraints. lower_bounds (pytree): The user provided lower_bounds upper_bounds (pytree): The user provided upper bounds func_eval (float, dict or pytree): An evaluation of ``func`` at ``params``. Used to deterimine how the function output has to be transformed for the optimizer. primary_key (str): One of "value", "contributions" and "root_contributions". Used to determine how the function and derivative output has to be transformed for the optimzer. scaling (bool): Whether scaling should be performed. scaling_options (dict): User provided scaling options. derivative_eval (dict, pytree or None): Evaluation of the derivative of func at params. Used for consistency checks. soft_lower_bounds (pytree): As lower_bounds soft_upper_bounds (pytree): As upper_bounds add_soft_bounds (bool): Whether soft bounds should be added to the internal_params Returns: Converter: NamedTuple with methods to convert between internal and external parameters, derivatives and function outputs. InternalParams: NamedTuple with internal parameter values, lower_bounds and upper_bounds. """ fast_path = _is_fast_path( params=params, constraints=constraints, func_eval=func_eval, primary_key=primary_key, scaling=scaling, derivative_eval=derivative_eval, add_soft_bounds=add_soft_bounds, ) if fast_path: return _get_fast_path_converter( params=params, lower_bounds=lower_bounds, upper_bounds=upper_bounds, primary_key=primary_key, ) tree_converter, internal_params = get_tree_converter( params=params, lower_bounds=lower_bounds, upper_bounds=upper_bounds, func_eval=func_eval, derivative_eval=derivative_eval, primary_key=primary_key, soft_lower_bounds=soft_lower_bounds, soft_upper_bounds=soft_upper_bounds, add_soft_bounds=add_soft_bounds, ) flat_constraints = process_selectors( constraints=constraints, params=params, tree_converter=tree_converter, param_names=internal_params.names, ) space_converter, internal_params = get_space_converter( internal_params=internal_params, internal_constraints=flat_constraints) scale_converter, scaled_params = get_scale_converter( internal_params=internal_params, scaling=scaling, scaling_options=scaling_options, ) def _params_to_internal(params): x_flat = tree_converter.params_flatten(params) x_internal = space_converter.params_to_internal(x_flat) x_scaled = scale_converter.params_to_internal(x_internal) return x_scaled def _params_from_internal(x, return_type="tree"): x_unscaled = scale_converter.params_from_internal(x) x_external = space_converter.params_from_internal(x_unscaled) x_tree = tree_converter.params_unflatten(x_external) if return_type == "tree": out = x_tree elif return_type == "tree_and_flat": out = x_tree, x_external elif return_type == "flat": out = x_external else: msg = ( "Invalid return type: {return_type}. Must be one of 'tree', 'flat', " "'tree_and_flat'") raise ValueError(msg) return out def _derivative_to_internal(derivative_eval, x, jac_is_flat=False): if jac_is_flat: jacobian = derivative_eval else: jacobian = tree_converter.derivative_flatten(derivative_eval) x_unscaled = scale_converter.params_from_internal(x) jac_with_space_conversion = space_converter.derivative_to_internal( jacobian, x_unscaled) jac_with_unscaling = scale_converter.derivative_to_internal( jac_with_space_conversion) return jac_with_unscaling def _func_to_internal(func_eval): return tree_converter.func_flatten(func_eval) internal_params = scaled_params._replace( free_mask=internal_params.free_mask) converter = Converter( params_to_internal=_params_to_internal, params_from_internal=_params_from_internal, derivative_to_internal=_derivative_to_internal, func_to_internal=_func_to_internal, has_transforming_constraints=space_converter. has_transforming_constraints, ) return converter, internal_params