From 3de156c2ccd2f5cd9e361bceeb45e52e56f19522 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 31 Dec 2023 15:23:38 -0500 Subject: [PATCH 01/14] tests/Composition: Check that running/executing with no inputs produces a warning Fixes 2 instances of: UserWarning: No inputs provided in call to ... Signed-off-by: Jan Vesely --- tests/composition/test_composition.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 40ce2f5beea..6f2381f3772 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -3833,7 +3833,10 @@ def test_execute_no_inputs(self, mode): inner_comp = Composition(pathways=[m_inner]) m_outer = ProcessingMechanism(size=2) outer_comp = Composition(pathways=[m_outer, inner_comp]) - result = outer_comp.run(execution_mode=mode) + + with pytest.warns(UserWarning, match="No inputs provided in call"): + result = outer_comp.run(execution_mode=mode) + np.testing.assert_allclose(result, [[0.0, 0.0]]) @pytest.mark.composition @@ -3842,7 +3845,10 @@ def test_run_no_inputs(self, comp_mode): inner_comp = Composition(pathways=[m_inner]) m_outer = ProcessingMechanism(size=2) outer_comp = Composition(pathways=[m_outer, inner_comp]) - result = outer_comp.run(execution_mode=comp_mode) + + with pytest.warns(UserWarning, match="No inputs provided in call"): + result = outer_comp.run(execution_mode=comp_mode) + np.testing.assert_allclose(result, [[0.0, 0.0]]) def test_lpp_invalid_matrix_keyword(self): From 41b041ce0f62f8e4303eeb571915c3fac69b784c Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 31 Dec 2023 17:24:48 -0500 Subject: [PATCH 02/14] tests/learning: Enable node_spec_types test. Simplify parametrization ids. Fixes: SyntaxWarning: "is" with a literal. Did you mean "=="? Signed-off-by: Jan Vesely --- tests/composition/test_learning.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/composition/test_learning.py b/tests/composition/test_learning.py index f46b7472c2e..2cd856be390 100644 --- a/tests/composition/test_learning.py +++ b/tests/composition/test_learning.py @@ -36,8 +36,9 @@ def xor_network(): matrix=np.full((10,1), 0.1), sender=hidden_layer, receiver=output_layer) - inputs = np.array([[0, 0],[0, 1],[1, 0],[1, 1]]) - targets = np.array([[0],[1],[1],[0]]) + inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) + targets = np.array([[0], [1], [1], [0]]) + def _get_comp_type(comp_type, comp_learning_rate, pathway_learning_rate): if comp_type == 'composition': xor = Composition(learning_rate=comp_learning_rate) @@ -65,18 +66,15 @@ def _get_comp_type(comp_type, comp_learning_rate, pathway_learning_rate): class TestInputAndTargetSpecs: @pytest.mark.pytorch - @pytest.mark.parametrize('input_type', ['dict', 'func', 'gen', 'gen_func'], - ids=['dict', 'func', 'gen', 'gen_func']) + @pytest.mark.parametrize('input_type', ['dict', 'func', 'gen', 'gen_func']) @pytest.mark.parametrize('exec_mode', [pnl.ExecutionMode.PyTorch, pnl.ExecutionMode.LLVMRun, - pnl.ExecutionMode.Python], - ids=['PyTorch', 'LLVM', 'Python']) - @pytest.mark.parametrize('comp_type', ['composition', 'autodiff'], - ids=['composition', 'autodiff']) - def node_spec_types(self, xor_network, comp_type, input_type, exec_mode): + pnl.ExecutionMode.Python]) + @pytest.mark.parametrize('comp_type', ['composition', 'autodiff']) + def test_node_spec_types(self, xor_network, comp_type, input_type, exec_mode): if comp_type == 'composition' and exec_mode != pnl.ExecutionMode.Python: - pytest.skip(f"Execution mode {exec_mode} not relevant for Composition") + pytest.skip(f"Execution mode {exec_mode} not relevant for Composition learn") comp, input_layer, hidden_layer, output_layer, target_mechanism, stims, targets =\ xor_network(comp_type, 0.001, None) @@ -113,12 +111,8 @@ def get_inputs_gen(): else: assert False, f"Unrecognized input_type: {input_type}" - expected_results = [[0.6341436044849351]] - if comp_type is 'composition': - results = comp.learn(inputs=inputs) - else: - results = comp.learn(inputs=inputs, execution_mode=exec_mode) - np.testing.assert_allclose(results, expected_results) + results = comp.learn(inputs=inputs, execution_mode=exec_mode) + np.testing.assert_allclose(results, [[0.6341436044849351]]) @pytest.mark.composition @pytest.mark.pytorch From f83218ded3351a6d92f06bae527fee4e7a66c946 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 3 Jan 2024 15:34:54 -0500 Subject: [PATCH 03/14] tests/LCA: Add invocation wrapper to use in benchmark fixture Return both result and num_executions_before_finished. Fixes: PytestBenchmarkWarning: Benchmark fixture was not used at all in this test! Signed-off-by: Jan Vesely --- tests/mechanisms/test_lca.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/mechanisms/test_lca.py b/tests/mechanisms/test_lca.py index 7374f1e0679..9996dca42d6 100644 --- a/tests/mechanisms/test_lca.py +++ b/tests/mechanisms/test_lca.py @@ -185,12 +185,14 @@ def test_LCAMechanism_threshold_with_convergence(self, benchmark, comp_mode): comp = Composition() comp.add_node(lca) - result = comp.run(inputs={lca:[0,1,2]}, execution_mode=comp_mode) - np.testing.assert_allclose(result, [[0.19153799, 0.5, 0.80846201]]) + def func(*args, **kwargs): + res = comp.run(*args, **kwargs) + return (res, lca.num_executions_before_finished) + + results = benchmark(func, inputs={lca:[0,1,2]}, execution_mode=comp_mode) + np.testing.assert_allclose(results[0], [[0.19153799, 0.5, 0.80846201]]) if comp_mode is pnl.ExecutionMode.Python: - assert lca.num_executions_before_finished == 18 - if benchmark.enabled: - benchmark(comp.run, inputs={lca:[0,1,2]}, execution_mode=comp_mode) + assert results[1] == 18 @pytest.mark.composition @pytest.mark.lca_mechanism From a6242aa515f1340cbe90e24f4edac3ee4b277cc6 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Wed, 3 Jan 2024 16:38:10 -0500 Subject: [PATCH 04/14] tests/control: Only consider first 10 values in checks Makes the test benchmark agnostic. Remove explicit benchmark re-run from the test. Fixes: PytestBenchmarkWarning: Benchmark fixture was not used at all in this test! Signed-off-by: Jan Vesely --- tests/composition/test_control.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index bda69ad4353..68f96ae8fed 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -3710,22 +3710,17 @@ def test_grid_search_random_selection(self, comp_mode, benchmark): inputs = {A: [[[1.0]]]} - comp.run(inputs=inputs, num_trials=10, context='outer_comp', execution_mode=comp_mode) - np.testing.assert_allclose(comp.results, [[[0.7310585786300049]], [[0.999999694097773]], [[0.999999694097773]], [[0.9999999979388463]], [[0.9999999979388463]], [[0.999999694097773]], [[0.9999999979388463]], [[0.999999999986112]], [[0.999999694097773]], [[0.9999999999999993]]]) + benchmark(comp.run, inputs=inputs, num_trials=10, context='outer_comp', execution_mode=comp_mode) + np.testing.assert_allclose(comp.results[:10], + [[[0.7310585786300049]], [[0.999999694097773]], [[0.999999694097773]], [[0.9999999979388463]], [[0.9999999979388463]], + [[0.999999694097773]], [[0.9999999979388463]], [[0.999999999986112]], [[0.999999694097773]], [[0.9999999999999993]]]) # control signal value (mod slope) is chosen randomly from all of the control signal values # that correspond to a net outcome of 1 if comp_mode is pnl.ExecutionMode.Python: log_arr = A.log.nparray_dictionary() np.testing.assert_allclose([[1.], [15.], [15.], [20.], [20.], [15.], [20.], [25.], [15.], [35.]], - log_arr['outer_comp']['mod_slope']) - - if benchmark.enabled: - # Disable logging for the benchmark run - A.log.set_log_conditions(items="mod_slope", log_condition=LogCondition.OFF) - A.log.clear_entries() - benchmark(comp.run, inputs=inputs, num_trials=10, context='bench_outer_comp', execution_mode=comp_mode) - assert len(A.log.get_logged_entries()) == 0 + log_arr['outer_comp']['mod_slope'][:10]) def test_input_CIM_assignment(self, comp_mode): From 52c89dca508e545f6a2519cba9270775bba5cf6e Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 6 Jan 2024 15:07:19 -0500 Subject: [PATCH 05/14] setup: Remove stale warning filter. Having multiple parameter ports for parameters of the same name is now an error. There is no difference in emitted warnings with or without this filter. Signed-off-by: Jan Vesely --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 36b832f292d..318879c8f93 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,7 +69,6 @@ xfail_strict = True filterwarnings = error:Creating an ndarray from ragged nested sequences \(which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes\) is deprecated.*:numpy.VisibleDeprecationWarning error:Invalid escape sequence - ignore:Multiple ParameterPorts:UserWarning [pycodestyle] # for code explanation see https://pep8.readthedocs.io/en/latest/intro.html#error-codes From fc75cd2248efa3d6534f54167dffad61dceeb52b Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 6 Jan 2024 15:14:04 -0500 Subject: [PATCH 06/14] tests/Composition: Remove spurious return statement Tests should not return anything. Fixes: PytestReturnNotNoneWarning: Expected None, but tests/composition/test_composition.py::TestNestedCompositions::test_invalid_projection_deletion_when_nesting_comps returned (Composition ocomp), which will be an error in a future version of pytest. Did you mean to use `assert` instead of `return`? Signed-off-by: Jan Vesely --- tests/composition/test_composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 6f2381f3772..e8178ae6e52 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -4928,7 +4928,7 @@ def test_invalid_projection_deletion_when_nesting_comps(self): allocation_samples=pnl.SampleSpec(start=1.0, stop=5.0, num=5))]) ) assert not ocomp._check_for_existing_projections(sender=ib, receiver=ocomp_objective_mechanism) - return ocomp + # # Does not work yet due to initialize_cycle_values bug that causes first recurrent projection to pass different values # # to TranfserMechanism version vs Logistic fn + AdaptiveIntegrator fn version # def test_recurrent_transfer_mechanism_composition(self): From dbb30cb326df7e49b365a464c48f73a10b257c81 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sat, 6 Jan 2024 20:33:47 -0500 Subject: [PATCH 07/14] tests: Don't use deprecated pytest.warns(None) The construct has been deprecated in pytest warns about it: PytestRemovedIn8Warning: Passing None has been deprecated. See https://docs.pytest.org/en/latest/how-to/capture-warnings.html#additional-use-cases-of-warnings-in-tests for alternatives in common use cases. Address three different situations in tests in a way that does not hide unrelated warnings. * test_composition.py::*_subset_duplicate_warnings: capture all warnings and search them for the expected message if verbosity == True assert that there are no warnings if verbosity == False * test_projection_specifications.py::test_no_warning_when_matrix_specified: Use filterwarnings mark to change the undesired warning into an error * test_control.py::test_model_based_num_estimates Use null context or pytest.warns context based on whether warning is expected Signed-off-by: Jan Vesely --- tests/composition/test_composition.py | 46 +++++++++++-------- tests/composition/test_control.py | 22 ++++----- .../test_projection_specifications.py | 32 ++++++------- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index e8178ae6e52..29c1af6658d 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -1185,19 +1185,22 @@ def test_add_processing_pathway_subset_duplicate_warning(self, verbosity): C = TransferMechanism() comp = Composition() - comp.add_linear_processing_pathway(pathway=[A,B,C]) + comp.add_linear_processing_pathway(pathway=[A, B, C]) + comp.verbosePref = PreferenceEntry(verbosity, PreferenceLevel.INSTANCE) + + with warnings.catch_warnings(record=True) as msgs: + comp.add_linear_processing_pathway(pathway=[A, B]) - # Test for warning if verbosePref is set to True if verbosity: - regexp = f"Pathway specified in 'pathway' arg for add_linear_processing_pathway method of '{comp.name}' " \ - f"has a subset of nodes in a Pathway already in '{comp.name}': Pathway-0; the latter will be used." - with pytest.warns(UserWarning, match=regexp): - comp.verbosePref = PreferenceEntry(True, PreferenceLevel.INSTANCE) - comp.add_linear_processing_pathway(pathway=[A,B]) + # Test for warning if verbosePref is set to True + warning = f"Pathway specified in 'pathway' arg for add_linear_processing_pathway method of '{comp.name}' " \ + f"has a subset of nodes in a Pathway already in '{comp.name}': Pathway-0; the latter will be used." + + # The above issues 2 warnings, but we only test for one of them here + assert any(str(m.message) == warning for m in msgs), list(str(m.message) for m in msgs) else: - # Test for suppression of warning if verbosePref not set - with pytest.warns(None): - comp.add_linear_processing_pathway(pathway=[A,B]) + # Test for suppression of warning if verbosePref is not set + assert len(msgs) == 0 def test_add_backpropagation_pathway_exact_duplicate_warning(self): A = TransferMechanism() @@ -1230,19 +1233,24 @@ def test_add_backpropagation_pathway_contiguous_subset_duplicate_warning(self, v B = TransferMechanism() C = TransferMechanism() comp = Composition() - comp.add_backpropagation_learning_pathway(pathway=[A,B,C]) + comp.add_backpropagation_learning_pathway(pathway=[A, B, C]) + + comp.verbosePref = PreferenceEntry(verbosity, PreferenceLevel.INSTANCE) + + with warnings.catch_warnings(record=True) as msgs: + comp.add_backpropagation_learning_pathway(pathway=[A, B]) - # Test for warning if verbosePref is set to True if verbosity: - regexp = f"Pathway specified in 'pathway' arg for add_backpropagation_learning_pathway method of '{comp.name}'" \ - f" has a subset of nodes in a Pathway already in '{comp.name}':.*; the latter will be used." - with pytest.warns(UserWarning, match=regexp): - comp.verbosePref = PreferenceEntry(True, PreferenceLevel.INSTANCE) - comp.add_backpropagation_learning_pathway(pathway=[A,B]) + # Test for warning if verbosePref is set to True + warning = f"Pathway specified in 'pathway' arg for add_backpropagation_learning_pathway method of '{comp.name}'" \ + f" has a subset of nodes in a Pathway already in '{comp.name}': Pathway-0; the latter will be used." + + # The above issues 2 warnings, but we only test for one of them here + assert any(str(m.message) == warning for m in msgs), list(str(m.message) for m in msgs) else: # Test for suppression of warning if verbosePref is not set - with pytest.warns(None): - comp.add_backpropagation_learning_pathway(pathway=[A,B]) + assert len(msgs) == 0 + def test_add_processing_pathway_non_contiguous_subset_is_OK(self): A = TransferMechanism() diff --git a/tests/composition/test_control.py b/tests/composition/test_control.py index 68f96ae8fed..0533cd934c2 100644 --- a/tests/composition/test_control.py +++ b/tests/composition/test_control.py @@ -1,5 +1,5 @@ +import contextlib import re - import numpy as np import pytest @@ -3587,27 +3587,27 @@ def test_model_based_num_estimates(self, num_estimates, rand_var): intensity_cost_function=pnl.Linear(slope=0.)) objective_mech = pnl.ObjectiveMechanism(monitor=[B]) - warning_type = None + warning_msg = f"'OptimizationControlMechanism-0' has 'num_estimates = {num_estimates}' specified, " \ + f"but its 'agent_rep' \\('comp'\\) has no random variables: " \ + f"'RANDOMIZATION_CONTROL_SIGNAL' will not be created, and num_estimates set to None." + if num_estimates and not rand_var: - warning_type = UserWarning - warning_msg = f'"\'OptimizationControlMechanism-0\' has \'num_estimates = {num_estimates}\' specified, ' \ - f'but its \'agent_rep\' (\'comp\') has no random variables: ' \ - f'\'RANDOMIZATION_CONTROL_SIGNAL\' will not be created, and num_estimates set to None."' - with pytest.warns(warning_type) as warnings: + warning_context = pytest.warns(UserWarning, match=warning_msg) + else: + warning_context = contextlib.nullcontext() + + with warning_context: ocm = pnl.OptimizationControlMechanism(agent_rep=comp, state_features=[A.input_port], objective_mechanism=objective_mech, function=pnl.GridSearch(), num_estimates=num_estimates, control_signals=[control_signal]) - if warning_type: - assert any(warning_msg == repr(w.message.args[0]) for w in warnings) comp.add_controller(ocm) inputs = {A: [[[1.0]]]} - comp.run(inputs=inputs, - num_trials=2) + comp.run(inputs=inputs, num_trials=2) if not num_estimates or not rand_var: assert pnl.RANDOMIZATION_CONTROL_SIGNAL not in comp.controller.control_signals # Confirm no estimates diff --git a/tests/projections/test_projection_specifications.py b/tests/projections/test_projection_specifications.py index b1602f131fe..adee0838155 100644 --- a/tests/projections/test_projection_specifications.py +++ b/tests/projections/test_projection_specifications.py @@ -462,25 +462,23 @@ def test_formats_for_gating_specification_of_input_and_output_ports(self, input_ # assert 'Primary OutputPort of ControlMechanism-1 (ControlSignal-0) ' \ # 'cannot be used as a sender of a Projection to OutputPort of T2' in error_text.value.args[0] + @pytest.mark.filterwarnings("error:elementwise comparison failed; returning scalar instead:") def test_no_warning_when_matrix_specified(self): - with pytest.warns(None) as w: - c = pnl.Composition() - m0 = pnl.ProcessingMechanism( - default_variable=[0, 0, 0, 0] - ) - p0 = pnl.MappingProjection( - matrix=[[0, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 0], - [0, 0, 0, 0]] - ) - m1 = pnl.TransferMechanism( - default_variable=[0, 0, 0, 0] - ) - c.add_linear_processing_pathway([m0, p0, m1]) - for warn in w: - assert 'elementwise comparison failed; returning scalar instead' not in warn.message.args[0] + c = pnl.Composition() + m0 = pnl.ProcessingMechanism( + default_variable=[0, 0, 0, 0] + ) + p0 = pnl.MappingProjection( + matrix=[[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]] + ) + m1 = pnl.TransferMechanism( + default_variable=[0, 0, 0, 0] + ) + c.add_linear_processing_pathway([m0, p0, m1]) # KDM: this is a good candidate for pytest.parametrize def test_masked_mapping_projection(self): From a532afb7147dd7449719dcc10172671415979691 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 7 Jan 2024 09:59:41 -0500 Subject: [PATCH 08/14] tests/MemoryFunctions: Escape special characters in regexp Square brackets '[' denote a match set and need to be escaped. Fixes: FutureWarning: Possible nested set at position ... Signed-off-by: Jan Vesely --- tests/functions/test_memory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functions/test_memory.py b/tests/functions/test_memory.py index 2ccb11e032a..c4cc72707d5 100644 --- a/tests/functions/test_memory.py +++ b/tests/functions/test_memory.py @@ -444,7 +444,7 @@ def test_DictionaryMemory_without_assoc(self): def test_DictionaryMemory_with_duplicate_entry_in_initializer_warning(self): - regexp = r'Attempt to initialize memory of DictionaryMemory with an entry \([[1 2 3]' + regexp = r'Attempt to initialize memory of DictionaryMemory with an entry \(\[\[1 2 3\]' with pytest.warns(UserWarning, match=regexp): em = EpisodicMemoryMechanism( name='EPISODIC MEMORY MECH', @@ -1034,7 +1034,7 @@ def test_ContentAddressableMemory_without_initializer_and_diff_field_sizes(self) def test_ContentAddressableMemory_with_duplicate_entry_in_initializer_warning(self): - regexp = r'Attempt to initialize memory of ContentAddressableMemory with an entry \([[1 2 3]' + regexp = r'Attempt to initialize memory of ContentAddressableMemory with an entry \(\[\[1 2 3\]' with pytest.warns(UserWarning, match=regexp): c = ContentAddressableMemory( initializer=np.array([[[1,2,3], [4,5,6]], From 12cffacce160510f30f652646faf323dfa338545 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 7 Jan 2024 11:23:45 -0500 Subject: [PATCH 09/14] Functions/MemoryFunctions: Do not use comparisons to empty list Convert tested value to np.array and check its size. Fixes: DeprecationWarning: The truth value of an empty array is ambiguous. ... DeprecationWarning: elementwise comparison failed; ... Signed-off-by: Jan Vesely --- .../core/components/functions/stateful/memoryfunctions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psyneulink/core/components/functions/stateful/memoryfunctions.py b/psyneulink/core/components/functions/stateful/memoryfunctions.py index bdd7c81d369..c87886b5c52 100644 --- a/psyneulink/core/components/functions/stateful/memoryfunctions.py +++ b/psyneulink/core/components/functions/stateful/memoryfunctions.py @@ -301,7 +301,7 @@ def reset(self, previous_value=None, context=None): if previous_value is None: previous_value = self._get_current_parameter_value("initializer", context) - if previous_value is None or previous_value == []: + if previous_value is None or np.asarray(previous_value).size == 0: self.parameters.previous_value._get(context).clear() value = deque([], maxlen=self.parameters.history.get(context)) @@ -1752,7 +1752,7 @@ def _get_distance(self, cue:Union[list, np.ndarray], field_weights = self._get_current_parameter_value('distance_field_weights', context) # Set any items in field_weights to None if they are None or an empty list: field_weights = np.atleast_1d([None if - fw is None or fw == [] or isinstance(fw, np.ndarray) and fw.tolist()==[] + fw is None or np.asarray(fw).size == 0 else fw for fw in field_weights]) if granularity == 'per_field': @@ -1763,7 +1763,7 @@ def _get_distance(self, cue:Union[list, np.ndarray], if len(field_weights)==1: field_weights = np.full(num_fields, field_weights[0]) for i in range(num_fields): - if not any([item is None or item == [] or isinstance(item, np.ndarray) and item.tolist() == [] + if not any([item is None or np.asarray(item).size == 0 for item in [cue[i], candidate[i], field_weights[i]]]): distances_by_field[i] = distance_fct([cue[i], candidate[i]]) * field_weights[i] return list(distances_by_field) @@ -2623,7 +2623,7 @@ def reset(self, previous_value=None, context=None): if previous_value is None: previous_value = self._get_current_parameter_value("initializer", context) - if previous_value == []: + if np.asarray(previous_value).size == 0: value = np.ndarray(shape=(2, 0, len(self.defaults.variable[0]))) self.parameters.previous_value._set(copy.deepcopy(value), context) From fcd842c13812382cff4808b34d3a2efb02bb3657 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Thu, 11 Jan 2024 13:58:39 -0500 Subject: [PATCH 10/14] Distance, LinearCombination: Only compute np.log if needed The expression replaces calculated np.log values with 0 if both v1 and v2 values at a given index are 0. Skip the computation of np.log(v2) in this case to avoid invalid values. Fixes: RuntimeWarning: divide by zero encountered in log RuntimeWarning: invalid value encountered in multiply Signed-off-by: Jan Vesely --- .../components/functions/nonstateful/combinationfunctions.py | 3 ++- .../components/functions/nonstateful/objectivefunctions.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py index 35d955ee435..564dcc6a73d 100644 --- a/psyneulink/core/components/functions/nonstateful/combinationfunctions.py +++ b/psyneulink/core/components/functions/nonstateful/combinationfunctions.py @@ -1442,7 +1442,8 @@ def _function(self, elif operation == CROSS_ENTROPY: v1 = variable[0] v2 = variable[1] - combination = np.where(np.logical_and(v1 == 0, v2 == 0), 0.0, v1 * np.log(v2)) + both_zero = np.logical_and(v1 == 0, v2 == 0) + combination = v1 * np.where(both_zero, 0.0, np.log(v2, where=np.logical_not(both_zero))) else: raise FunctionError("Unrecognized operator ({0}) for LinearCombination function". format(operation.self.Operation.SUM)) diff --git a/psyneulink/core/components/functions/nonstateful/objectivefunctions.py b/psyneulink/core/components/functions/nonstateful/objectivefunctions.py index 190c0b764e4..bdb5d072c18 100644 --- a/psyneulink/core/components/functions/nonstateful/objectivefunctions.py +++ b/psyneulink/core/components/functions/nonstateful/objectivefunctions.py @@ -1207,7 +1207,8 @@ def _function(self, # MODIFIED CW 3/20/18: avoid divide by zero error by plugging in two zeros # FIX: unsure about desired behavior when v2 = 0 and v1 != 0 # JDC: returns [inf]; leave, and let it generate a warning or error message for user - result = -np.sum(np.where(np.logical_and(v1 == 0, v2 == 0), 0.0, v1 * np.log(v2))) + both_zero = np.logical_and(v1 == 0, v2 == 0) + result = -np.sum(v1 * np.where(both_zero, 0.0, np.log(v2, where=np.logical_not(both_zero)))) # Energy elif self.metric == ENERGY: From 0e960c1e051e7422a2de09ca6d453c876d7dff64 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Thu, 11 Jan 2024 18:45:32 -0500 Subject: [PATCH 11/14] Port/_parse_port_spec: Convert value to string to compare to keyword Otherwise they might end up compared as containers. Fixes: FutureWarning: elementwise comparison failed; ... Signed-off-by: Jan Vesely --- psyneulink/core/components/ports/port.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psyneulink/core/components/ports/port.py b/psyneulink/core/components/ports/port.py index dc797799d53..8f5b6db03fd 100644 --- a/psyneulink/core/components/ports/port.py +++ b/psyneulink/core/components/ports/port.py @@ -3002,7 +3002,7 @@ def _parse_port_spec(port_type=None, port_type_name = port_type.__name__ proj_is_feedback = False - if isinstance(port_specification, tuple) and port_specification[1] == FEEDBACK: + if isinstance(port_specification, tuple) and str(port_specification[1]) == FEEDBACK: port_specification = port_specification[0] proj_is_feedback = True From 2a3f89eef92c2004b76820d51e7e6e779edfc079 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 21 Jan 2024 14:08:59 -0500 Subject: [PATCH 12/14] treewide: Assign values to ".base" attribute of a modulated Parameter Fixes 3 instances of: FutureWarning: Setting parameter values directly using dot notation may be removed in a future release. Signed-off-by: Jan Vesely --- .../core/components/mechanisms/processing/transfermechanism.py | 2 +- tests/mechanisms/test_ddm_mechanism.py | 2 +- tests/mechanisms/test_mechanisms.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index 0298ff4f733..221eac63850 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -443,7 +443,7 @@ **noise** (it must be the same length as the Mechanism's `variable `), in which case each element is applied Hadamard (elementwise) to the result, as shown here:: - >>> my_linear_tm.noise = [1.0,1.2,.9] + >>> my_linear_tm.noise.base = [1.0,1.2,.9] >>> my_linear_tm.execute([1.0, 1.0, 1.0]) array([[2. , 2.2, 1.9]]) diff --git a/tests/mechanisms/test_ddm_mechanism.py b/tests/mechanisms/test_ddm_mechanism.py index c9e463a4bf2..e3f306b0f81 100644 --- a/tests/mechanisms/test_ddm_mechanism.py +++ b/tests/mechanisms/test_ddm_mechanism.py @@ -77,7 +77,7 @@ def test_valid(self): # reset only decision variable D.function.initializer = 1.0 - D.function.non_decision_time = 0.0 + D.function.non_decision_time.base = 0.0 D.reset() np.testing.assert_allclose(D.function.value[0], 1.0) np.testing.assert_allclose(D.function.parameters.previous_value.get(), 1.0) diff --git a/tests/mechanisms/test_mechanisms.py b/tests/mechanisms/test_mechanisms.py index fd403991f6e..2835140936f 100644 --- a/tests/mechanisms/test_mechanisms.py +++ b/tests/mechanisms/test_mechanisms.py @@ -50,7 +50,7 @@ def test_noise_assignment_equivalence(self, noise): t2 = pnl.TransferMechanism(name='t2', size=2) t2.integrator_function.parameters.noise.set(noise()) - t1.integrator_function.noise.seed = 0 + t1.integrator_function.noise.seed.base = 0 t2.integrator_function.noise.base.seed = 0 for _ in range(5): From 15fcba7fe1d27b82b6bedc0514acb80eeacd8d19 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 21 Jan 2024 16:13:17 -0500 Subject: [PATCH 13/14] tests/RecurrentTransferMechanism: Use matrix.modulated or get_mod_matrix() instead of mod_matrix The latter is deprecated. Signed-off-by: Jan Vesely --- tests/mechanisms/test_recurrent_transfer_mechanism.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/mechanisms/test_recurrent_transfer_mechanism.py b/tests/mechanisms/test_recurrent_transfer_mechanism.py index 9fc8a95c4b7..d4fa9ff75bd 100644 --- a/tests/mechanisms/test_recurrent_transfer_mechanism.py +++ b/tests/mechanisms/test_recurrent_transfer_mechanism.py @@ -820,7 +820,7 @@ def test_recurrent_mech_with_learning(self): ) # Test that all of these are the same: np.testing.assert_allclose( - R.recurrent_projection.mod_matrix, + R.recurrent_projection.matrix.modulated, [ [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], @@ -880,8 +880,8 @@ def test_recurrent_mech_change_learning_rate(self): [1.1, 0., 1.1, 1.1], [1.1, 1.1, 0., 1.1], [1.1, 1.1, 1.1, 0.]] - np.testing.assert_allclose(R.recurrent_projection.mod_matrix, matrix_1) - print(R.recurrent_projection.mod_matrix) + np.testing.assert_allclose(R.recurrent_projection.get_mod_matrix(c), matrix_1) + print(R.recurrent_projection.get_mod_matrix(c)) R.learning_rate.base = 0.9 assert R.learning_rate.base == 0.9 @@ -892,8 +892,8 @@ def test_recurrent_mech_change_learning_rate(self): [1.911125, 0., 1.911125, 1.911125], [1.911125, 1.911125, 0., 1.911125], [1.911125, 1.911125, 1.911125, 0.]] - # np.testing.assert_allclose(R.recurrent_projection.mod_matrix, matrix_2) - print(R.recurrent_projection.mod_matrix) + # np.testing.assert_allclose(R.recurrent_projection.get_mod_matrix(c), matrix_2) + print(R.recurrent_projection.get_mod_matrix(c)) def test_learning_of_orthognal_inputs(self): size=4 From 16c70ce85073b30266a2fb0ae9e6ff6411139bd2 Mon Sep 17 00:00:00 2001 From: Jan Vesely Date: Sun, 21 Jan 2024 16:14:20 -0500 Subject: [PATCH 14/14] setup: Error on SyntaxWarnings Signed-off-by: Jan Vesely --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 318879c8f93..141ba999200 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,6 +67,7 @@ required_plugins = pytest-benchmark pytest-cov pytest-helpers-namespace pytest-p xfail_strict = True filterwarnings = + error::SyntaxWarning error:Creating an ndarray from ragged nested sequences \(which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes\) is deprecated.*:numpy.VisibleDeprecationWarning error:Invalid escape sequence