diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index ba8f0755585..a114b54821c 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -94,8 +94,8 @@ * **variable** - used as the input to its `function `. Specification of the **default_variable** argument in the constructor for a Component determines both its format (e.g., whether its value is numeric, its dimensionality and shape if it is an array, etc.) as well as its `default_value ` (the value - used when the Component is executed and no input is provided), and takes precedence over the specification of `size - `. + used when the Component is executed and no input is provided). + It may alternatively be specified by `size `. .. technical_note:: Internally, the attribute **variable** is not directly used as input to functions, to allow for parallelization. @@ -105,10 +105,17 @@ .. _Component_Size: -* **size** - the dimension of the `variable ` attribute. The **size** argument of the +* **size** - the numpy shape or iterable of shapes matching the + `variable ` attribute. The **size** argument of + the constructor for a Component can be used as a convenient method for specifying the `variable `, - attribute in which case it will be assigned as an array of zeros of the specified size. For example, - setting **size** = 3 is equivalent to setting **variable** = [0, 0, 0] and setting **size** = [4, 3] is equivalent + attribute in which case it will be assigned as an array of zeros of + the specified shape. When **size** is an iterable, each item in the + iterable is treated as a single shape, and the entire iterable is then + assigned as an array. When **size** is an integer, it is treated the + same as a one-item iterable containing that integer. For example, + setting **size** = 3 is equivalent to setting + **variable** = [[0, 0, 0]] and setting **size** = [4, 3] is equivalent to setting **variable** = [[0, 0, 0, 0], [0, 0, 0]]. .. note:: @@ -324,10 +331,9 @@ _instantiate_function method checks that the input of the Component's `function ` is compatible with its `variable `). - * `_handle_size ` converts the `variable ` and `size ` - arguments to the correct dimensions (for `Mechanism `, this is a 2D array and 1D - array, respectively). If **variable** is not passed as an argument, this method attempts to infer `variable - ` from the **size** argument, and vice versa if the **size** argument is missing. + * `_handle_size ` attempts to infer + `variable ` from the **size** argument if + **variable** is not passed as an argument. The _handle_size method then checks that the **size** and **variable** arguments are compatible. * `_instantiate_defaults ` first calls the validation methods, and then @@ -540,7 +546,7 @@ from psyneulink.core.globals.utilities import \ ContentAddressableList, convert_all_elements_to_np_array, convert_to_np_array, get_deepcopy_with_shared, \ is_instance_or_subclass, is_matrix, iscompatible, kwCompatibilityLength, \ - get_all_explicit_arguments, is_numeric, call_with_pruned_args, safe_equals, safe_len, parse_valid_identifier, try_extract_0d_array_item, contains_type + get_all_explicit_arguments, is_numeric, call_with_pruned_args, safe_equals, safe_len, parse_valid_identifier, try_extract_0d_array_item, contains_type, is_iterable from psyneulink.core.scheduling.condition import Never from psyneulink.core.scheduling.time import Time, TimeScale @@ -808,9 +814,10 @@ class Component(MDFSerializable, metaclass=ComponentsMeta): specifies template for the input to the Component's `function `, and the value used as the input to the Component if none is provided on execution (see `Component_Variable` for additional information). - size : int, list or np.ndarray of ints : default None + size : int, or Iterable of tuple or int : default None specifies default_variable as array(s) of zeros if **default_variable** is not passed as an argument; - if **default_variable** is specified, it takes precedence over the specification of **size** (see + if **default_variable** is specified, it is checked for + compatibility against **size** (see `size ` for additonal details). COMMENT: @@ -839,7 +846,7 @@ class Component(MDFSerializable, metaclass=ComponentsMeta): variable : 2d np.array see `variable ` - size : int or array of ints + size : typing.Union[int, typing.Iterable[typing.Union[int, tuple]]] see `size ` function : Function, function or method @@ -1105,7 +1112,7 @@ def _parse_modulable(self, param_name, param_value): def __init__(self, default_variable, param_defaults, - size=NotImplemented, # 7/5/17 CW: this is a hack to check whether the user has passed in a size arg + size=None, function=None, name=None, reset_stateful_function_when=None, @@ -1646,33 +1653,68 @@ def _handle_default_variable(self, default_variable=None, size=None): None otherwise """ default_variable = self._parse_arg_variable(default_variable) + default_variable = self._handle_size(size, default_variable) - if default_variable is None: - default_variable = self._handle_size(size, default_variable) - - if default_variable is None or default_variable is NotImplemented: - return None - else: - self._variable_shape_flexibility = self._specified_variable_shape_flexibility + if default_variable is None or default_variable is NotImplemented: + return None else: self._variable_shape_flexibility = self._specified_variable_shape_flexibility return convert_to_np_array(default_variable, dimension=1) + def _parse_size( + self, size: typing.Union[int, typing.Iterable[typing.Union[int, tuple]]] + ) -> np.ndarray: + """ + Returns the equivalent 'variable' array specified by **size** + + Args: + size (typing.Union[int, typing.Iterable[typing.Union[int, tuple]]]) + + Returns: + np.ndarray + """ + def get_size_elem(s, idx=None): + try: + return np.zeros(s) + except (TypeError, ValueError) as e: + if idx is not None: + idx_str = f' at index {idx}' + else: + idx_str = '' + + raise ComponentError( + f'Invalid size argument of {self}{idx_str}. size must be a' + ' valid numpy shape or a list of shapes for use with' + f' numpy.zeros: {e}' + ) from e + + if not is_iterable(size, exclude_str=True): + variable_from_size = np.asarray([get_size_elem(size)]) + else: + if len(size) == 0: + raise ComponentError( + f'Invalid size argument of {self}. size must not be an empty list' + ) + variable_from_size = [] + for i, s in enumerate(size): + variable_from_size.append(get_size_elem(s, i)) + variable_from_size = convert_all_elements_to_np_array(variable_from_size) + + return variable_from_size + # ELIMINATE SYSTEM # IMPLEMENTATION NOTE: (7/7/17 CW) Due to System and Process being initialized with size at the moment (which will # be removed later), I’m keeping _handle_size in Component.py. I’ll move the bulk of the function to Mechanism # through an override, when Composition is done. For now, only Port.py overwrites _handle_size(). def _handle_size(self, size, variable): """If variable is None, _handle_size tries to infer variable based on the **size** argument to the - __init__() function. This method is overwritten in subclasses like Mechanism and Port. - If self is a Mechanism, it converts variable to a 2D array, (for a Mechanism, variable[i] represents - the input from the i-th InputPort). If self is a Port, variable is a 1D array and size is a length-1 1D - array. It performs some validations on size and variable as well. This function is overridden in Port.py. - If size is NotImplemented (usually in the case of Projections/Functions), then this function passes without - doing anything. Be aware that if size is NotImplemented, then variable is never cast to a particular shape. + __init__() function. If size is None (usually in the case of + Projections/Functions), then this function passes without + doing anything. If both size and variable are not None, a + ComponentError is thrown if they are not compatible. """ - if size is not NotImplemented: + if size is not None: self._variable_shape_flexibility = self._specified_variable_shape_flexibility # region Fill in and infer variable and size if they aren't specified in args # if variable is None and size is None: @@ -1680,109 +1722,52 @@ def _handle_size(self, size, variable): # 6/30/17 now handled in the individual subclasses' __init__() methods because each subclass has different # expected behavior when variable is None and size is None. - def checkAndCastInt(x): - if not isinstance(x, numbers.Number): - raise ComponentError("An element ({}) in size is not a number.".format(x)) - if x < 1: - raise ComponentError("An element ({}) in size is not a positive number.".format(x)) - try: - int_x = int(x) - except: - raise ComponentError( - "Failed to convert an element ({}) in size argument for {} {} to an integer. size " - "should be a number, or iterable of numbers, which are integers or " - "can be converted to integers.".format(x, type(self), self.name)) - if int_x != x: - if hasattr(self, 'prefs') and hasattr(self.prefs, VERBOSE_PREF) and self.prefs.verbosePref: - warnings.warn("When an element ({}) in the size argument was cast to " - "integer, its value changed to {}.".format(x, int_x)) - return int_x + # implementation note: for good coding practices, perhaps add setting to enable easy change of the default + # value of variable (though it's an unlikely use case), which is an array of zeros at the moment - if variable is not None: - variable = np.array(variable) - if variable.dtype == object: - # CAVEAT: assuming here that object dtype implies there are list objects (i.e. array with - # different sized arrays/lists inside like [[0, 1], [2, 3, 4]]), even though putting a None - # value in the array will give object dtype. This case doesn't really make sense in our - # context though, so ignoring this case in the interest of quickly fixing 3D variable behavior - variable = np.atleast_1d(variable) + def conflict_error(reason=None): + if reason is not None: + reason_str = f': {reason}' else: - variable = np.atleast_2d(variable) + reason_str = '' - variable = convert_all_elements_to_np_array(variable) + return ComponentError( + f'size and default_variable arguments of {self} conflict{reason_str}' + ) - try: - if size is not None: - size = np.atleast_1d(size) - if len(np.shape(size)) > 1: # number of dimensions of size > 1 - if hasattr(self, 'prefs') and hasattr(self.prefs, VERBOSE_PREF) and self.prefs.verbosePref: - warnings.warn( - "size had more than one dimension (size had {} dimensions), so only the first " - "element of its highest-numbered axis will be used".format(len(np.shape(size)))) - while len(np.shape(size)) > 1: # reduce the dimensions of size - size = size[0] - except: - raise ComponentError("Failed to convert size (of type {}) to a 1D array.".format(type(size))) + variable_from_size = self._parse_size(size) - if size is not None: - size = np.array(list(map(checkAndCastInt, size))) # convert all elements of size to int + if variable is None: + return variable_from_size - # implementation note: for good coding practices, perhaps add setting to enable easy change of the default - # value of variable (though it's an unlikely use case), which is an array of zeros at the moment - if variable is None and size is not None: - try: - variable = [] - for s in size: - variable.append(np.zeros(s)) - variable = convert_to_np_array(variable) - # TODO: fix bare except - except: - raise ComponentError("variable (possibly default_variable) was not specified, but PsyNeuLink " - "was unable to infer variable from the size argument, {}. size should be" - " an integer or an array or list of integers. Either size or " - "variable must be specified.".format(size)) - - # the two regions below (creating size if it's None and/or expanding it) are probably obsolete (7/7/17 CW) - - if size is None and variable is not None: - size = [] - try: - for input_vector in variable: - size.append(len(input_vector)) - size = np.array(size) - except: - raise ComponentError( - "{}: size was not specified, and unable to infer it from the variable argument ({}) " - "-- it can be an array, list, a 2D array, a list of arrays, array of lists, etc. ". - format(self.name, variable)) - # endregion - - if size is not None and variable is not None: - if len(size) == 1 and len(variable) > 1: - new_size = np.empty(len(variable)) - new_size.fill(size[0]) - size = new_size - - # the two lines below were used when size was a param and are likely obsolete (7/7/17 CW) - # param_defaults['size'] = size # 7/5/17 potentially buggy? Not sure (CW) - # self.user_params_for_instantiation['size'] = None # 7/5/17 VERY HACKY: See Changyan's Notes on this. - - # Both variable and size are specified - if variable is not None and size is not None: - # If they conflict, give warning - if len(size) != len(variable): - if hasattr(self, 'prefs') and hasattr(self.prefs, VERBOSE_PREF) and self.prefs.verbosePref: - warnings.warn("The size arg of {} conflicts with the length " - "of its variable arg ({}) at element {}: variable takes precedence". - format(self.name, size, variable)) + if is_iterable(size, exclude_str=True): + assert len(size) == len(variable_from_size) + + if variable.ndim == 0: + raise conflict_error( + 'size gives a list of items but default_variable is 0d' + ) + elif len(size) != len(variable): + raise conflict_error( + f'len(size) is {len(size)};' + f' len(default_variable) is {len(variable)}' + ) else: for i in range(len(size)): - if size[i] != len(variable[i]): - if hasattr(self, 'prefs') and hasattr(self.prefs, VERBOSE_PREF) and self.prefs.verbosePref: - warnings.warn("The size arg of {} ({}) conflicts with the length " - "of its variable arg ({}) at element {}: variable takes precedence". - format(self.name, size[i], variable[i], i)) + if variable_from_size[i].shape != variable[i].shape: + raise conflict_error( + f'size[{i}].shape: {variable_from_size[i].shape};' + f' default_variable[{i}].shape: {variable[i].shape}' + ) + else: + if variable_from_size.shape != variable.shape: + raise conflict_error( + f'size.shape: {variable_from_size.shape};' + f' default_variable.shape: {variable.shape}' + ) + # if variable_from_size is created an error has not been thrown + # so far, variable is equal return variable def _get_allowed_arguments(self) -> set: diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index fed6c4a6327..79caf6c97d4 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -1222,12 +1222,15 @@ class Mechanism_Base(Mechanism): of its `function ` if those are not specified. If it is not specified, then a subclass-specific default is assigned (usually [[0]]). - size : int, list or np.ndarray of ints : default None + size : int, or Iterable of tuples or ints : default None specifies default_variable as array(s) of zeros if **default_variable** is not passed as an argument; - if **default_variable** is specified, it takes precedence over the specification of **size**. + if **default_variable** is specified, it must be equivalent to + **size**. For example, the following Mechanisms are equivalent:: my_mech = ProcessingMechanism(size = [3, 2]) my_mech = ProcessingMechanism(default_variable = [[0, 0, 0], [0, 0]]) + When specified as an iterable, each element of **size** is used + as the size of the corresponding InputPort. input_ports : str, list, dict, or np.ndarray : default None specifies the InputPorts for the Mechanism; if it is not specified, a single InputPort is created diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index 1bfeb0a36a4..a3d23e6da86 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -1124,58 +1124,6 @@ def __init__(self, if context.source == ContextFlags.COMMAND_LINE: owner.add_ports([self]) - def _handle_size(self, size, variable): - """Overwrites the parent method in Component.py, because the variable of a Port - is generally 1D, rather than 2D as in the case of Mechanisms - """ - if size is not NotImplemented: - - def checkAndCastInt(x): - if not isinstance(x, numbers.Number): - raise PortError("Size ({}) is not a number.".format(x)) - if x < 1: - raise PortError("Size ({}) is not a positive number.".format(x)) - try: - int_x = int(x) - except: - raise PortError( - "Failed to convert size argument ({}) for {} {} to an integer. For Ports, size " - "should be a number, which is an integer or can be converted to integer.". - format(x, type(self), self.name)) - if int_x != x: - if hasattr(self, 'prefs') and hasattr(self.prefs, VERBOSE_PREF) and self.prefs.verbosePref: - warnings.warn("When size ({}) was cast to integer, its value changed to {}.".format(x, int_x)) - return int_x - - # region Convert variable to a 1D array, cast size to an integer - if size is not None: - size = checkAndCastInt(size) - try: - if variable is not None: - variable = np.atleast_1d(variable) - except: - raise PortError("Failed to convert variable (of type {}) to a 1D array.".format(type(variable))) - # endregion - - # region if variable is None and size is not None, make variable a 1D array of zeros of length = size - if variable is None and size is not None: - try: - variable = np.zeros(size) - except: - raise ComponentError("variable (perhaps default_variable) was not specified, but PsyNeuLink " - "was unable to infer variable from the size argument, {}. size should be" - " an integer or able to be converted to an integer. Either size or " - "variable must be specified.".format(size)) - #endregion - - if variable is not None and size is not None: # try tossing this "if" check - # If they conflict, raise exception - if size != len(variable): - raise PortError("The size arg of {} ({}) conflicts with the length of its variable arg ({})". - format(self.name, size, variable)) - - return variable - def _validate_variable(self, variable, context=None): """Validate variable and return validated variable diff --git a/psyneulink/library/components/mechanisms/processing/transfer/contrastivehebbianmechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/contrastivehebbianmechanism.py index a08a6185f18..460063a0d15 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/contrastivehebbianmechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/contrastivehebbianmechanism.py @@ -1032,7 +1032,6 @@ def __init__(self, self.target_start = 0 self._target_included = False self.target_end = self.target_start + target_size - size = self.recurrent_size default_variable = [np.zeros(input_size), np.zeros(self.recurrent_size)] # Set InputPort sizes in _instantiate_input_ports, @@ -1059,7 +1058,6 @@ def __init__(self, super().__init__( default_variable=default_variable, - size=size, input_ports=input_ports, combination_function=combination_function, function=function, diff --git a/tests/composition/test_interfaces.py b/tests/composition/test_interfaces.py index a84c7603346..b43bf535656 100644 --- a/tests/composition/test_interfaces.py +++ b/tests/composition/test_interfaces.py @@ -500,8 +500,8 @@ def test_user_added_ports(self): mech = ProcessingMechanism() comp.add_node(mech) # instantiate custom input and output ports - inp = InputPort(size=2) - out = OutputPort(size=2) + inp = InputPort() + out = OutputPort() # NOTE: Adding ports to CIM from command line is currenlty disallowed # # add custom input and output ports to CIM diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index 5959fd96fd4..92955c64a38 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -529,25 +529,6 @@ def test_DDM_size_int_inputs(): # INVALID INPUTS -# ------------------------------------------------------------------------------------------------ -# TEST 1 -# size = 0, check less-than-one error - - -def test_DDM_mech_size_zero(): - with pytest.raises(ComponentError) as error_text: - T = DDM( - name='DDM', - size=0, - function=DriftDiffusionIntegrator( - noise=0.0, - rate=-5.0, - time_step_size=1.0 - ), - execute_until_finished=False, - ) - assert "is not a positive number" in str(error_text.value) - # ------------------------------------------------------------------------------------------------ # TEST 2 # size = -1.0, check less-than-one error @@ -557,7 +538,7 @@ def test_DDM_mech_size_negative_one(): with pytest.raises(ComponentError) as error_text: T = DDM( name='DDM', - size=-1.0, + size=-1, function=DriftDiffusionIntegrator( noise=0.0, rate=-5.0, @@ -565,7 +546,7 @@ def test_DDM_mech_size_negative_one(): ), execute_until_finished=False, ) - assert "is not a positive number" in str(error_text.value) + assert "negative dimensions" in str(error_text.value) # ------------------------------------------------------------------------------------------------ # TEST 3 @@ -576,7 +557,7 @@ def test_DDM_size_too_large(): with pytest.raises(DDMError) as error_text: T = DDM( name='DDM', - size=3.0, + size=3, function=DriftDiffusionIntegrator( noise=0.0, rate=-5.0, diff --git a/tests/mechanisms/test_transfer_mechanism.py b/tests/mechanisms/test_transfer_mechanism.py index 2578f122ee8..0dad5034f89 100644 --- a/tests/mechanisms/test_transfer_mechanism.py +++ b/tests/mechanisms/test_transfer_mechanism.py @@ -998,48 +998,6 @@ def test_transfer_mech_size_int_inputs_floats(self): # val = T.execute([Linear().execute(), NormalDist().execute(), Exponential().execute(), ExponentialDist().execute()]) # np.testing.assert_allclose(val, [[np.array([0.]), 0.4001572083672233, np.array([1.]), 0.7872011523172707]] - # ------------------------------------------------------------------------------------------------ - # TEST 5 - # size = float, check if variable is an array of zeros - - @pytest.mark.mechanism - @pytest.mark.transfer_mechanism - def test_transfer_mech_size_float_inputs_check_var(self): - T = TransferMechanism( - name='T', - size=4.0, - ) - np.testing.assert_array_equal(T.defaults.variable, [[0, 0, 0, 0]]) - assert len(T.size == 1) and T.size[0] == 4.0 and isinstance(T.size[0], np.integer) - - # ------------------------------------------------------------------------------------------------ - # TEST 6 - # size = float, variable = list of ints - - @pytest.mark.mechanism - @pytest.mark.transfer_mechanism - def test_transfer_mech_size_float_inputs_ints(self): - T = TransferMechanism( - name='T', - size=4.0 - ) - val = T.execute([10, 10, 10, 10]) - np.testing.assert_array_equal(val, [[10.0, 10.0, 10.0, 10.0]]) - - # ------------------------------------------------------------------------------------------------ - # TEST 7 - # size = float, variable = list of floats - - @pytest.mark.mechanism - @pytest.mark.transfer_mechanism - def test_transfer_mech_size_float_inputs_floats(self): - T = TransferMechanism( - name='T', - size=4.0 - ) - val = T.execute([10.0, 10.0, 10.0, 10.0]) - np.testing.assert_array_equal(val, [[10.0, 10.0, 10.0, 10.0]]) - # ------------------------------------------------------------------------------------------------ # TEST 8 # size = float, variable = list of functions @@ -1069,18 +1027,6 @@ def test_transfer_mech_size_list_of_ints(self): assert len(T.defaults.variable) == 3 and len(T.defaults.variable[0]) == 2 and len(T.defaults.variable[1]) == 3 and len(T.defaults.variable[2]) == 4 # ------------------------------------------------------------------------------------------------ - # TEST 10 - # size = list of floats, check that variable is correct - - @pytest.mark.mechanism - @pytest.mark.transfer_mechanism - def test_transfer_mech_size_list_of_floats(self): - T = TransferMechanism( - name='T', - size=[2., 3., 4.] - ) - assert len(T.defaults.variable) == 3 and len(T.defaults.variable[0]) == 2 and len(T.defaults.variable[1]) == 3 and len(T.defaults.variable[2]) == 4 - # note that this output under the Linear function is useless/odd, but the purpose of allowing this configuration # is for possible user-defined functions that do use unusual shapes. @@ -1089,7 +1035,7 @@ def test_transfer_mech_size_list_of_floats(self): def test_transfer_mech_size_var_both_lists(self): T = TransferMechanism( name='T', - size=[2., 3.], + size=[2, 3], default_variable=[[1, 2], [3, 4, 5]] ) assert len(T.defaults.variable) == 2 @@ -1103,13 +1049,14 @@ def test_transfer_mech_size_var_both_lists(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_size_scalar_var_2d(self): - T = TransferMechanism( - name='T', - size=2, - default_variable=[[1, 2], [3, 4]] - ) - np.testing.assert_array_equal(T.defaults.variable, [[1, 2], [3, 4]]) - assert len(T.size) == 2 and T.size[0] == 2 and T.size[1] == 2 + with pytest.raises( + ComponentError, match=r'size and default_variable arguments.*conflict.*' + ): + TransferMechanism( + name='T', + size=2, + default_variable=[[1, 2], [3, 4]] + ) # ------------------------------------------------------------------------------------------------ # TEST 13 @@ -1131,14 +1078,14 @@ def test_transfer_mech_var_2d_array(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_var_1D_size_wrong(self): - T = TransferMechanism( - name='T', - default_variable=[1, 2, 3, 4], - size=2 - ) - np.testing.assert_array_equal(T.defaults.variable, [[1, 2, 3, 4]]) - val = T.execute([10.0, 10.0, 10.0, 10.0]) - np.testing.assert_array_equal(val, [[10.0, 10.0, 10.0, 10.0]]) + with pytest.raises( + ComponentError, match=r'size and default_variable arguments.*conflict.*' + ): + TransferMechanism( + name='T', + default_variable=[1, 2, 3, 4], + size=2 + ) # ------------------------------------------------------------------------------------------------ # TEST 15 @@ -1147,14 +1094,14 @@ def test_transfer_mech_var_1D_size_wrong(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_var_1D_size_wrong_2(self): - T = TransferMechanism( - name='T', - default_variable=[1, 2, 3, 4], - size=[2, 3, 4] - ) - np.testing.assert_array_equal(T.defaults.variable, [[1, 2, 3, 4]]) - val = T.execute([10.0, 10.0, 10.0, 10.0]) - np.testing.assert_array_equal(val, [[10.0, 10.0, 10.0, 10.0]]) + with pytest.raises( + ComponentError, match=r'size and default_variable arguments.*conflict.*' + ): + TransferMechanism( + name='T', + default_variable=[1, 2, 3, 4], + size=[2, 3, 4] + ) # ------------------------------------------------------------------------------------------------ # TEST 16 @@ -1163,14 +1110,14 @@ def test_transfer_mech_var_1D_size_wrong_2(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_size_var_incompatible1(self): - T = TransferMechanism( - name='T', - size=2, - default_variable=[[1, 2], [3, 4, 5]] - ) - assert len(T.defaults.variable) == 2 - np.testing.assert_array_equal(T.defaults.variable[0], [1, 2]) - np.testing.assert_array_equal(T.defaults.variable[1], [3, 4, 5]) + with pytest.raises( + ComponentError, match=r'size and default_variable arguments.*conflict.*' + ): + TransferMechanism( + name='T', + size=2, + default_variable=[[1, 2], [3, 4, 5]] + ) # ------------------------------------------------------------------------------------------------ # TEST 17 @@ -1179,33 +1126,19 @@ def test_transfer_mech_size_var_incompatible1(self): @pytest.mark.mechanism @pytest.mark.transfer_mechanism def test_transfer_mech_size_var_incompatible2(self): - T = TransferMechanism( - name='T', - size=[2, 2], - default_variable=[[1, 2], [3, 4, 5]] - ) - assert len(T.defaults.variable) == 2 - np.testing.assert_array_equal(T.defaults.variable[0], [1, 2]) - np.testing.assert_array_equal(T.defaults.variable[1], [3, 4, 5]) + with pytest.raises( + ComponentError, match=r'size and default_variable arguments.*conflict.*' + ): + TransferMechanism( + name='T', + size=[2, 2], + default_variable=[[1, 2], [3, 4, 5]] + ) # ------------------------------------------------------------------------------------------------ # INVALID INPUTS - # ------------------------------------------------------------------------------------------------ - # TEST 1 - # size = 0, check less-than-one error - - @pytest.mark.mechanism - @pytest.mark.transfer_mechanism - def test_transfer_mech_size_zero(self): - with pytest.raises(ComponentError) as error_text: - T = TransferMechanism( - name='T', - size=0, - ) - assert "is not a positive number" in str(error_text.value) - # ------------------------------------------------------------------------------------------------ # TEST 2 # size = -1.0, check less-than-one error @@ -1216,9 +1149,9 @@ def test_transfer_mech_size_negative_one(self): with pytest.raises(ComponentError) as error_text: T = TransferMechanism( name='T', - size=-1.0, + size=-1, ) - assert "is not a positive number" in str(error_text.value) + assert "negative dimensions" in str(error_text.value) # this test below and the (currently commented) test immediately after it _may_ be deprecated if we ever fix # warnings to be no longer fatal. At the time of writing (6/30/17, CW), warnings are always fatal.