def test_shape(small_psp, basic_psp, adv_psp, seq_psp): """Asserts that the returned shape is correct""" assert small_psp.shape == (2, 3, 5) assert basic_psp.shape == (3, 3, 3, 3, 3, 3) assert adv_psp.shape == (3, 3, 3, 3, 3, 3) assert seq_psp.shape == (3, 3, 3, 3, 3, 3, 3) p = ParamSpace( dict( a=ParamDim(default=0, values=[1]), # 1 b=ParamDim(default=0, range=[0, 10, 2]), # 5 c=ParamDim(default=0, linspace=[1, 2, 20]), # 20 d=ParamDim(default=0, logspace=[1, 2, 12, 1]), # 12 ) ) assert p.shape == (1, 5, 20, 12) # Also test the number of dimensions assert basic_psp.num_dims == 6 assert adv_psp.num_dims == 6 assert p.num_dims == 4 # And the state shape, which is +1 larger in each entry assert small_psp.states_shape == (3, 4, 6) assert basic_psp.states_shape == (4, 4, 4, 4, 4, 4) assert adv_psp.states_shape == (4, 4, 4, 4, 4, 4) # And that the maximum state number is correct assert small_psp.max_state_no == 71 assert basic_psp.max_state_no == 4095 assert adv_psp.max_state_no == 4095
def test_iteration(various_pdims): """Tests whether the iteration over the span's state works.""" pd = ParamDim(default=0, values=[1, 2, 3]) # Should start in default state assert pd.state == 0 # First iteration assert pd.__next__() == 1 assert pd.__next__() == 2 assert pd.__next__() == 3 with pytest.raises(StopIteration): pd.__next__() # Should be able to iterate again assert pd.__next__() == 1 assert pd.__next__() == 2 assert pd.__next__() == 3 with pytest.raises(StopIteration): pd.__next__() # State should be reset to 0 now assert pd.state == 0 # And as a loop for _ in pd: continue
def test_subspace_mixed_values(): """Test that parameter spaces with parameter dimensions that have mixed values raise the expected error message. """ psp = ParamSpace( dict( all_str=ParamDim(default="foo", values=["foo", "bar", "baz"]), mixed=ParamDim(default="0", values=[0.0, "1", "2", 3, "4"]), ) ) assert psp.volume == 3 * 5 # These should fail due to a mixed-type dimension is tried to be masked with pytest.raises(TypeError, match="Could not ascertain whether"): psp.activate_subspace(mixed=0.0) with pytest.raises(TypeError, match="Could not ascertain whether"): psp.activate_subspace(all_str="foo", mixed=[0.0, "1"]) with pytest.raises(TypeError, match="Could not ascertain whether"): psp.activate_subspace(all_str="foo", mixed=["1", "2"]) # ... but this should work, as the mixed-type dimension is not touched psp.activate_subspace(all_str="foo") assert psp.volume == 1 * 5
def small_psp(): """Used to setup a small pspace object to be tested on.""" return ParamSpace( dict( p0=ParamDim(default=0, values=[1, 2]), p1=ParamDim(default=0, values=[1, 2, 3]), p2=ParamDim(default=0, values=[1, 2, 3, 4, 5]), ) )
def float_valued_psp(): """A small parameter space that has float values for parameter values""" return ParamSpace( dict( lin1=ParamDim(default=0.0, linspace=[-1.0, 1.0, 11]), lin2=ParamDim(default=0.0, linspace=[-2.0, 2.0, 11]), log1=ParamDim(default=0.0, logspace=[-10.0, 10.0, 6]), ) )
def str_valued_psp(): """A small parameter space that has string values for parameter values""" return ParamSpace( dict( p0=ParamDim(default="foo", values=["foo", "bar", "baz"]), p1=ParamDim(default="0", values=["0", "1", "2", "3", "4"]), lin=ParamDim(default=0.0, linspace=[0.0, 1.0, 6]), ) )
def check_names(*name_check_pdim): """Create the necessary input data for the create_names function, then perform it and assert equality to the expected values ... """ kv_pairs = [ (path, ParamDim(default=0, values=[1, 2], name=pd_name)) if pdim is not None else (path, ParamDim(default=0, values=[1, 2])) for path, _, pd_name in name_check_pdim ] expected_names = [name_out for _, name_out, _ in name_check_pdim] actual_names = [k for k, _ in create_names(kv_pairs)] assert expected_names == actual_names
def test_np_methods_return_floats(): """Assert that when using linspace or logspace, the values are floats and _not_ numpy scalar types. """ pds = [ ParamDim(default=0, linspace=[0, 10, 11]), ParamDim(default=0, logspace=[0, 10, 11]), ] for pd in pds: types = [type(v) for v in pd.values] print("Types: " + str(types)) assert all([t is float for t in types])
def psp_with_coupled(): """Used to setup a pspace object with coupled param dims""" d = dict( a=ParamDim(default=0, values=[1, 2, 3], order=0), c1=CoupledParamDim(target_name=("a",)), d=dict( aa=ParamDim(default=0, values=[1, 2, 3], order=-1), cc1=CoupledParamDim(target_name=("d", "aa")), cc2=CoupledParamDim(target_name=("a",)), cc3=CoupledParamDim(target_name="aa"), ), foo="bar", spam="eggs", mutable=[0, 0, 0], ) return ParamSpace(d)
def various_pdims(): """Used to setup various pspan objects to be tested on.""" pds = {} pds["one"] = ParamDim(default=0, values=[1, 2, 3]) pds["two"] = ParamDim(default=0, values=[1.0, 2, "three", np.inf]) pds["range"] = ParamDim(default=0, range=[1, 4, 1]) pds["linspace"] = ParamDim(default=0, linspace=[1, 3, 3, True]) pds["logspace"] = ParamDim(default=0, logspace=[-1, 1, 11]) pds["typed"] = ParamDim(default=0, range=[3], as_type="float") pds["a_list"] = ParamDim(default=0, values=[[1, 2], [3, 4]]) pds["as_tuple"] = ParamDim(default=0, values=[1, [2], [3, 4, [5]]], as_type="tuple") pds["named"] = ParamDim(default=0, values=[1, 2, 3], name="named_span") pds["with_order"] = ParamDim(default=0, values=[1, 2, 3], order=42) return pds
def test_values(various_pdims): """Assert the correct values are chosen""" vpd = various_pdims assert vpd["range"].values == tuple(range(1, 4, 1)) assert all(vpd["linspace"].values == np.linspace(1, 3, 3, True)) assert all(vpd["logspace"].values == np.logspace(-1, 1, 11)) # Test the as_type argument assert isinstance(vpd["a_list"].values[0], list) assert isinstance(vpd["typed"].values[0], float) assert vpd["as_tuple"].values == (1, (2, ), (3, 4, (5, ))) with pytest.raises(KeyError, match="some_type"): ParamDim(default=0, range=[10], as_type="some_type") # Assert that values are unique with pytest.raises(ValueError, match="need to be unique, but there were"): ParamDim(default=0, values=[1, 1, 2])
def test_volume(small_psp, basic_psp, adv_psp, seq_psp): """Asserts that the volume calculation is correct""" assert small_psp.volume == 2 * 3 * 5 assert basic_psp.volume == 3 ** 6 assert adv_psp.volume == 3 ** 6 assert seq_psp.volume == 3 ** 7 p = ParamSpace( dict( a=ParamDim(default=0, values=[1]), # 1 b=ParamDim(default=0, range=[0, 10, 2]), # 5 c=ParamDim(default=0, linspace=[1, 2, 20]), # 20 d=ParamDim(default=0, logspace=[1, 2, 12, 1]), # 12 ) ) assert p.volume == 1 * 5 * 20 * 12 # And of a paramspace without dimensions empty_psp = ParamSpace(dict(a=1)) assert empty_psp.volume == 0 == empty_psp.full_volume
def adv_psp(): """Used to setup a more elaborate pspace object to be tested on. Includes name clashes, manually set names, order, ...""" d = dict( a=1, b=2, foo="bar", spam="eggs", mutable=[0, 0, 0], p1=ParamDim(default=0, values=[1, 2, 3], order=0), p2=ParamDim(default=0, values=[1, 2, 3], order=1), d=dict( a=1, b=2, p1=ParamDim(default=0, values=[1, 2, 3], order=-1), p2=ParamDim(default=0, values=[1, 2, 3], order=0), d=dict( a=1, b=2, p1=ParamDim(default=0, values=[1, 2, 3], name="ppp1"), p2=ParamDim(default=0, values=[1, 2, 3], name="ppp2"), ), ), ) return ParamSpace(d)
def basic_psp(): """Used to setup a basic pspace object to be tested on.""" d = dict( a=1, b=2, foo="bar", spam="eggs", mutable=[0, 0, 0], p1=ParamDim(default=0, values=[1, 2, 3]), p2=ParamDim(default=0, values=[1, 2, 3]), d=dict( aa=1, bb=2, pp1=ParamDim(default=0, values=[1, 2, 3]), pp2=ParamDim(default=0, values=[1, 2, 3]), dd=dict( aaa=1, bbb=2, ppp1=ParamDim(default=0, values=[1, 2, 3]), ppp2=ParamDim(default=0, values=[1, 2, 3]), ), ), ) return ParamSpace(d)
def test_cpd_iteration(): """Tests iteration of CoupledParamDim""" # ParamDim to couple to for testing pd = ParamDim(default=0, values=[1, 2, 3]) # Simplest case: cpd follows pd for pval, cpval in zip(pd, CoupledParamDim(target_pdim=pd)): assert pval == cpval # With custom cpd values for pval, cpval in zip(pd, CoupledParamDim(target_pdim=pd, values=[2, 3, 4])): assert pval + 1 == cpval
def test_coupled_mask(): """Test the masking features' effects on CoupledParamDim""" pd = ParamDim(default=0, values=[1, 2, 3, 4], mask=(0, 1, 0, 1)) # It should not be possible to mask a CPD with pytest.raises(TypeError, match="Received invalid keyword-argument"): CoupledParamDim(target_pdim=pd, mask=True) # Test that coupled iteration is masked accordingly vals = [] for pval, cpval in zip(pd, CoupledParamDim(target_pdim=pd)): assert pval == cpval vals.append(pval) assert vals == [1, 3]
def test_coupled(psp_with_coupled): """Test parameter spaces with CoupledParamDims in them""" psp = psp_with_coupled print("ParamSpace with CoupledParamDim:\n", psp) def assert_coupling(src: tuple, target: tuple): """Asserts that the CoupledParamDim at keyseq src is coupled to the target ParamDim at keyseq target.""" assert ( psp.coupled_dims_by_loc[src].target_pdim == psp.dims_by_loc[target] ) # Assert correct coupling assert_coupling(("c1",), ("a",)) assert_coupling(("d", "cc1"), ("d", "aa")) assert_coupling(("d", "cc2"), ("a",)) assert_coupling(("d", "cc3"), ("d", "aa")) # Check default is correct default = psp.default assert default["c1"] == default["a"] assert default["d"]["cc1"] == default["d"]["aa"] assert default["d"]["cc2"] == default["a"] assert default["d"]["cc3"] == default["d"]["aa"] # Iterate over the paramspace and check correctness for pt in psp: print("Point: ", pt) assert pt["c1"] == pt["a"] assert pt["d"]["cc1"] == pt["d"]["aa"] assert pt["d"]["cc2"] == pt["a"] assert pt["d"]["cc3"] == pt["d"]["aa"] # Invalid coupling targets should raise an error with pytest.raises(ValueError, match="Could not resolve the coupling"): ParamSpace( dict( a=ParamDim(default=0, range=[10]), b=CoupledParamDim(target_name="foo"), ) )
def seq_psp(): """A parameter space with dimensions within sequences""" d = dict( a=1, b=2, foo="bar", spam="eggs", mutable=[0, 0, 0], s=[ ParamDim(default=0, values=[1, 2, 3]), [ParamDim(default=1, values=[1, 2, 3], order=1), 2, 3], ], d=dict( a=1, b=2, s=[ ParamDim(default=0, values=[1, 2, 3], order=-1), ParamDim(default=1, values=[1, 2, 3], order=0, name="ds1"), 2, 3, ], d=dict( a=1, b=2, p=ParamDim(default=0, values=[1, 2, 3]), s=[ 0, ParamDim(default=1, values=[1, 2, 3]), ParamDim(default=2, values=[1, 2, 3], name="dds2"), 3, ], ), ), ) return ParamSpace(d)
def test_mask(): """Test that masking works""" # Test initialization, property getter and setter, and type pd = ParamDim(default=0, values=[1, 2, 3, 4], mask=False) assert pd.mask is False # NOTE not trivial to test because the .mask getter _computes_ the value assert not any([isinstance(v, Masked) for v in pd.values]) pd.mask = True assert pd.mask is True assert all([isinstance(v, Masked) for v in pd.values]) # Check the string representation of masked values assert str(pd.values[0]).find("<1>") > -1 assert str(pd).find("Masked object, value:") > -1 # Now to a more complex mask pd.mask = (True, False, True, False) assert pd.mask == (True, False, True, False) # Test the properties that inform about the number of masked and unmasked # values assert len(pd) == 2 assert pd.num_masked == 2 # Setting one with a bad length should not work with pytest.raises(ValueError, match="container of same length as the"): pd.mask = (True, False) # Check that iteration starts at first unmasked state pd.enter_iteration() assert pd.state == 2 assert pd.current_value == 2 # Iterate one step, this should jump to index and value 3 assert pd.__next__() == 4 assert pd.state == 4 # Setting the state manually to something masked should not work with pytest.raises(MaskedValueError, match="Value at index 1 is masked"): pd.state = 1 # No further iteration should be possible for this one with pytest.raises(StopIteration): pd.iterate_state() assert pd.state == 0 # Check iteration again assert list(pd) == [2, 4] # For fully masked, the default value should be returned. Eff. length: 1 pd.mask = True assert len(pd) == 1 assert pd.num_masked == pd.num_states - 1 assert list(pd) == [0] # Try using a slice to set the mask pd.mask = slice(2) assert pd.mask == (True, True, False, False) pd.mask = slice(2, None) assert pd.mask == (False, False, True, True) pd.mask = slice(None, None, 2) assert pd.mask == (True, False, True, False)
def test_cpd_init(): """Test whether initialisation of CoupledParamDim works""" # These should work CoupledParamDim(target_name=("foo", )) CoupledParamDim(target_name=("foo", ), default=0) CoupledParamDim(target_name=("foo", ), values=[1, 2, 3]) CoupledParamDim(target_name=("foo", ), range=[3]) CoupledParamDim(target_name=("foo", ), linspace=[0, 1, 3]) CoupledParamDim(target_name=("foo", ), logspace=[0, 2, 3]) CoupledParamDim(target_name="foo") # These should fail due to wrong arguments given with pytest.raises(TypeError, match="Expected either argument"): # Neither target_pdim nor target_name given CoupledParamDim() with pytest.raises(ValueError, match="The coupling target has not been"): # Not coupled yet CoupledParamDim(target_name=("foo", )).default with pytest.raises(TypeError, match="Got both `target_pdim` and"): # Got both target_pdim and target_name CoupledParamDim( target_pdim=ParamDim(default=0, values=[1, 2, 3]), target_name=["foo", "bar"], ) with pytest.raises(TypeError, match="should be a tuple or list"): # Bad target_name type CoupledParamDim(target_name=dict(foo="bar")) # Set target pd = ParamDim(default=0, values=[1, 2, 3]) cpd = CoupledParamDim(target_pdim=pd) assert len(pd) == len(cpd) assert pd.values == cpd.values assert pd.default == cpd.default assert cpd.target_name is None # Test if the name behaviour is correct with pytest.raises(ValueError, match="name cannot be changed after"): cpd.target_name = ("bar", ) # Accessing coupling target without it having been set should raise errors cpd = CoupledParamDim(target_name=("foo", )) with pytest.raises(ValueError, match="name cannot be changed!"): cpd.target_name = ("foo", ) with pytest.raises(ValueError, match="The coupling target has not been"): cpd.target_pdim with pytest.raises(TypeError, match="Target of CoupledParamDim needs to"): cpd.target_pdim = "foo" cpd.target_pdim = pd # Setting it again also works cpd.target_pdim = pd # Test lengths are matching with pytest.raises(ValueError, match="The lengths of the value sequences"): cpd = CoupledParamDim(target_pdim=pd, values=[1, 2, 3, 4]) # Assure values cannot be changed cpd = CoupledParamDim(target_pdim=pd, values=[2, 3, 4]) with pytest.raises(AttributeError, match="Values already set; cannot be"): cpd._set_values([1, 2, 3], assert_unique=False) # Test it has no state set cpd = CoupledParamDim(target_pdim=pd, values=[2, 3, 4]) assert cpd.state == 0 assert cpd.current_value == 0 # that of the coupled ParamDim! # Check that it can access the target's mask assert cpd.mask is cpd.target_pdim.mask
def test_init(): """Test whether all initialisation methods work.""" # These should all work ParamDim(default=0, values=[1, 2, 3]) ParamDim(default=0, values=[1.0, 2, "three", np.inf]) ParamDim(default=0, range=[1, 4, 1]) ParamDim(default=0, linspace=[1, 3, 3, True]) ParamDim(default=0, logspace=[-1, 1, 11]) ParamDim(default=0, range=[3], as_type="float") ParamDim(default=0, values=[[1, 2], [3, 4]]) ParamDim(default=0, values=[1, [2], [3, 4, [5]]], as_type="tuple") ParamDim(default=0, values=[1, 2, 3], name="named_span") ParamDim(default=0, values=[1, 2, 3], order=42) # No default given with pytest.raises(TypeError): ParamDim() # No values given with pytest.raises(TypeError, match="Missing one of the following"): ParamDim(default=0) with pytest.raises(ValueError, match="need be a container of length >= 1"): ParamDim(default=0, values=[]) with pytest.raises(TypeError, match="Received invalid keyword argument"): ParamDim(default=0, foo="bar") # Multiple values or kwargs given with pytest.raises(TypeError, match="Received too many keyword arguments"): ParamDim(default=0, values=[1, 2, 3], linspace=[10, 20, 30]) with pytest.raises(TypeError, match="Received too many keyword arguments"): ParamDim(default=0, range=[1, 2], linspace=[10, 20, 30])