diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 68740b70e58..ba2ba59d824 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -4,8 +4,9 @@
@@ -18,4 +19,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/AGT_Test_Script.xml b/.idea/runConfigurations/AGT_Test_Script.xml
index 78d22b8a0b4..e8d443a835e 100644
--- a/.idea/runConfigurations/AGT_Test_Script.xml
+++ b/.idea/runConfigurations/AGT_Test_Script.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/DDM_Learning_Test.xml b/.idea/runConfigurations/DDM_Learning_Test.xml
index 953e3a60d1f..1830c04f79a 100644
--- a/.idea/runConfigurations/DDM_Learning_Test.xml
+++ b/.idea/runConfigurations/DDM_Learning_Test.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/EVC_Gratton_Script___SEBASTIAN.xml b/.idea/runConfigurations/EVC_Gratton_Script___SEBASTIAN.xml
index fc05b0da0c2..d5ebe06c98c 100644
--- a/.idea/runConfigurations/EVC_Gratton_Script___SEBASTIAN.xml
+++ b/.idea/runConfigurations/EVC_Gratton_Script___SEBASTIAN.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_DEBUGGING.xml b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_DEBUGGING.xml
index f4ef35dca3a..068f6e70c3c 100644
--- a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_DEBUGGING.xml
+++ b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_DEBUGGING.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script__SPECIFY_ControlSignals__.xml b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script__SPECIFY_ControlSignals__.xml
index 60a334dc9c6..22c4216d7c7 100644
--- a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script__SPECIFY_ControlSignals__.xml
+++ b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script__SPECIFY_ControlSignals__.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_with_Custom_Fn.xml b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_with_Custom_Fn.xml
index eda21b9d569..29683d7ef3e 100644
--- a/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_with_Custom_Fn.xml
+++ b/.idea/runConfigurations/EVC_System_Laming_Validation_Test_Script_with_Custom_Fn.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/EVC_System_Test_Script_with_Profiler.xml b/.idea/runConfigurations/EVC_System_Test_Script_with_Profiler.xml
index 10159fb830b..a132b8e24fa 100644
--- a/.idea/runConfigurations/EVC_System_Test_Script_with_Profiler.xml
+++ b/.idea/runConfigurations/EVC_System_Test_Script_with_Profiler.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/FinalProject.xml b/.idea/runConfigurations/FinalProject.xml
index 359470a52bc..e14524de681 100644
--- a/.idea/runConfigurations/FinalProject.xml
+++ b/.idea/runConfigurations/FinalProject.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Gating_Mechanism_Test_Script.xml b/.idea/runConfigurations/Gating_Mechanism_Test_Script.xml
index 0a56fccb006..3b434d0b03e 100644
--- a/.idea/runConfigurations/Gating_Mechanism_Test_Script.xml
+++ b/.idea/runConfigurations/Gating_Mechanism_Test_Script.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/GilzenratModel.xml b/.idea/runConfigurations/GilzenratModel.xml
index 0a4febeba4c..551bfb71abb 100644
--- a/.idea/runConfigurations/GilzenratModel.xml
+++ b/.idea/runConfigurations/GilzenratModel.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/Make_HTML.xml b/.idea/runConfigurations/Make_HTML.xml
index 5bb9d2dc53a..273f929bfe6 100644
--- a/.idea/runConfigurations/Make_HTML.xml
+++ b/.idea/runConfigurations/Make_HTML.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/.idea/runConfigurations/Multilayer_Learning_Test_Script_VARIATIONS.xml b/.idea/runConfigurations/Multilayer_Learning_Test_Script_VARIATIONS.xml
index a3a9182bb4b..71e33c5b672 100644
--- a/.idea/runConfigurations/Multilayer_Learning_Test_Script_VARIATIONS.xml
+++ b/.idea/runConfigurations/Multilayer_Learning_Test_Script_VARIATIONS.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Multilayer_Learning_Test_Script__WITH_GATING_.xml b/.idea/runConfigurations/Multilayer_Learning_Test_Script__WITH_GATING_.xml
index 77d1c734a67..04e08d34a15 100644
--- a/.idea/runConfigurations/Multilayer_Learning_Test_Script__WITH_GATING_.xml
+++ b/.idea/runConfigurations/Multilayer_Learning_Test_Script__WITH_GATING_.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/NEW_Learning_Markus.xml b/.idea/runConfigurations/NEW_Learning_Markus.xml
index 86d88ccf993..d2e7bece107 100644
--- a/.idea/runConfigurations/NEW_Learning_Markus.xml
+++ b/.idea/runConfigurations/NEW_Learning_Markus.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/NetworkX_Examples.xml b/.idea/runConfigurations/NetworkX_Examples.xml
index 91210ca7522..df2774f8a56 100644
--- a/.idea/runConfigurations/NetworkX_Examples.xml
+++ b/.idea/runConfigurations/NetworkX_Examples.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/Profile_EVC_System_Test.xml b/.idea/runConfigurations/Profile_EVC_System_Test.xml
index 1016f59da8b..13365fb7733 100644
--- a/.idea/runConfigurations/Profile_EVC_System_Test.xml
+++ b/.idea/runConfigurations/Profile_EVC_System_Test.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/Stroop_Model_Learning_and_Control_Test_Script.xml b/.idea/runConfigurations/Stroop_Model_Learning_and_Control_Test_Script.xml
index f376af157cc..d110cc35e81 100644
--- a/.idea/runConfigurations/Stroop_Model_Learning_and_Control_Test_Script.xml
+++ b/.idea/runConfigurations/Stroop_Model_Learning_and_Control_Test_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Stroop_Model_Test_Script_VARIATIONS.xml b/.idea/runConfigurations/Stroop_Model_Test_Script_VARIATIONS.xml
index 41386758f1d..d3886cc032f 100644
--- a/.idea/runConfigurations/Stroop_Model_Test_Script_VARIATIONS.xml
+++ b/.idea/runConfigurations/Stroop_Model_Test_Script_VARIATIONS.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Two_Layer_Test_Script.xml b/.idea/runConfigurations/Two_Layer_Test_Script.xml
index 4fac001f902..e5dd04359a9 100644
--- a/.idea/runConfigurations/Two_Layer_Test_Script.xml
+++ b/.idea/runConfigurations/Two_Layer_Test_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Xor_Script_1_process_working.xml b/.idea/runConfigurations/Xor_Script_1_process_working.xml
index 462fab831a2..9c86080c6e5 100644
--- a/.idea/runConfigurations/Xor_Script_1_process_working.xml
+++ b/.idea/runConfigurations/Xor_Script_1_process_working.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/Xor_Script_2_processes.xml b/.idea/runConfigurations/Xor_Script_2_processes.xml
index 5d5d714b893..a4372c37cc6 100644
--- a/.idea/runConfigurations/Xor_Script_2_processes.xml
+++ b/.idea/runConfigurations/Xor_Script_2_processes.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_DDM_Test_Script.xml b/.idea/runConfigurations/_DDM_Test_Script.xml
index 7611a378b2e..a07730b20c8 100644
--- a/.idea/runConfigurations/_DDM_Test_Script.xml
+++ b/.idea/runConfigurations/_DDM_Test_Script.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/_DDM_scheduled_simple.xml b/.idea/runConfigurations/_DDM_scheduled_simple.xml
index 70a8845cd74..db8dfd99dc2 100644
--- a/.idea/runConfigurations/_DDM_scheduled_simple.xml
+++ b/.idea/runConfigurations/_DDM_scheduled_simple.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Documentation_Examples.xml b/.idea/runConfigurations/_Documentation_Examples.xml
index c50b0e61c51..021c739c153 100644
--- a/.idea/runConfigurations/_Documentation_Examples.xml
+++ b/.idea/runConfigurations/_Documentation_Examples.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/_EVC_System_DEMO_Script.xml b/.idea/runConfigurations/_EVC_System_DEMO_Script.xml
index b116789eb36..5d2f7244c7c 100644
--- a/.idea/runConfigurations/_EVC_System_DEMO_Script.xml
+++ b/.idea/runConfigurations/_EVC_System_DEMO_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_EVC_System_Laming_Validation_Test_Script.xml b/.idea/runConfigurations/_EVC_System_Laming_Validation_Test_Script.xml
index fcd9608fd0b..8f52a8aa544 100644
--- a/.idea/runConfigurations/_EVC_System_Laming_Validation_Test_Script.xml
+++ b/.idea/runConfigurations/_EVC_System_Laming_Validation_Test_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Integrator_scheduled_timescales.xml b/.idea/runConfigurations/_Integrator_scheduled_timescales.xml
index f8eb4f8b27f..e8bc21786ce 100644
--- a/.idea/runConfigurations/_Integrator_scheduled_timescales.xml
+++ b/.idea/runConfigurations/_Integrator_scheduled_timescales.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_META_TEST.xml b/.idea/runConfigurations/_META_TEST.xml
index ee5da1f19b9..bae196ffa6f 100644
--- a/.idea/runConfigurations/_META_TEST.xml
+++ b/.idea/runConfigurations/_META_TEST.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Mixed_NN___DDM_DEMO_Script.xml b/.idea/runConfigurations/_Mixed_NN___DDM_DEMO_Script.xml
index fd0b6ec987a..fcc62c27e0e 100644
--- a/.idea/runConfigurations/_Mixed_NN___DDM_DEMO_Script.xml
+++ b/.idea/runConfigurations/_Mixed_NN___DDM_DEMO_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Mixed_Neural_Network_and_DDM.xml b/.idea/runConfigurations/_Mixed_Neural_Network_and_DDM.xml
index f795122c488..e349065887c 100644
--- a/.idea/runConfigurations/_Mixed_Neural_Network_and_DDM.xml
+++ b/.idea/runConfigurations/_Mixed_Neural_Network_and_DDM.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/_Multilayer_Learning_Test_Script.xml b/.idea/runConfigurations/_Multilayer_Learning_Test_Script.xml
index d22a8ede09c..e6f3df4461a 100644
--- a/.idea/runConfigurations/_Multilayer_Learning_Test_Script.xml
+++ b/.idea/runConfigurations/_Multilayer_Learning_Test_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Multilayer_Learning_Test_Script_DEMO.xml b/.idea/runConfigurations/_Multilayer_Learning_Test_Script_DEMO.xml
index d01b747a1aa..7664f86ca9c 100644
--- a/.idea/runConfigurations/_Multilayer_Learning_Test_Script_DEMO.xml
+++ b/.idea/runConfigurations/_Multilayer_Learning_Test_Script_DEMO.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Reinforcement_Learning_Test_Script.xml b/.idea/runConfigurations/_Reinforcement_Learning_Test_Script.xml
index 430d9fd183d..3b5775ad30b 100644
--- a/.idea/runConfigurations/_Reinforcement_Learning_Test_Script.xml
+++ b/.idea/runConfigurations/_Reinforcement_Learning_Test_Script.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/_Scratch_Pad.xml b/.idea/runConfigurations/_Scratch_Pad.xml
index 13bfa32a9c8..cb1de20263c 100644
--- a/.idea/runConfigurations/_Scratch_Pad.xml
+++ b/.idea/runConfigurations/_Scratch_Pad.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/_Stroop_Demo_Script.xml b/.idea/runConfigurations/_Stroop_Demo_Script.xml
index b2017ea4204..69554f0ec22 100644
--- a/.idea/runConfigurations/_Stroop_Demo_Script.xml
+++ b/.idea/runConfigurations/_Stroop_Demo_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Stroop_Model_Learning_Test_Script.xml b/.idea/runConfigurations/_Stroop_Model_Learning_Test_Script.xml
index 669f716e256..6ad30dcbae8 100644
--- a/.idea/runConfigurations/_Stroop_Model_Learning_Test_Script.xml
+++ b/.idea/runConfigurations/_Stroop_Model_Learning_Test_Script.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_Stroop_Model_Test.xml b/.idea/runConfigurations/_Stroop_Model_Test.xml
index 86d586ed263..710857aec7b 100644
--- a/.idea/runConfigurations/_Stroop_Model_Test.xml
+++ b/.idea/runConfigurations/_Stroop_Model_Test.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_System_Graph_and_Input_Test.xml b/.idea/runConfigurations/_System_Graph_and_Input_Test.xml
index de08911f03b..060d51af7cc 100644
--- a/.idea/runConfigurations/_System_Graph_and_Input_Test.xml
+++ b/.idea/runConfigurations/_System_Graph_and_Input_Test.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/_System_Learning_Test_Script.xml b/.idea/runConfigurations/_System_Learning_Test_Script.xml
index ddfae0edc63..f53d89841bb 100644
--- a/.idea/runConfigurations/_System_Learning_Test_Script.xml
+++ b/.idea/runConfigurations/_System_Learning_Test_Script.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/make_html_and_ghpages_py.xml b/.idea/runConfigurations/make_html_and_ghpages_py.xml
index e7e43e27492..bef143ce0ac 100644
--- a/.idea/runConfigurations/make_html_and_ghpages_py.xml
+++ b/.idea/runConfigurations/make_html_and_ghpages_py.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations/py_test_for_tests_documentation_library_test_lib_mechanism_docs_test_lc_mechanism_docs.xml b/.idea/runConfigurations/py_test_for_tests_documentation_library_test_lib_mechanism_docs_test_lc_mechanism_docs.xml
new file mode 100644
index 00000000000..6d11c29fa19
--- /dev/null
+++ b/.idea/runConfigurations/py_test_for_tests_documentation_library_test_lib_mechanism_docs_test_lc_mechanism_docs.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/py_test_in_test_lib_mechanism_docs_py.xml b/.idea/runConfigurations/py_test_in_test_lib_mechanism_docs_py.xml
new file mode 100644
index 00000000000..6cafd894296
--- /dev/null
+++ b/.idea/runConfigurations/py_test_in_test_lib_mechanism_docs_py.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/test_system.xml b/.idea/runConfigurations/test_system.xml
index e1ee9ba5f1f..0442de18fd8 100644
--- a/.idea/runConfigurations/test_system.xml
+++ b/.idea/runConfigurations/test_system.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/.idea/runConfigurations/topo_example.xml b/.idea/runConfigurations/topo_example.xml
index dd4f4fcce34..98674565da0 100644
--- a/.idea/runConfigurations/topo_example.xml
+++ b/.idea/runConfigurations/topo_example.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/Scripts/Examples/Multitasking.py b/Scripts/Examples/Multitasking.py
new file mode 100644
index 00000000000..34a598b0133
--- /dev/null
+++ b/Scripts/Examples/Multitasking.py
@@ -0,0 +1,40 @@
+import psyneulink as pnl
+
+stimulus_layer = pnl.TransferMechanism(size=4)
+task_layer = pnl.TransferMechanism(size=4)
+hidden_layer = pnl.TransferMechanism(size=4, function=pnl.Logistic)
+output_layer = pnl.TransferMechanism(size=4, function=pnl.Logistic)
+
+network_process = pnl.Process(pathway=[stimulus_layer, hidden_layer, output_layer])
+hidden_control_process = pnl.Process(pathway=[task_layer, hidden_layer])
+output_control_process = pnl.Process(pathway=[task_layer, output_layer])
+
+multitasking_system = pnl.System(processes=[network_process, hidden_control_process, output_control_process])
+
+# WEIGHTS TO COME FROM SEBASTIAN
+
+example_stimulus_inputs = [[1,0,0,1],[1,0,1,0]]
+example_task_inputs = [[0,0,0,1],[1,0,0,0]]
+example_training_pattern = [[0,0,0,1],[1,0,0,0]]
+
+# RUN THIS TO GET SPACE OF INPUTS ON WHICH TO OPTIMIZE LCA PARAMS:
+inputs_to_LCA = multitasking_system.run(inputs={stimulus_layer:example_stimulus_inputs,
+ task_layer:example_task_inputs})
+
+# SOME PYTHON ALGORITHM HERE THAT SELECTS THE 2-UNIT SUBVECTOR FROM inputs_to_LCA CORRESPONDING TO THE RELEVANT TASK
+# AS INPUT TO optimization_system BELOW, AND THEN RUN THE SYSTEM FOR EACH INPUT, USING EVC TO OPTIMIZE LCA PARAMETERS
+# FOR EACH, BASED CONTROL PARAMETERS AND OBJECTIVE FUNCTION
+
+input_layer = pnl.TransferMechanism(size=2)
+decision_layer = pnl.LCA(size=2,
+ # INCLUDE TERMINATION CONDITION USING THREHSOLD = ControlSignal)
+ )
+decision_process = pnl.Process(input_layer, decision_layer)
+optimization_system = pnl.System(processes=decision_process,
+ monitor_for_control=[decision_layer.output_states[pnl.RESULT]])
+# ADD COMPARATOR MECHANISM FOR ACCURACY DETERMINATION
+
+
+# EVC PARAMS:
+# - number of simulations to run per LCA
+# - which inputs to provide (i.e., *NOT* using typical PredictionMechanisms)
diff --git a/Scripts/Scratch Pad.py b/Scripts/Scratch Pad.py
index 28a50d30e80..280b34c5ccc 100644
--- a/Scripts/Scratch Pad.py
+++ b/Scripts/Scratch Pad.py
@@ -716,38 +716,222 @@ def __init__(self, error_value):
#endregion
-#region TEST InputState
-print("TEST InputState")
-# T1 = pnl.TransferMechanism(name='T1', default_variable=[0,0,0])
-# # G = pnl.GatingMechanism(gating_signals=[([pnl.DECISION_VARIABLE, pnl.RESPONSE_TIME], T1)])
-# T2 = pnl.TransferMechanism(input_states=[(pnl.RESULT, RESULT-1, T1)])
-# # assert T2.input_states[0].path_afferents[0].sender.owner.name == 'D'
-# assert True
+#region TEST MODULATORY SPECS
+# print ("TEST MODULATORY SPECS")
+#
+# # # ADD TO TEST:
+# # import psyneulink as pnl
+# # my_mechanism = pnl.RecurrentTransferMechanism(size=5,
+# # # noise=pnl.CONTROL,
+# # # noise=pnl.CONTROL_SIGNAL,
+# # # noise=pnl.ControlSignal,
+# # # noise=pnl.ControlSignal(),
+# # # noise=(1, pnl.CONTROL),
+# # # noise=(1, pnl.CONTROL_SIGNAL),
+# # # noise=(1, pnl.ControlSignal),
+# # # noise=(1, pnl.ControlSignal()),
+# # # noise=(1, pnl.ControlProjection),
+# # noise=(0.3, pnl.ControlProjection()),
+# # # noise=(1, pnl.ControlMechanism), # <- FIX: DOESN'T WORK
+# # # noise=(1, pnl.ControlMechanism()), # <- FIX: DOESN'T WORK
+# # function=pnl.Logistic(
+# # # gain=pnl.CONTROL,
+# # # gain=pnl.CONTROL_SIGNAL,
+# # # gain=pnl.ControlSignal,
+# # # gain=pnl.ControlSignal(),
+# # # gain=(0.5, pnl.CONTROL),
+# # # gain=(0.5, pnl.CONTROL_SIGNAL),
+# # # gain=(0.5, pnl.ControlSignal),
+# # # gain=(0.5, pnl.ControlSignal()),
+# # # gain=(0.5, pnl.ControlProjection),
+# # gain=(0.5, pnl.ControlProjection()),
+# # # gain=(0.5, pnl.ControlMechanism), # <- FIX: DOESN'T WORK
+# # # gain=(0.5, pnl.ControlMechanism()), # <- FIX: DOESN'T WORK
+# # bias=(1.0, pnl.ControlSignal(modulation=pnl.ModulationParam.ADDITIVE)))
+# # )
+# # print ('MOD_AFFERENTS: ', my_mechanism.parameter_states[pnl.NOISE].mod_afferents)
+# # print ('MOD_AFFERENTS: ', my_mechanism.parameter_states[pnl.GAIN].mod_afferents)
+# # # print ('MOD_AFFERENTS: ', my_mechanism.parameter_states[pnl.BIAS].mod_afferents)
+#
+# import psyneulink as pnl
+#
+# # control_spec_list = [
+# # pnl.CONTROL,
+# # pnl.CONTROL_SIGNAL,
+# # pnl.ControlSignal,
+# # pnl.ControlSignal(),
+# # (0.3, pnl.CONTROL),
+# # (0.3, pnl.CONTROL_SIGNAL),
+# # (0.3, pnl.ControlSignal),
+# # (0.3, pnl.ControlSignal()),
+# # (0.3, pnl.ControlProjection),
+# # (0.3, pnl.ControlProjection())
+# # ]
+#
+#
+# # for i, ctl_tuple in enumerate([i for i in zip(control_spec_list, reversed(control_spec_list))]):
+# # c1, c2 = ctl_tuple
+# # m = pnl.RecurrentTransferMechanism(noise=c1,
+# # function=pnl.Logistic(gain=c2))
+# # assert m.parameter_states[pnl.NOISE].mod_afferents[0].name in \
+# # 'ControlProjection for RecurrentTransferMechanism-{}[noise]'.format(i)
+# # assert m.parameter_states[pnl.GAIN].mod_afferents[0].name in \
+# # 'ControlProjection for RecurrentTransferMechanism-{}[gain]'.format(i)
+#
+# # for c1, c2 in ctl_signals:
+# # print("-------\n{}\n{}".format(c1, c2))
+# # print("--------------\n--------------")
+#
+# # for c1, c2 in ctl_signals:
+# # m = pnl.RecurrentTransferMechanism(noise=c1,
+# # function=pnl.Logistic(gain=c2))
+# # print('-------------\nSIGNAL: {}\nMOD_AFFERENTS: {}\nSIGNAL: {}\nMOD_AFFERENTS: {}'
+# # .format(c2, m.parameter_states[pnl.NOISE].mod_afferents,
+# # c1, m.parameter_states[pnl.GAIN].mod_afferents))
+#
+#
+# gating_spec_list = [
+# pnl.GATING,
+# pnl.GATING_SIGNAL,
+# pnl.GatingSignal,
+# pnl.GatingSignal(),
+# (0.3, pnl.GATING),
+# (0.3, pnl.GATING_SIGNAL),
+# (0.3, pnl.GatingSignal),
+# (0.3, pnl.GatingSignal()),
+# (0.3, pnl.GatingProjection),
+# (0.3, pnl.GatingProjection())
+# ]
+#
+# T = pnl.TransferMechanism(output_states=[
+# # pnl.GATING, # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # pnl.GATING_SIGNAL, # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # pnl.GatingSignal, # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# pnl.GatingSignal(), # <- FIX DOESN"T WORK FOR INPUT_STATES BUT DOES FOR OUTPUT_STATES
+# # (0.3, pnl.GATING), # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # (0.3, pnl.GATING_SIGNAL), # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # (0.3, pnl.GatingSignal), # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # (0.3, pnl.GatingSignal()),
+# # (0.3, pnl.GatingProjection), # FIX: DOESN"T WORK FOR OUTPUT_STATES
+# # (0.3, pnl.GatingProjection())
+# ])
+#
+#
+# # for i, gating_tuple in enumerate([i for i in zip(gating_spec_list, reversed(gating_spec_list))]):
+# # G1, G2 = gating_tuple
+# # T = pnl.TransferMechanism(input_states=[G1],
+# # output_states=[G2])
+# # assert T.input_states[0].mod_afferents[0].name in \
+# # 'GatingProjection for TransferMechanism-0[InputState-{}]'.format(i)
+# #
+# # assert T.output_states[0].mod_afferents[0].name in \
+# # 'GatingProjection for TransferMechanism-0[OutputState-{}]'.format(i)
+#
+#endregion
+#region TEST DOCUMENTATION
+print ("TEST DOCUMENTATION")
+
+# # import matlab.engine
+# # eng1 = matlab.engine.start_matlab('-nojvm')
+# my_DDM_NavarroAndFuss = pnl.DDM(function=pnl.NavarroAndFuss(drift_rate=3.0,
+# starting_point=1.0,
+# threshold=30.0,
+# noise=1.5,
+# t0 = 2.0),
+# name='my_DDM_NavarroAndFuss')
-# I = pnl.InputState(name='I', owner=T1)
-# # I = pnl.InputState(name='I')
-# # FIX: IMPLEMENT ASSIGNMENT TO OWNER WITH ASSIGNMENT OF OWNER
-# # I.owner = T1
-# assert T1.input_states[1].name == 'I'
+import psyneulink as pnl
+
+A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+C = pnl.TransferMechanism(function=pnl.Linear(), name='C')
+
+p = pnl.Process(
+ pathway=[A, B, C],
+ name = 'p'
+)
+s = pnl.System(
+ processes=[p],
+ name='s'
+)
+my_scheduler = pnl.Scheduler(system=s)
+
+# implicit condition of Always for A
+my_scheduler.add_condition(B, pnl.scheduling.condition.EveryNCalls(A, 2))
+my_scheduler.add_condition(C, pnl.scheduling.condition.EveryNCalls(B, 3))
+
+# implicit AllHaveRun Termination condition
+execution_sequence = list(my_scheduler.run())
+execution_sequence
+
+A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+
+p = pnl.Process(
+ pathway=[A, B],
+ name = 'p'
+)
+s = pnl.System(
+ processes=[p],
+ name='s'
+)
+my_scheduler = pnl.Scheduler(system=s)
+
+my_scheduler.add_condition(A,
+ pnl.scheduling.condition.Any(pnl.scheduling.condition.AtPass(0),
+ pnl.scheduling.condition.EveryNCalls(B, 2)))
+my_scheduler.add_condition(B,
+ pnl.scheduling.condition.Any(pnl.scheduling.condition.EveryNCalls(A, 1),
+ pnl.scheduling.condition.EveryNCalls(B, 1)))
+
+termination_conds = {ts: None for ts in pnl.TimeScale}
+termination_conds[pnl.TimeScale.TRIAL] = pnl.scheduling.condition.AfterNCalls(B,
+ 4,
+ time_scale=pnl.TimeScale.TRIAL)
+# CRASHING on line 473 of scheduler (self.termination_conds[ts] is None)
+execution_sequence = list(my_scheduler.run(termination_conds=termination_conds))
+
+A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+C = pnl.TransferMechanism(function=pnl.Linear(), name='C')
+
+p = pnl.Process(
+ pathway=[A, C],
+ name = 'p'
+)
+q = pnl.Process(
+ pathway=[B, C],
+ name = 'q'
+)
+s = pnl.System(
+ processes=[p, q],
+ name='s'
+)
+my_scheduler = pnl.Scheduler(system=s)
+
+my_scheduler.add_condition(A, pnl.scheduling.condition.EveryNPasses(1))
+my_scheduler.add_condition(B, pnl.scheduling.condition.EveryNCalls(A, 2))
+my_scheduler.add_condition(C,
+ pnl.scheduling.condition.Any(pnl.scheduling.condition.AfterNCalls(A, 3),
+ pnl.scheduling.condition.AfterNCalls(B, 3)))
+
+termination_conds = {ts: None for ts in pnl.TimeScale}
+termination_conds[pnl.TimeScale.TRIAL] = pnl.scheduling.condition.AfterNCalls(C,
+ 4,
+ time_scale=pnl.TimeScale.TRIAL)
+execution_sequence = list(my_scheduler.run(termination_conds=termination_conds))
-G = pnl.GatingMechanism(gating_signals=['a','b'])
-T = pnl.TransferMechanism(name='T',
- input_states=(3,G),
- output_states=(2,G.gating_signals['b']))
-assert T.input_states[0].mod_afferents[0].sender==G.gating_signals[0]
-assert T.output_states[0].mod_afferents[0].sender==G.gating_signals[1]
-assert True
#endregion
#region TEST INPUT FORMATS
-
+# print ("TEST INPUT FORMATS")
#
# x = TransferMechanism([0,0,0],
# name='x')
diff --git a/tests/conftest.py b/conftest.py
similarity index 94%
rename from tests/conftest.py
rename to conftest.py
index 3791a96cacc..5317a9d046f 100644
--- a/tests/conftest.py
+++ b/conftest.py
@@ -57,3 +57,7 @@ def expand_np_ndarray(arr):
nested_elem = [nested_elem]
results_list.extend(nested_elem)
return results_list
+
+def pytest_runtest_setup():
+ import doctest
+ doctest.ELLIPSIS_MARKER = "[...]"
\ No newline at end of file
diff --git a/docs/source/LCMechanism.rst b/docs/source/LCMechanism.rst
index 24bc02f0ecf..dab5365c9c1 100644
--- a/docs/source/LCMechanism.rst
+++ b/docs/source/LCMechanism.rst
@@ -1,6 +1,6 @@
LCMechanism
===========
-.. automodule:: psyneulink.library.mechanisms.adaptivemechanisms.controlmechanisms.lcmechanism
+.. automodule:: psyneulink.library.mechanisms.adaptive.control.lcmechanism
:members:
:exclude-members: random, LinearCombination, Linear
diff --git a/psyneulink/components/component.py b/psyneulink/components/component.py
index bdb68225b23..3b77fc4f344 100644
--- a/psyneulink/components/component.py
+++ b/psyneulink/components/component.py
@@ -351,7 +351,7 @@ class `UserList ` is multiplied before applying the
`bias ` (if it is specified).
- bias : float
+ bias : float : default 0.0
value added to each element of `variable ` after applying the `gain `
(if it is specified).
@@ -8001,6 +8001,9 @@ def _validate_params(self, request_set, target_set=None, context=None):
matrix = target_set[MATRIX]
+ if isinstance(matrix, str):
+ matrix = get_matrix(matrix)
+
if isinstance(matrix, MappingProjection):
try:
matrix = matrix._parameter_states[MATRIX].value
@@ -8029,7 +8032,11 @@ def _validate_params(self, request_set, target_set=None, context=None):
format(param_type_string, MATRIX, self.name, matrix))
rows = matrix.shape[0]
cols = matrix.shape[1]
- size = len(np.squeeze(self.instance_defaults.variable))
+ # MODIFIED 11/25/17 OLD:
+ # size = len(np.squeeze(self.instance_defaults.variable))
+ # MODIFIED 11/25/17 NEW:
+ size = len(self.instance_defaults.variable)
+ # MODIFIED 11/25/17 END
if rows != size:
raise FunctionError("The value of the {} specified for the {} arg of {} is the wrong size;"
@@ -8052,7 +8059,11 @@ def _instantiate_attributes_before_function(self, context=None):
"""
- size = len(np.squeeze(self.instance_defaults.variable))
+ # MODIFIED 11/25/17 OLD:
+ # size = len(np.squeeze(self.instance_defaults.variable))
+ # MODIFIED 11/25/17 NEW:
+ size = len(self.instance_defaults.variable)
+ # MODIFIED 11/25/17 END
from psyneulink.components.projections.pathway.mappingprojection import MappingProjection
from psyneulink.components.states.parameterstate import ParameterState
diff --git a/psyneulink/components/mechanisms/adaptive/control/controlmechanism.py b/psyneulink/components/mechanisms/adaptive/control/controlmechanism.py
index 5a4c7b611b7..4cd77cc17d4 100644
--- a/psyneulink/components/mechanisms/adaptive/control/controlmechanism.py
+++ b/psyneulink/components/mechanisms/adaptive/control/controlmechanism.py
@@ -239,20 +239,25 @@
The following example creates a ControlMechanism by specifying its **objective_mechanism** using a constructor
that specifies the OutputStates to be monitored by its `objective_mechanism `::
- my_transfer_mech_A = TransferMechanism()
- my_DDM = DDM()
- my_transfer_mech_B = TransferMechanism(function=Logistic)
+ >>> import psyneulink as pnl
+ >>> my_transfer_mech_A = pnl.TransferMechanism(name="Transfer Mech A")
+ >>> my_DDM = pnl.DDM(name="My DDM")
+ >>> my_transfer_mech_B = pnl.TransferMechanism(function=pnl.Logistic,
+ ... name="Transfer Mech B")
+
+ >>> my_control_mech = pnl.ControlMechanism(
+ ... objective_mechanism=pnl.ObjectiveMechanism(monitored_output_states=[(my_transfer_mech_A, 2, 1),
+ ... my_DDM.output_states[pnl.RESPONSE_TIME]],
+ ... name="Objective Mechanism"),
+ ... function=pnl.LinearCombination(operation=pnl.PRODUCT),
+ ... control_signals=[(pnl.THRESHOLD, my_DDM),
+ ... (pnl.GAIN, my_transfer_mech_B)],
+ ... name="My Control Mech")
- my_control_mech = ControlMechanism(
- objective_mechanism=ObjectiveMechanism(monitored_output_states=[(my_transfer_mech_A, 2, 1),
- my_DDM.output_states[RESPONSE_TIME]],
- function=LinearCombination(operation=PRODUCT)),
- control_signals=[(THRESHOLD, my_DDM),
- (GAIN, my_transfer_mech_B)])
This creates an ObjectiveMechanism for the ControlMechanism that monitors the `primary OutputState
` of ``my_Transfer_mech_A`` and the *RESPONSE_TIME* OutputState of ``my_DDM``; its function
-first multiplies the former by 2 before, then takes product of ther values and passes the result as the input to the
+first multiplies the former by 2 before, then takes product of their values and passes the result as the input to the
ControlMechanism. The ControlMechanism's `function ` uses this value to determine
the allocation for its ControlSignals, that control the value of the `threshold ` parameter of
``my_DDM`` and the `gain ` parameter of the `Logistic` Function for ``my_transfer_mech_B``.
@@ -260,25 +265,26 @@
The following example specifies the same set of OutputStates for the ObjectiveMechanism, by assigning them directly
to the **objective_mechanism** argument::
- my_control_mech = ControlMechanism(
- objective_mechanism=[(my_transfer_mech_A, 2, 1),
- my_DDM.output_states[RESPONSE_TIME]],
- control_signals=[(THRESHOLD, my_DDM),
- (GAIN, my_transfer_mech_B)])
+ >>> my_control_mech = pnl.ControlMechanism(
+ ... objective_mechanism=[(my_transfer_mech_A, 2, 1),
+ ... my_DDM.output_states[pnl.RESPONSE_TIME]],
+ ... control_signals=[(pnl.THRESHOLD, my_DDM),
+ ... (pnl.GAIN, my_transfer_mech_B)])
+ ...
Note that, while this form is more succinct, it precludes specifying the ObjectiveMechanism's function. Therefore,
the values of the monitored OutputStates will be added (the default) rather than multiplied.
The ObjectiveMechanism can also be created on its own, and then referenced in the constructor for the ControlMechanism::
- my_obj_mech=ObjectiveMechanism(monitored_output_states=[(my_transfer_mech_A, 2, 1),
- my_DDM.output_states[RESPONSE_TIME]],
- function=LinearCombination(operation=PRODUCT))
+ >>> my_obj_mech = pnl.ObjectiveMechanism(monitored_output_states=[(my_transfer_mech_A, 2, 1),
+ ... my_DDM.output_states[pnl.RESPONSE_TIME]],
+ ... function=pnl.LinearCombination(operation=pnl.PRODUCT))
- my_control_mech = ControlMechanism(
- objective_mechanism=my_obj_mech,
- control_signals=[(THRESHOLD, my_DDM),
- (GAIN, my_transfer_mech_B)])
+ >>> my_control_mech = pnl.ControlMechanism(
+ ... objective_mechanism=my_obj_mech,
+ ... control_signals=[(pnl.THRESHOLD, my_DDM),
+ ... (pnl.GAIN, my_transfer_mech_B)])
Here, as in the first example, the constructor for the ObjectiveMechanism can be used to specify its function, as well
as the OutputState that it monitors.
@@ -308,8 +314,9 @@
from psyneulink.components.shellclasses import System_Base
from psyneulink.globals.defaults import defaultControlAllocation
from psyneulink.globals.keywords import \
- AUTO_ASSIGN_MATRIX, CONTROL, CONTROL_PROJECTIONS, CONTROL_SIGNALS, EXPONENT, INIT__EXECUTE__METHOD_ONLY, \
- NAME, OBJECTIVE_MECHANISM, PRODUCT, PROJECTIONS, SYSTEM, VARIABLE, WEIGHT
+ AUTO_ASSIGN_MATRIX, INIT__EXECUTE__METHOD_ONLY, \
+ CONTROL, CONTROL_PROJECTION, CONTROL_PROJECTIONS, CONTROL_SIGNAL, CONTROL_SIGNALS, \
+ NAME, OBJECTIVE_MECHANISM, PRODUCT, PROJECTIONS, SYSTEM, VARIABLE, WEIGHT, EXPONENT
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.globals.utilities import ContentAddressableList
@@ -323,6 +330,21 @@
ControlMechanismRegistry = {}
+
+def _is_control_spec(spec):
+ from psyneulink.components.projections.modulatory.controlprojection import ControlProjection
+ if isinstance(spec, tuple):
+ return _is_control_spec(spec[1])
+ elif isinstance(spec, (ControlMechanism, ControlSignal, ControlProjection)):
+ return True
+ elif isinstance(spec, type) and issubclass(spec, ControlSignal):
+ return True
+ elif isinstance(spec, str) and spec in {CONTROL, CONTROL_PROJECTION, CONTROL_SIGNAL}:
+ return True
+ else:
+ return False
+
+
class ControlMechanismError(Exception):
def __init__(self, error_value):
self.error_value = error_value
@@ -487,6 +509,9 @@ class ControlMechanism(AdaptiveMechanism_Base):
initMethod = INIT__EXECUTE__METHOD_ONLY
+ state_list_attr = Mechanism_Base.state_list_attr.copy()
+ state_list_attr.update({ControlSignal:CONTROL_SIGNALS})
+
classPreferenceLevel = PreferenceLevel.TYPE
# Any preferences specified below will override those specified in TypeDefaultPreferences
# Note: only need to specify setting; level will be assigned to TYPE automatically
@@ -555,8 +580,8 @@ def _validate_params(self, request_set, target_set=None, context=None):
from psyneulink.components.states.state import _parse_state_spec
super(ControlMechanism, self)._validate_params(request_set=request_set,
- target_set=target_set,
- context=context)
+ target_set=target_set,
+ context=context)
if SYSTEM in target_set:
if not isinstance(target_set[SYSTEM], System_Base):
raise KeyError
@@ -622,7 +647,8 @@ def _validate_params(self, request_set, target_set=None, context=None):
for control_signal in target_set[CONTROL_SIGNALS]:
_parse_state_spec(state_type=ControlSignal, owner=self, state_spec=control_signal)
- # IMPLEMENTATION NOTE: THIS SHOULD BE MOVED TO COMPOSITION ONCE THAT IS IMPLEMENTED
+ # IMPLEMENTATION NOTE: THIS SHOULD BE MOVED TO COMPOSITION
+ # ONCE THAT IS IMPLEMENTED
def _instantiate_objective_mechanism(self, context=None):
"""
Assign InputState to ControlMechanism for each OutputState to be monitored;
@@ -692,23 +718,25 @@ def _instantiate_objective_mechanism(self, context=None):
self._objective_mechanism = ObjectiveMechanism(monitored_output_states=monitored_output_states,
function=LinearCombination(operation=PRODUCT),
name=self.name + '_ObjectiveMechanism')
+
except ObjectiveMechanismError as e:
raise ObjectiveMechanismError(e)
# Print monitored_output_states
if self.prefs.verbosePref:
- print ("{0} monitoring:".format(self.name))
+ print("{0} monitoring:".format(self.name))
for state in self.monitored_output_states:
weight = self.monitored_output_states_weights_and_exponents[
self.monitored_output_states.index(state)][WEIGHT_INDEX]
exponent = self.monitored_output_states_weights_and_exponents[
self.monitored_output_states.index(state)][EXPONENT_INDEX]
- print ("\t{0} (exp: {1}; wt: {2})".format(state.name, weight, exponent))
+ print("\t{0} (exp: {1}; wt: {2})".format(state.name, weight, exponent))
# Assign ObjectiveMechanism's role as CONTROL
self.objective_mechanism._role = CONTROL
- # If ControlMechanism is a System controller, name Projection from ObjectiveMechanism based on the System
+ # If ControlMechanism is a System controller, name Projection from
+ # ObjectiveMechanism based on the System
if self.system is not None:
name = self.system.name + ' outcome signal'
# Otherwise, name it based on the ObjectiveMechanism
@@ -723,7 +751,8 @@ def _instantiate_objective_mechanism(self, context=None):
def _instantiate_input_states(self, context=None):
super()._instantiate_input_states(context=context)
- # IMPLEMENTATION NOTE: THIS SHOULD BE MOVED TO COMPOSITION ONCE THAT IS IMPLEMENTED
+ # IMPLEMENTATION NOTE: THIS SHOULD BE MOVED TO COMPOSITION
+ # ONCE THAT IS IMPLEMENTED
self._instantiate_objective_mechanism(context=context)
def _instantiate_output_states(self, context=None):
@@ -753,7 +782,6 @@ def _instantiate_output_states(self, context=None):
for control_signal in self.control_signals:
self._instantiate_control_signal(control_signal, context=context)
-
super()._instantiate_output_states(context=context)
# Reassign control_signals to capture any user_defined ControlSignals instantiated by in call to super
@@ -841,11 +869,11 @@ def _instantiate_control_signal(self, control_signal, context=None):
def _execute(self,
- variable=None,
- runtime_params=None,
- clock=CentralClock,
- time_scale=TimeScale.TRIAL,
- context=None):
+ variable=None,
+ runtime_params=None,
+ clock=CentralClock,
+ time_scale=TimeScale.TRIAL,
+ context=None):
"""Updates ControlSignals based on inputs
Must be overriden by subclass
@@ -859,9 +887,9 @@ def show(self):
`.
"""
- print ("\n---------------------------------------------------------")
+ print("\n---------------------------------------------------------")
- print ("\n{0}".format(self.name))
+ print("\n{0}".format(self.name))
print("\n\tMonitoring the following Mechanism OutputStates:")
for state in self.objective_mechanism.input_states:
for projection in state.path_afferents:
@@ -972,14 +1000,33 @@ def assign_as_controller(self, system:System_Base, context=None):
# Flag ObjectiveMechanism as associated with a ControlMechanism that is a controller for the System
self._objective_mechanism.controller = True
-
@property
def monitored_output_states(self):
- return self.objective_mechanism.monitored_output_states
+ try:
+ return self._objective_mechanism.monitored_output_states
+ except AttributeError:
+ return None
+
+ @monitored_output_states.setter
+ def monitored_output_states(self, value):
+ try:
+ self._objective_mechanism._monitored_output_states = value
+ except AttributeError:
+ # # MODIFIED 11/25/17 OLD:
+ # raise ControlMechanismError("Control Mechanism {}'s Objective "
+ # "Mechanism has not been "
+ # "instantiated.".format(self.name))
+ # MODIFIED 11/25/17 NEW:
+ return None
+
+ # # MODIFIED 11/25/17 NEWER:
+ # self._instantiate_objective_mechanism(context='INSTANTIATE_OBJECTIVE_MECHANISM')
+ # return self.monitored_output_states
+ # MODIFIED 11/25/17 END:
@property
def monitored_output_states_weights_and_exponents(self):
- return self.objective_mechanism.monitored_output_states_weights_and_exponents
+ return self._objective_mechanism.monitored_output_states_weights_and_exponents
diff --git a/psyneulink/components/mechanisms/adaptive/gating/gatingmechanism.py b/psyneulink/components/mechanisms/adaptive/gating/gatingmechanism.py
index a624b91a517..153737dd75f 100644
--- a/psyneulink/components/mechanisms/adaptive/gating/gatingmechanism.py
+++ b/psyneulink/components/mechanisms/adaptive/gating/gatingmechanism.py
@@ -182,6 +182,16 @@
GatingMechanismRegistry = {}
+def _is_gating_spec(spec):
+ from psyneulink.components.projections.modulatory.gatingprojection import GatingProjection
+ if isinstance(spec, tuple):
+ return _is_gating_spec(spec[1])
+ elif isinstance(spec, (GatingMechanism, GatingSignal, GatingProjection)):
+ return True
+ else:
+ return False
+
+
class GatingMechanismError(Exception):
def __init__(self, error_value):
self.error_value = error_value
@@ -321,6 +331,10 @@ class GatingMechanism(AdaptiveMechanism_Base):
output_state_type = GatingSignal
+ state_list_attr = Mechanism_Base.state_list_attr.copy()
+ state_list_attr.update({GatingSignal:GATING_SIGNALS})
+
+
classPreferenceLevel = PreferenceLevel.TYPE
# Any preferences specified below will override those specified in TypeDefaultPreferences
# Note: only need to specify setting; level will be assigned to TYPE automatically
diff --git a/psyneulink/components/mechanisms/adaptive/learning/learningmechanism.py b/psyneulink/components/mechanisms/adaptive/learning/learningmechanism.py
index 2426d57f5ba..2379181ca2a 100644
--- a/psyneulink/components/mechanisms/adaptive/learning/learningmechanism.py
+++ b/psyneulink/components/mechanisms/adaptive/learning/learningmechanism.py
@@ -138,8 +138,8 @@
.. _LearningMechanism_Input_Error_Signal:
-* *ERROR_SIGNAL* - receives the `value from the *OUTCOME* `OutputState
- of a `ComparatorMechanism` or the *ERROR_SIGNAL* OutputState of another
+* *ERROR_SIGNAL* - receives the `value ` from the *OUTCOME* `OutputState
+ ` of a `ComparatorMechanism` or the *ERROR_SIGNAL* OutputState of another
`LearningMechanism `. If the
`primary_learned_projection` projects to the `TERMINAL` Mechanism of the Process or System being learned,
or is not part of a `multilayer learning sequence `,
@@ -520,7 +520,9 @@
import typecheck as tc
from psyneulink.components.component import InitStatus, parameter_keywords
-from psyneulink.components.functions.function import BackPropagation, ModulationParam, _is_modulation_param, is_function_type
+from psyneulink.components.functions.function import BackPropagation, ModulationParam, _is_modulation_param, \
+ is_function_type
+from psyneulink.components.mechanisms.mechanism import Mechanism_Base
from psyneulink.components.mechanisms.adaptive.adaptivemechanism import AdaptiveMechanism_Base
from psyneulink.components.mechanisms.processing.objectivemechanism import OUTCOME, ObjectiveMechanism
from psyneulink.components.shellclasses import Mechanism
@@ -543,7 +545,8 @@
parameter_keywords.update({LEARNING_PROJECTION, LEARNING})
-def _is_learning_spec(spec):
+
+def _is_learning_spec(spec, include_matrix_spec=True):
"""Evaluate whether spec is a valid learning specification
Return `True` if spec is LEARNING or a valid projection_spec (see Projection_Base._is_projection_spec)
@@ -552,10 +555,14 @@ def _is_learning_spec(spec):
"""
from psyneulink.components.projections.projection import _is_projection_spec
- if spec in {LEARNING, ENABLED}:
- return True
- else:
- return _is_projection_spec(spec)
+ try:
+ if spec in {LEARNING, ENABLED}:
+ return True
+ else:
+ return _is_projection_spec(spec=spec,
+ include_matrix_spec=include_matrix_spec)
+ except:
+ return False
# Used to index variable:
@@ -819,6 +826,9 @@ class LearningMechanism(AdaptiveMechanism_Base):
output_state_type = LearningSignal
+ state_list_attr = Mechanism_Base.state_list_attr.copy()
+ state_list_attr.update({LearningSignal:LEARNING_SIGNALS})
+
classPreferenceLevel = PreferenceLevel.TYPE
# ClassDefaults.variable = None
diff --git a/psyneulink/components/mechanisms/mechanism.py b/psyneulink/components/mechanisms/mechanism.py
index 9c781f9e471..ee151f2bd7f 100644
--- a/psyneulink/components/mechanisms/mechanism.py
+++ b/psyneulink/components/mechanisms/mechanism.py
@@ -163,15 +163,17 @@
The following example creates an instance of a TransferMechanism that names the default InputState ``MY_INPUT``,
and assigns three `Standard OutputStates `::
- my_mech = TransferMechanism(input_states=['MY_INPUT'],
- output_states=[RESULT, MEAN, VARIANCE])
+ >>> import psyneulink as pnl
+ >>> my_mech = pnl.TransferMechanism(input_states=['MY_INPUT'],
+ ... output_states=[pnl.RESULT, pnl.MEAN, pnl.VARIANCE])
+
.. _Mechanism_Example_2:
This shows how the same Mechanism can be specified using a dictionary assigned to the **params** argument::
- my_mech = TransferMechanism(params={INPUT_STATES: ['MY_INPUT'],
- OUTPUT_STATES: [RESULT, MEAN, VARIANCE]})
+ >>> my_mech = pnl.TransferMechanism(params={pnl.INPUT_STATES: ['MY_INPUT'],
+ ... pnl.OUTPUT_STATES: [pnl.RESULT, pnl.MEAN, pnl.VARIANCE]})
See `State ` for additional examples of specifying the States of a Mechanism.
@@ -208,7 +210,7 @@
parameters). For example, the `function ` of a `TransferMechanism`, which is `Linear` by
default, can be specified to be the `Logistic` function as follows::
- my_mechanism = TransferMechanism(function=Logistic(gain=1.0, bias=-4))
+ >>> my_mechanism = pnl.TransferMechanism(function=pnl.Logistic(gain=1.0, bias=-4))
Notice that the parameters of the :keyword:`function` (in this case, `gain` and `bias`) can be specified by including
them in its constructor. Some Mechanisms support only a single function. In that case, the :keyword:`function`
@@ -222,9 +224,8 @@
constructor. For example, the parameters of the `Logistic` function in the example above can
also be assigned as follows::
- my_mechanism = TransferMechanism(function=Logistic
- params={FUNCTION_PARAMS: {GAIN:1.0,
- BIAS=-4.0})
+ >>> my_mechanism = pnl.TransferMechanism(function=pnl.Logistic,
+ ... params={pnl.FUNCTION_PARAMS: {pnl.GAIN: 1.0, pnl.BIAS: -4.0}})
Again, while not as simple as specifying these as arguments in the function's constructor, this format is more flexible.
Any values specified in the parameter dictionary will **override** any specified within the constructor for the function
@@ -1257,7 +1258,7 @@ def _parse_arg_variable(self, variable):
return variable
- def _parse_arg_input_states(self, input_states):
+ def _parse_arg_input_states(self, default_variable, size, input_states):
'''
Takes user-inputted argument **input_states** and returns an instance_defaults.variable-like
object that it represents
@@ -1268,6 +1269,7 @@ def _parse_arg_input_states(self, input_states):
A is an instance_defaults.variable-like object
B is True if **input_states** contained an explicit variable specification, False otherwise
'''
+
if input_states is None:
return None, False
@@ -1277,8 +1279,18 @@ def _parse_arg_input_states(self, input_states):
if not isinstance(input_states, Iterable):
input_states = [input_states]
- for s in input_states:
- parsed_spec = _parse_state_spec(owner=self, state_type=InputState, state_spec=s)
+ # Pass default_variable or one based on size to _parse_state_spe as default
+ # FIX: THIS REALLY ISN'T RIGHT: NEED TO BASE IT ON SHAPE REQUESTED IN SIZE
+ # dv = [0]*size if default_variable is None and size is not None else default_variable
+ dv = np.zeros(size) if default_variable is None and size is not None else default_variable
+ dv = convert_to_np_array(dv,2).tolist() if dv is not None else None
+ # dv = convert_to_np_array(default_variable,2).tolist() if default_variable is not None else None
+ for i, s in enumerate(input_states):
+ parsed_spec = _parse_state_spec(owner=self,
+ variable=dv[i] if dv is not None else None,
+ state_type=InputState,
+ state_spec=s,
+ context='_parse_arg_input_states')
if isinstance(parsed_spec, dict):
try:
@@ -1328,13 +1340,15 @@ def _handle_default_variable(self, default_variable=None, size=None, input_state
# handle specifying through params dictionary
try:
- default_variable_from_input_states, input_states_variable_was_specified = self._parse_arg_input_states(params[INPUT_STATES])
+ default_variable_from_input_states, input_states_variable_was_specified = \
+ self._parse_arg_input_states(default_variable, size, params[INPUT_STATES])
except (TypeError, KeyError):
pass
if default_variable_from_input_states is None:
# fallback to standard arg specification
- default_variable_from_input_states, input_states_variable_was_specified = self._parse_arg_input_states(input_states)
+ default_variable_from_input_states, input_states_variable_was_specified = \
+ self._parse_arg_input_states(default_variable, size, input_states)
if default_variable_from_input_states is not None:
if default_variable is None:
@@ -1348,9 +1362,8 @@ def _handle_default_variable(self, default_variable=None, size=None, input_state
else:
raise MechanismError(
'default variable determined from the specified input_states spec ({0}) '
- 'is not compatible with the default variable determined from size parameter ({1})'.format(
- default_variable_from_input_states,
- size_variable,
+ 'is not compatible with the default variable determined from size parameter ({1})'.
+ format(default_variable_from_input_states, size_variable,
)
)
else:
@@ -1361,10 +1374,9 @@ def _handle_default_variable(self, default_variable=None, size=None, input_state
if iscompatible(self._parse_arg_variable(default_variable), default_variable_from_input_states):
default_variable = default_variable_from_input_states
else:
- raise MechanismError(
- 'default variable determined from the specified input_states spec ({0}) '
- 'is not compatible with the specified default variable ({1})'.format(
- default_variable_from_input_states, default_variable
+ raise MechanismError('default variable determined from the specified input_states spec ({0}) '
+ 'is not compatible with the specified default variable ({1})'.
+ format(default_variable_from_input_states, default_variable
)
)
else:
@@ -2276,7 +2288,7 @@ def add_states(self, states, context=ADD_STATES):
`State specification dictionary ` (the latter must have a *STATE_TYPE* entry
specifying the class or keyword for InputState or OutputState).
- Returns
+ Returns a dictionary with two entries, containing the list of InputStates and OutputStates added.
-------
Dictionary with entries containing InputStates and/or OutputStates added
@@ -2308,7 +2320,7 @@ def add_states(self, states, context=ADD_STATES):
# _instantiate_state_list(self, input_states, InputState)
if input_states:
# FIX: 11/9/17
- added_variable, added_input_state = self._parse_arg_input_states(input_states)
+ added_variable, added_input_state = self._parse_arg_input_states(self.variable, self.size, input_states)
if added_input_state:
old_variable = self.instance_defaults.variable.tolist()
old_variable.extend(added_variable)
diff --git a/psyneulink/components/mechanisms/processing/integratormechanism.py b/psyneulink/components/mechanisms/processing/integratormechanism.py
index 2120e600ba2..ebd3b1bccec 100644
--- a/psyneulink/components/mechanisms/processing/integratormechanism.py
+++ b/psyneulink/components/mechanisms/processing/integratormechanism.py
@@ -27,7 +27,8 @@
specifying *INTEGRATOR_MECHANISM* as its **mech_spec** argument. Its function is specified in the **function**
argument, which can be parametrized by calling its constructor with parameter values::
- my_time_averaging_mechanism = IntegratorMechanism(function=AdaptiveIntegrator(rate=0.5))
+ >>> import psyneulink as pnl
+ >>> my_time_averaging_mechanism = pnl.IntegratorMechanism(function=pnl.AdaptiveIntegrator(rate=0.5))
The **default_variable** argument specifies the format of its input (i.e., whether it is a single scalar or an
array), as well as the value to use if none is provided when Mechanism is executed. Alternatively, the **size**
diff --git a/psyneulink/components/mechanisms/processing/objectivemechanism.py b/psyneulink/components/mechanisms/processing/objectivemechanism.py
index 5c208c693ea..7b5abad3a98 100644
--- a/psyneulink/components/mechanisms/processing/objectivemechanism.py
+++ b/psyneulink/components/mechanisms/processing/objectivemechanism.py
@@ -185,12 +185,17 @@
environment). In the example below, this is accomplished by using `default_variable` in the constructor of the
ObjectiveMechanism to force the InputState for the ObjectiveMechanism to have a single value::
- my_action_select_mech = TransferMechanism(default_variable = [0,0,0], function=SoftMax(output=PROB))
+ >>> import psyneulink as pnl
+ >>> my_action_select_mech = pnl.TransferMechanism(default_variable=[0, 0, 0],
+ ... function=pnl.SoftMax(output=pnl.PROB),
+ ... name='Action Selection Mech')
- my_reward_mech = TransferMechanism(default_variable = [0])
+ >>> my_reward_mech = pnl.TransferMechanism(default_variable=[0],
+ ... name='Reward Mech')
- my_objective_mech = ObjectiveMechanism(default_variable = [[0],[0]],
- monitored_output_states = [my_action_select_mech, my_reward_mech])
+ >>> my_objective_mech = pnl.ObjectiveMechanism(default_variable=[[0],[0]],
+ ... monitored_output_states=[my_action_select_mech,
+ ... my_reward_mech])
Note that the OutputStates for the ``my_action_selection`` and ``my_reward_mech`` are specified
in `monitored_output_states`. If that were the only specification, the InputState created for ``my_action_select_mech``
@@ -208,12 +213,12 @@
for ``my_action_select_mech`` using a `InputState specification dictionary ` in
the **monitored_output_states** argument of ``my_objective_mech``, as follows::
- my_objective_mech = ObjectiveMechanism(monitored_output_states = [{MECHANISM: my_action_select_mech,
- VARIABLE: [0]},
- my_reward_mech])
+ >>> my_objective_mech = pnl.ObjectiveMechanism(monitored_output_states=[{pnl.MECHANISM: my_action_select_mech,
+ ... pnl.VARIABLE: [0]},
+ ... my_reward_mech])
Note that the *VARIABLE* entry here specifies the `variable ` for the InputState of the
-ObjectiveMechanism created to receive a Projection from ``my_action_selcect_mech``, and not ``my_action_selcect_mech``
+ObjectiveMechanism created to receive a Projection from ``my_action_select_mech``, and not ``my_action_select_mech``
itself (see `ObjectiveMechanism_Input` for a full explanation).
.. _ObjectiveMechanism_Projection_Example:
@@ -223,8 +228,9 @@
specification ` to assign the matrix for the MappingProjection from
``my_action_select_mech`` to the corresponding InputState of ``my_objective_mech``::
- my_objective_mech = ObjectiveMechanism(monitored_output_states = [(my_action_select_mech, np.ones((3,1)),
- my_reward_mech])
+ >>> import numpy as np
+ >>> my_objective_mech = pnl.ObjectiveMechanism(monitored_output_states=[(my_action_select_mech, np.ones((3,1))),
+ ... my_reward_mech])
Since the matrix specified has three rows (for its inputs) and one col (for the output), it will take the length three
vector provided as the output of ``my_action_select_mech`` and combine its elements into a single value that is
@@ -247,8 +253,9 @@
below, the ObjectiveMechanism used in the previous example is further customized to subtract the value of the action
selected from the value of the reward::
- my_objective_mech = ObjectiveMechanism(default_variable = [[0],[0]],
- monitored_output_states = [(my_action_select_mech, -1, 1), my_reward_mech])
+ >>> my_objective_mech = pnl.ObjectiveMechanism(default_variable = [[0],[0]],
+ ... monitored_output_states = [(my_action_select_mech, -1, 1),
+ ... my_reward_mech])
This specifies that ``my_action_select_mech`` should be assigned a weight of -1 and an exponent of 1 when it is
submitted to the ObjectiveMechanism's `function `. Notice that the exponent had to be
@@ -256,18 +263,32 @@
specified. Notice also that ``my_reward_mech`` does not use a tuple, so it will be assigned defaults for both the
weight and exponent parameters.
-Tuples can also be included in a specification dictionary for the **monitored_output_states** argument, which
-allow several OutputStates for the same Mechanism to be specified more easily, each by name rather than by full
-reference (which is required if they are specified on their own or in a tuple)::
+.. _ObjectiveMechanism_Multiple_OutputStates_Example:
- my_objective_mech = ObjectiveMechanism(monitored_output_states=[Reward,
- {MECHANISM: Decision,
- OUTPUT_STATES: [PROBABILITY_UPPER_THRESHOLD,
- (RESPONSE_TIME, 1, -1)]}])
+An ObjectiveMechanism can also be configured to monitor multiple OutputStates of the same Mechanism. In the following
+example, an ObjectiveMechanism is configured to calculate the reward rate for a `DDM` Mechanism, by specifying
+OutputStates for the DDM that report its response time and accuracy::
+
+ >>> my_decision_mech = pnl.DDM(output_states=[pnl.RESPONSE_TIME,
+ ... pnl.PROBABILITY_UPPER_THRESHOLD])
+
+ >>> my_objective_mech = pnl.ObjectiveMechanism(monitored_output_states=[
+ ... my_reward_mech,
+ ... my_decision_mech.output_states[pnl.PROBABILITY_UPPER_THRESHOLD],
+ ... (my_decision_mech.output_states[pnl.RESPONSE_TIME], 1, -1)])
+
+This specifies that the ObjectiveMechanism should multiply the `value ` of ``my_reward_mech``'s
+`primary OutputState ` by the `value ` of ``my_decision_mech``'s
+*PROBABILITY_UPPER_THRESHOLD*, and divide the result by ``my_decision_mech``'s *RESPONSE_TIME* `value
+`. The two OutputStates of ``my_decision_mech`` are referenced as items in the `output_states
+` list of ``my_decision_mech``. However, a `2-item (State name, Mechanism) tuple
+` can be used to reference them more simply, as follows::
+
+ >>> my_objective_mech = pnl.ObjectiveMechanism(monitored_output_states=[
+ ... my_reward_mech,
+ ... (pnl.PROBABILITY_UPPER_THRESHOLD, my_decision_mech),
+ ... ((pnl.RESPONSE_TIME, my_decision_mech), 1, -1)])
-Note that, as shown in this example, the tuple format can still be used for each individual OutputState in the list
-assigned to the *OUTPUT_STATES* entry.
-COMMENT
*Customizing the ObjectiveMechanism's function*
@@ -275,9 +296,9 @@
`function ` for use in combining their values. The same can be accomplished by
specifying the relevant parameter(s) of the function itself, as in the following example::
- my_objective_mech = ObjectiveMechanism(default_variable = [[0],[0]],
- monitored_output_states = [my_action_select_mech, my_reward_mech],
- function=LinearCombination(weights=[[-1], [1]]))
+ >>> my_objective_mech = pnl.ObjectiveMechanism(default_variable = [[0],[0]],
+ ... monitored_output_states = [my_action_select_mech, my_reward_mech],
+ ... function=pnl.LinearCombination(weights=[[-1], [1]]))
Here, the `weights ` parameter of the `LinearCombination` function is specified directly,
with two values [-1] and [1] corresponding to the two items in `monitored_output_states` (and `default_variable`).
@@ -357,9 +378,11 @@ def __str__(self):
class ObjectiveMechanism(ProcessingMechanism_Base):
+ # monitored_output_states is an alias to input_states argument, which can
+ # still be used in a spec dict
"""
ObjectiveMechanism( \
- monitored_output_states, # alias to input_states argument, which can still be used in a spec dict \
+ monitored_output_states, \
default_variable, \
size, \
function=LinearCombination, \
@@ -400,7 +423,7 @@ class ObjectiveMechanism(ProcessingMechanism_Base):
---------
monitored_output_states : List[`OutputState`, `Mechanism`, str, value, dict, `MonitoredOutputStatesOption`] or dict
- specifies the OutputStates, the `value `\\s of which will be monitored, and evaluated by
+ specifies the OutputStates, the `values ` of which will be monitored, and evaluated by
the ObjectiveMechanism's `function ` (see `ObjectiveMechanism_Monitored_Output_States`
for details of specification).
@@ -452,7 +475,7 @@ class ObjectiveMechanism(ProcessingMechanism_Base):
the input to Mechanism's `function `.
monitored_output_states : ContentAddressableList[OutputState]
- determines the OutputStates, the `value `\\s of which are monitored, and evaluated by the
+ determines the OutputStates, the `values ` of which are monitored, and evaluated by the
ObjectiveMechanism's `function `. Each item in the list refers to an
`OutputState` containing the value to be monitored, with a `MappingProjection` from it to the
corresponding `InputState` listed in the `input_states ` attribute.
diff --git a/psyneulink/components/mechanisms/processing/transfermechanism.py b/psyneulink/components/mechanisms/processing/transfermechanism.py
index db80308aa08..c3373ad9273 100644
--- a/psyneulink/components/mechanisms/processing/transfermechanism.py
+++ b/psyneulink/components/mechanisms/processing/transfermechanism.py
@@ -42,8 +42,9 @@
the **function** argument, which can be the name of a `Function ` class (first example below), or a call to
a Function constructor that can include arguments specifying the Function's parameters (second example)::
- my_linear_transfer_mechanism = TransferMechanism(function=Linear)
- my_logistic_transfer_mechanism = TransferMechanism(function=Logistic(gain=1.0, bias=-4)
+ >>> import psyneulink as pnl
+ >>> my_linear_transfer_mechanism = pnl.TransferMechanism(function=pnl.Linear)
+ >>> my_logistic_transfer_mechanism = pnl.TransferMechanism(function=pnl.Logistic(gain=1.0, bias=-4))
In addition to Function-specific parameters, `noise ` and `time_constant
` parameters can be specified for the Mechanism (see `Transfer_Execution`).
@@ -156,6 +157,7 @@
from psyneulink.components.functions.function import AdaptiveIntegrator, Linear, TransferFunction
from psyneulink.components.mechanisms.mechanism import Mechanism, MechanismError
from psyneulink.components.mechanisms.processing.processingmechanism import ProcessingMechanism_Base
+from psyneulink.components.mechanisms.adaptive.control.controlmechanism import _is_control_spec
from psyneulink.components.states.inputstate import InputState
from psyneulink.components.states.outputstate import OutputState, PRIMARY, StandardOutputStates, standard_output_states
from psyneulink.globals.keywords import NAME, INDEX, FUNCTION, INITIALIZER, INITIALIZING, MEAN, MEDIAN, NOISE, RATE, \
@@ -511,8 +513,7 @@ def __init__(self,
if not isinstance(self.standard_output_states, StandardOutputStates):
self.standard_output_states = StandardOutputStates(self,
self.standard_output_states,
- indices=PRIMARY
- )
+ indices=PRIMARY)
super(TransferMechanism, self).__init__(
variable=default_variable,
@@ -619,11 +620,14 @@ def _validate_noise(self, noise, var):
"The elements of a noise list or array must be floats or functions. {} is not a valid noise"
" element for {}".format(noise_item, self.name))
+ elif _is_control_spec(noise):
+ pass
+
# Otherwise, must be a float, int or function
elif not isinstance(noise, (float, int)) and not callable(noise):
- raise MechanismError(
- "Noise parameter ({}) for {} must be a float, function, or array/list of these."
- .format(noise, self.name))
+ raise MechanismError("Noise parameter ({}) for {} must be a float, "
+ "function, or array/list of these.".format(noise,
+ self.name))
def _try_execute_param(self, param, var):
diff --git a/psyneulink/components/projections/modulatory/gatingprojection.py b/psyneulink/components/projections/modulatory/gatingprojection.py
index 6045e5dbd59..608012ab6aa 100644
--- a/psyneulink/components/projections/modulatory/gatingprojection.py
+++ b/psyneulink/components/projections/modulatory/gatingprojection.py
@@ -116,6 +116,7 @@ def __init__(self, error_value):
def __str__(self):
return repr(self.error_value)
+
class GatingProjection(ModulatoryProjection_Base):
"""
GatingProjection( \
diff --git a/psyneulink/components/projections/modulatory/learningprojection.py b/psyneulink/components/projections/modulatory/learningprojection.py
index 1bc99278da6..dae413ffaa5 100644
--- a/psyneulink/components/projections/modulatory/learningprojection.py
+++ b/psyneulink/components/projections/modulatory/learningprojection.py
@@ -92,7 +92,7 @@
`learning_rate ` for the `LearningMechanism` from which it receives the
`learning_signal `. Specification of the `learning_rate
` for a LearningProjection supersedes any specification(s) of the
-:keyword:`learning_rate` for any `Process ` to which the
+:keyword:`learning_rate` for any `Process ` and/or `System ` to which the
Projection belongs (see `learning_rate ` for additional details). However, its
`learning_rate ` can be specified by the `LearningSignal
` that is its `sender `; that specification takes precedence
diff --git a/psyneulink/components/projections/modulatory/modulatoryprojection.py b/psyneulink/components/projections/modulatory/modulatoryprojection.py
index 9f24e25a559..dd4fa5c1c40 100644
--- a/psyneulink/components/projections/modulatory/modulatoryprojection.py
+++ b/psyneulink/components/projections/modulatory/modulatoryprojection.py
@@ -31,7 +31,7 @@
`function ` in modulating its `value `.
..
* `ControlProjection`
- takes the `value of a of a `ControlSignal` belonging to a `ControlMechanism`,
+ takes the `value of a ` of a `ControlSignal` belonging to a `ControlMechanism`,
and conveys it to the `ParameterState` for the parameter of a `Mechanism ` or its
`function `, for use in modulating the value of the parameter.
diff --git a/psyneulink/components/projections/pathway/mappingprojection.py b/psyneulink/components/projections/pathway/mappingprojection.py
index 128cafe2a1f..5da14f135d5 100644
--- a/psyneulink/components/projections/pathway/mappingprojection.py
+++ b/psyneulink/components/projections/pathway/mappingprojection.py
@@ -46,7 +46,7 @@
* by a `LearningMechanism`, between it and the other components required to implement learning
(see `LearningMechanism_Learning_Configurations` for details);
..
- * by a `ControlMechanism `, from the *OUTCOME* `OutputState of the `ObjectiveMechanism` that `it
+ * by a `ControlMechanism `, from the *OUTCOME* `OutputState` of the `ObjectiveMechanism` that `it
creates ` to its *ERROR_SIGNAL* `InputState`, and from the `OutputStates
` listed in the ObjectiveMechanism's `monitored_output_states `
attribute to the ObjectiveMechanism's `primary InputState ` (as described above; an
@@ -145,7 +145,7 @@
specifying its **sender** or **receiver** arguments. However, for the MappingProjection to be operational,
initialization must be completed by calling its `deferred_init` method. This is not necessary if the MappingProjection
is specified in the `pathway ` of `Process`, or anywhere else that its `sender
-` and receiver ` can be determined by context.
+` and `receiver ` can be determined by context.
.. _Mapping_Structure:
diff --git a/psyneulink/components/projections/projection.py b/psyneulink/components/projections/projection.py
index ca663f998e0..99385ee9a55 100644
--- a/psyneulink/components/projections/projection.py
+++ b/psyneulink/components/projections/projection.py
@@ -390,11 +390,14 @@
from psyneulink.components.shellclasses import Mechanism, Process_Base, Projection, State
from psyneulink.components.states.state import StateError
from psyneulink.globals.keywords import \
- CONTEXT, CONTROL, CONTROL_PROJECTION, EXPONENT, GATING, GATING_PROJECTION, \
- INPUT_STATE, LEARNING, LEARNING_PROJECTION, MAPPING_PROJECTION, MATRIX, MATRIX_KEYWORD_SET, \
- MECHANISM, NAME, OUTPUT_STATE, PARAMETER_STATE_PARAMS, PARAMS, PATHWAY, \
+ NAME, PARAMS, CONTEXT, PATHWAY, \
+ MECHANISM, INPUT_STATE, OUTPUT_STATE, PARAMETER_STATE_PARAMS, \
+ STANDARD_ARGS, STATE, STATES, WEIGHT, EXPONENT, \
PROJECTION, PROJECTION_PARAMS, PROJECTION_SENDER, PROJECTION_TYPE, RECEIVER, SENDER, \
- STANDARD_ARGS, STATE, STATES, WEIGHT, GATING_SIGNAL, \
+ MAPPING_PROJECTION, MATRIX, MATRIX_KEYWORD_SET, \
+ LEARNING, LEARNING_SIGNAL, LEARNING_PROJECTION, \
+ CONTROL, CONTROL_SIGNAL, CONTROL_PROJECTION, \
+ GATING, GATING_SIGNAL, GATING_PROJECTION, \
kwAddInputState, kwAddOutputState, kwProjectionComponentCategory
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.globals.registry import register_category
@@ -414,10 +417,13 @@
PROJECTION_SPEC_KEYWORDS = {PATHWAY: MAPPING_PROJECTION,
LEARNING: LEARNING_PROJECTION,
+ LEARNING_SIGNAL: LEARNING_PROJECTION,
LEARNING_PROJECTION: LEARNING_PROJECTION,
CONTROL: CONTROL_PROJECTION,
+ CONTROL_SIGNAL: CONTROL_PROJECTION,
CONTROL_PROJECTION: CONTROL_PROJECTION,
GATING: GATING_PROJECTION,
+ GATING_SIGNAL: GATING_PROJECTION,
GATING_PROJECTION: GATING_PROJECTION
}
@@ -1009,14 +1015,14 @@ def _is_projection_subclass(spec, keyword):
return True
# Get projection subclass specified by keyword
try:
- type = ProjectionRegistry[keyword]
+ proj_type = ProjectionRegistry[keyword].subclass
except KeyError:
pass
else:
# Check if spec is either the name of the subclass or an instance of it
- if inspect.isclass(spec) and issubclass(spec, type):
+ if inspect.isclass(spec) and issubclass(spec, proj_type):
return True
- if isinstance(spec, type):
+ if isinstance(spec, proj_type):
return True
# spec is a specification dict for an instance of the projection subclass
if isinstance(spec, dict) and keyword in spec:
@@ -1069,15 +1075,15 @@ def _parse_projection_spec(projection_spec,
elif inspect.isclass(projection_spec) and issubclass(projection_spec, Projection):
proj_spec_dict[PROJECTION_TYPE] = projection_spec
- # Projection keyword
- elif isinstance(projection_spec, str):
- proj_spec_dict[PROJECTION_TYPE] = _parse_projection_keyword(projection_spec)
-
# Matrix
elif is_matrix(projection_spec):
is_matrix(projection_spec)
proj_spec_dict[MATRIX] = projection_spec
+ # Projection keyword
+ elif isinstance(projection_spec, str):
+ proj_spec_dict[PROJECTION_TYPE] = _parse_projection_keyword(projection_spec)
+
# State object
elif isinstance(projection_spec, State):
proj_spec_dict[PROJECTION_TYPE] = projection_spec.paramClassDefaults[PROJECTION_TYPE]
@@ -1509,9 +1515,9 @@ def _parse_connection_specs(connectee_state_type,
mech=mech,
mech_state_attribute=connect_with_attr,
projection_socket=projection_socket)
- except StateError:
- raise ProjectionError("Unrecognized specification for {} ({}) in {} specification ({}) for {}".
- format(State.__name__, state_spec, Projection.__name__, connection, owner.name))
+ except StateError as e:
+ raise ProjectionError("Problem with specification for {} in {} specification for {}: ".
+ format(State.__name__, Projection.__name__, owner.name) + e.error_value)
# Check compatibility with any State(s) returned by _get_state_for_socket
@@ -1652,7 +1658,7 @@ def _validate_projection_type(projection_class):
# Try to get the State to which the Projection will be connected when fully initialized
# as confirmation that it is the correct type for state_type
try:
- projection_socket_state = projection_spec.socket_assignments[RECEIVER]
+ projection_socket_state = projection_spec.socket_assignments[projection_socket]
# State for projection's socket couldn't be determined
except KeyError:
# Use Projection's type for validation
@@ -1660,12 +1666,14 @@ def _validate_projection_type(projection_class):
return _validate_projection_type(projection_spec.__class__)
# Projection's socket has been assigned to a State
else:
- if projection_socket_state:
+ # if both SENDER and RECEIVER are specified:
+ if projection_spec.init_args[SENDER] and projection_spec.init_args[RECEIVER]:
# Validate that the State is a class in connect_with_states
if (isinstance(projection_socket_state, connect_with_states) or
(inspect.isclass(projection_socket_state)
and issubclass(projection_socket_state, connect_with_states))):
return True
+ # Otherwise, revert again to validating Projection's type
else:
return _validate_projection_type(projection_spec.__class__)
diff --git a/psyneulink/components/states/inputstate.py b/psyneulink/components/states/inputstate.py
index 13c263d225e..496e4c32677 100644
--- a/psyneulink/components/states/inputstate.py
+++ b/psyneulink/components/states/inputstate.py
@@ -241,6 +241,8 @@
* **InputState specification tuples** -- these are convenience formats that can be used to compactly specify an
InputState and Projections to it any of the following ways:
+ .. _InputState_State_Mechanism_Tuple:
+
* **2-item tuple:** *(, Mechanism)* -- 1st item must be the name of an
`OutputState` or `ModulatorySignal`, or a list of such names, and the 2nd item must be the Mechanism to
which they all belong. Projections of the relevant types are created for each of the specified States
@@ -260,10 +262,10 @@
|
* **value, State specification, or list of State specifications** -- specifies either the `variable
` of the InputState, or one or more States that should project to it. The State
- specification(s) can include Mechanisms, in which case their `primary OutputState `
- is used. All of the State specifications must be consistent with (that is, their `value
- ` must be compatible with the `variable ` of) the
- Projection specified in the fourth item if that is included;
+ specification(s) can be a (State name, Mechanism) tuple (see above), and/or include Mechanisms (in which
+ case their `primary OutputState ` is used. All of the State specifications must be
+ consistent with (that is, their `value ` must be compatible with the `variable
+ ` of) the Projection specified in the fourth item if that is included;
|
* **weight** -- must be an integer or a float; multiplies the `value ` of the InputState
before it is combined with others by the Mechanism's `function ` (see
@@ -770,9 +772,8 @@ def _validate_against_reference_value(self, reference_value):
"""
if reference_value is not None and not iscompatible(reference_value, self.value):
name = self.name or ""
- raise InputStateError("Value specified for {} {} of {} ({}) is not compatible "
- "with its expected format ({})".
- format(name, self.componentName, self.owner.name, self.value, reference_value))
+ raise InputStateError("Value specified for {} {} of {} ({}) is not compatible with its expected format ({})"
+ .format(name, self.componentName, self.owner.name, self.value, reference_value))
def _instantiate_function(self, context=None):
"""Insure that function is LinearCombination and that output is compatible with owner.instance_defaults.variable
@@ -878,15 +879,21 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
# FIX: IS APPLIED TO ALL THE OutputStates SPECIFIED IN OUTPUT_STATES
# FIX: UNLESS THEY THEMSELVES USE A State specification dict WITH ANY OF THOSE ENTRIES
# FIX: USE ObjectiveMechanism EXAMPLES
+ # if MECHANISM in state_specific_spec:
+ # if OUTPUT_STATES in state_specific_spec
return None, state_specific_spec
elif isinstance(state_specific_spec, tuple):
+ # GET STATE_SPEC AND ASSIGN PROJECTIONS_SPEC **********************************************************
+
tuple_spec = state_specific_spec
+ # 2-item tuple specification
if len(tuple_spec) == 2:
- # FIX: 11/12/17 - ??GENERALIZE FOR ALL STATES AND MOVE TO _parse_state_spec
+
# 1st item is a value, so treat as State spec (and return to _parse_state_spec to be parsed)
+ # and treat 2nd item as Projection specification
if is_numeric(tuple_spec[0]):
state_spec = tuple_spec[0]
reference_value = state_dict[REFERENCE_VALUE]
@@ -900,9 +907,10 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
format(InputState.__name__, owner.name, state_spec,
REFERENCE_VALUE, reference_value))
projections_spec = tuple_spec[1]
+
+ # Tuple is Projection specification that is used to specify the State,
else:
- # Tuple is projection specification that is used to specify the State,
- # so return None in state_spec to suppress further, recursive parsing of it in _parse_state_spec
+ # return None in state_spec to suppress further, recursive parsing of it in _parse_state_spec
state_spec = None
if tuple_spec[0] != self:
# If 1st item is not the current state (self), treat as part of the projection specification
@@ -911,14 +919,23 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
# Otherwise, just use 2nd item as projection spec
state_spec = None
projections_spec = tuple_spec[1]
- elif len(tuple_spec) == 4:
+
+ # 3- or 4-item tuple specification
+ elif len(tuple_spec) in {3,4}:
# Tuple is projection specification that is used to specify the State,
# so return None in state_spec to suppress further, recursive parsing of it in _parse_state_spec
state_spec = None
- projections_spec = tuple_spec
+ # Reduce to 2-item tuple Projection specification
+ projection_item = tuple_spec[3] if len(tuple_spec)==4 else None
+ projections_spec = (tuple_spec[0],projection_item)
- if projections_spec is not None:
+ # GET PROJECTIONS IF SPECIFIED *************************************************************************
+ try:
+ projections_spec
+ except UnboundLocalError:
+ pass
+ else:
try:
params_dict[PROJECTIONS] = _parse_connection_specs(self,
owner=owner,
@@ -933,7 +950,17 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
sender_dim = projection_spec.state.value.ndim
projection = projection_spec.projection
if isinstance(projection, dict):
- matrix = projection[MATRIX]
+ # # MODIFIED 11/25/17 OLD:
+ # matrix = projection[MATRIX]
+ # MODIFIED 11/25/17 NEW:
+ # Don't try to get MATRIX from projection without checking,
+ # since projection is a defaultDict,
+ # which will add a matrix entry and assign it to None if it is not there
+ if MATRIX in projection:
+ matrix = projection[MATRIX]
+ else:
+ matrix = None
+ # MODIFIED 11/25/17 END
elif isinstance(projection, Projection):
if projection.init_status is InitStatus.DEFERRED_INITIALIZATION:
continue
@@ -950,7 +977,7 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
if VARIABLE not in state_dict or state_dict[VARIABLE] is None:
state_dict[VARIABLE] = variable
# If variable HAS been assigned, make sure value is the same for this sender
- elif state_dict[VARIABLE].shape != variable.shape:
+ elif np.array(state_dict[VARIABLE]).shape != variable.shape:
# If values for senders differ, assign None so that State's default is used
state_dict[VARIABLE] = None
# No need to check any more Projections
@@ -972,7 +999,8 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
OutputState.__name__,
Projection.__name__))
- # Get weights and exponents if specified
+ # GET WEIGHT AND EXPONENT IF SPECIFIED ***************************************************************
+
if len(tuple_spec) == 2:
pass
diff --git a/psyneulink/components/states/modulatorysignals/controlsignal.py b/psyneulink/components/states/modulatorysignals/controlsignal.py
index 72c87bb6a50..02cf86caae6 100644
--- a/psyneulink/components/states/modulatorysignals/controlsignal.py
+++ b/psyneulink/components/states/modulatorysignals/controlsignal.py
@@ -30,7 +30,7 @@
A ControlSignal is created automatically whenever the parameter of a Mechanism or of its function is `specified for
control `. ControlSignals can also be specified in the **control_signals** argument
of the constructor for a `ControlMechanism `. Although a ControlSignal can be created directly
-using its constructor (or any of the other ways for `creating an outputState `), this is usually
+using its constructor (or any of the other ways for `creating an OutputState `), this is usually
not necessary nor is it advisable, as a ControlSignal has dedicated components and requirements for configuration
that must be met for it to function properly.
@@ -210,7 +210,8 @@
*Modulate the parameter of a Mechanism's function*. The following example assigns a
ControlSignal to the `bias ` parameter of the `Logistic` Function used by a `TransferMechanism`::
- My_Mech = TransferMechanism(function=Logistic(bias=(1.0, ControlSignal)))
+ >>> import psyneulink as pnl
+ >>> my_mech = pnl.TransferMechanism(function=pnl.Logistic(bias=(1.0, pnl.ControlSignal)))
Note that the ControlSignal is specified by it class. This will create a default ControlSignal,
with a ControlProjection that projects to the TransferMechanism's `ParameterState` for the `bias `
@@ -224,7 +225,8 @@
ControlSignal adds to, rather than multiplies, the value of the `gain ` parameter of the Logistic
function::
- My_Mech = TransferMechanism(function=Logistic(gain=(1.0, ControlSignal(modulation=ModulationParam.ADDITIVE))))
+ >>> my_mech = pnl.TransferMechanism(function=pnl.Logistic(gain=(1.0,
+ ... pnl.ControlSignal(modulation=pnl.ModulationParam.ADDITIVE))))
Note that the `ModulationParam` specified for the `ControlSignal` pertains to the function of a *ParameterState*
for the *Logistic* Function (in this case, its `gain ` parameter), and *not* the Logistic function
@@ -260,20 +262,21 @@
the `gain ` parameter of the `Logistic` function for ``My_Mech_A`` and the `intercept
` parameter of the `Linear` function for ``My_Mech_B``::
- My_Mech_A = TransferMechanism(function=Logistic)
- My_Mech_B = TransferMechanism(function=Linear,
- output_states=[RESULT, MEAN])
- Process_A = Process(pathway=[My_Mech_A])
- Process_B = Process(pathway=[My_Mech_B])
-
- My_System = System(processes=[Process_A, Process_B],
- monitor_for_control=[My_Mech_A.output_states[RESULT],
- My_Mech_B.output_states[MEAN]],
- control_signals=[(GAIN, My_Mech_A),
- {NAME: INTERCEPT,
- MECHANISM: My_Mech_B,
- MODULATION: ModulationParam.ADDITIVE}],
- name='My Test System')
+ >>> my_mech_a = pnl.TransferMechanism(function=pnl.Logistic)
+ >>> my_mech_b = pnl.TransferMechanism(function=pnl.Linear,
+ ... output_states=[pnl.RESULT, pnl.MEAN])
+
+ >>> process_a = pnl.Process(pathway=[my_mech_a])
+ >>> process_b = pnl.Process(pathway=[my_mech_b])
+
+ >>> my_system = pnl.System(processes=[process_a, process_b],
+ ... monitor_for_control=[my_mech_a.output_states[pnl.RESULTS],
+ ... my_mech_b.output_states[pnl.MEAN]],
+ ... control_signals=[(pnl.GAIN, my_mech_a),
+ ... {pnl.NAME: pnl.INTERCEPT,
+ ... pnl.MECHANISM: my_mech_b,
+ ... pnl.MODULATION: pnl.ModulationParam.ADDITIVE}],
+ ... name='My Test System')
Class Reference
diff --git a/psyneulink/components/states/modulatorysignals/gatingsignal.py b/psyneulink/components/states/modulatorysignals/gatingsignal.py
index 8bd17a89458..9dafeb85d60 100644
--- a/psyneulink/components/states/modulatorysignals/gatingsignal.py
+++ b/psyneulink/components/states/modulatorysignals/gatingsignal.py
@@ -145,9 +145,11 @@
`primary InputState ` of one Mechanism, and the `primary OutputState `
of another::
- my_mechanism_A = TransferMechanism()
- my_mechanism_B = TransferMechanism()
- my_gating_mechanism = GatingMechanism(gating_signals=[my_mechanism_A, my_mechanism_B.output_state)
+ >>> import psyneulink as pnl
+ >>> my_mechanism_A = pnl.TransferMechanism(name="Mechanism A")
+ >>> my_mechanism_B = pnl.TransferMechanism(name="Mechanism B")
+ >>> my_gating_mechanism = pnl.GatingMechanism(gating_signals=[my_mechanism_A.input_state,
+ ... my_mechanism_B.output_state])
Note that, in the **gating_signals** argument, the first item references a Mechanism (``my_mechanism_A``) rather than
one of its states -- this is all that is necessary, since the default for a `GatingSignal` is to modulate the
@@ -164,13 +166,14 @@
*MULTIPLICATIVE_PARAM* of an InputState's `function `. In the example, this is changed so that
it *adds* the `value ` of the `GatingSignal` to the `value ` of each InputState::
- my_input_layer = TransferMechanism(size=3)
- my_hidden_layer = TransferMechanism(size=5)
- my_output_layer = TransferMechanism(size=2)
- my_gating_mechanism = GatingMechanism(gating_signals=[{'GATE_ALL': [my_input_layer,
- my_hidden_layer,
- my_output_layer]}],
- modulation=ModulationParam.ADDITIVE)
+ >>> my_input_layer = pnl.TransferMechanism(size=3)
+ >>> my_hidden_layer = pnl.TransferMechanism(size=5)
+ >>> my_output_layer = pnl.TransferMechanism(size=2)
+ >>> my_gating_mechanism = pnl.GatingMechanism(gating_signals=[{pnl.NAME: 'GATE_ALL',
+ ... pnl.PROJECTIONS: [my_input_layer,
+ ... my_hidden_layer,
+ ... my_output_layer]}],
+ ... modulation=pnl.ModulationParam.ADDITIVE)
Note that, again, the **gating_signals** are listed as Mechanisms, since in this case it is their primary InputStates
that are to be gated. Since they are all listed in a single entry of a
@@ -188,12 +191,12 @@
using a single GatingSignal. In the example below, a different GatingSignal is assigned to the InputState of each
Mechanism::
- my_gating_mechanism = GatingMechanism(gating_signals=[{NAME: 'GATING_SIGNAL_A',
- MODULATION: ModulationParam.ADDITIVE,
- PROJECTIONS: my_input_layer},
- {NAME: 'GATING_SIGNAL_B',
- PROJECTIONS: [my_hidden_layer,
- my_output_layer]}])
+ >>> my_gating_mechanism = pnl.GatingMechanism(gating_signals=[{pnl.NAME: 'GATING_SIGNAL_A',
+ ... pnl.MODULATION: pnl.ModulationParam.ADDITIVE,
+ ... pnl.PROJECTIONS: my_input_layer},
+ ... {pnl.NAME: 'GATING_SIGNAL_B',
+ ... pnl.PROJECTIONS: [my_hidden_layer,
+ ... my_output_layer]}])
Here, two GatingSignals are specified as `specification dictionaries `, each of which
contains an entry for the name of the GatingSignal, and a *PROJECTIONS* entry that specifies the States to which the
@@ -209,14 +212,14 @@
assigned to a GatingMechanism. In the example below, the same GatingSignals specified in the previous example are
created directly and then assigned to ``my_gating_mechanism``::
- my_gating_signal_A = GatingSignal(name='GATING_SIGNAL_A',
- modulation=ModulationParams.ADDITIVE,
- projections=my_input_layer)
- my_gating_signal_B = GatingSignal(name='GATING_SIGNAL_B',
- projections=my_hidden_layer,
- my_output_layer)
- my_gating_mechanism = GatingMechanism(gating_signals=[my_gating_signal_A,
- my_gating_signal_B])
+ >>> my_gating_signal_A = pnl.GatingSignal(name='GATING_SIGNAL_A',
+ ... modulation=pnl.ModulationParam.ADDITIVE,
+ ... projections=my_input_layer)
+ >>> my_gating_signal_B = pnl.GatingSignal(name='GATING_SIGNAL_B',
+ ... projections=[my_hidden_layer,
+ ... my_output_layer])
+ >>> my_gating_mechanism = pnl.GatingMechanism(gating_signals=[my_gating_signal_A,
+ ... my_gating_signal_B])
Class Reference
diff --git a/psyneulink/components/states/modulatorysignals/modulatorysignal.py b/psyneulink/components/states/modulatorysignals/modulatorysignal.py
index 76718a4478e..937c81cd74d 100644
--- a/psyneulink/components/states/modulatorysignals/modulatorysignal.py
+++ b/psyneulink/components/states/modulatorysignals/modulatorysignal.py
@@ -211,6 +211,19 @@
]
+def _is_modulatory_spec(spec, include_matrix_spec=True):
+ from psyneulink.components.mechanisms.adaptive.learning.learningmechanism import _is_learning_spec
+ from psyneulink.components.mechanisms.adaptive.control.controlmechanism import _is_control_spec
+ from psyneulink.components.mechanisms.adaptive.gating.gatingmechanism import _is_gating_spec
+
+ if (_is_learning_spec(spec, include_matrix_spec=include_matrix_spec)
+ or _is_control_spec(spec)
+ or _is_gating_spec(spec)):
+ return True
+ else:
+ return False
+
+
class ModulatorySignalError(Exception):
def __init__(self, error_value):
self.error_value = error_value
diff --git a/psyneulink/components/states/outputstate.py b/psyneulink/components/states/outputstate.py
index 0ed0fb9eaf1..b79623cd99a 100644
--- a/psyneulink/components/states/outputstate.py
+++ b/psyneulink/components/states/outputstate.py
@@ -269,10 +269,10 @@
* **value, State specification, or list of State specifications** -- specifies either the `variable
` of the InputState, or one or more States that should project to it. The State
- specification(s) can include Mechanisms, in which case their `primary OutputState `
- is used. All of the State specifications must be consistent with (that is, their `value
- ` must be compatible with the `variable ` of) the
- Projection specified in the fourth item if that is included.
+ specification(s) can be a (State name, Mechanism) tuple (see above), and/or include Mechanisms, in which
+ case their `primary InputState ` is used. All of the State specifications must be
+ consistent with (that is, their `value ` must be compatible with the `variable
+ ` of) the Projection specified in the fourth item if that is included.
|
* **index** -- must be an integer; specifies the `index ` for the OutputState.
|
@@ -356,11 +356,12 @@
predefined OutputStates in its `standard_output_states ` attribute.
These can be used in the list of OutputStates specified for a TransferMechanism object, as in the following example::
- my_mech = TransferMechanism(default_variable=[0,0],
- function=Logistic(),
- output_states=[TRANSFER_OUTPUT.RESULT,
- TRANSFER_OUTPUT.MEAN,
- TRANSFER_OUTPUT.VARIANCE)
+ >>> import psyneulink as pnl
+ >>> my_mech = pnl.TransferMechanism(default_variable=[0,0],
+ ... function=pnl.Logistic(),
+ ... output_states=[pnl.TRANSFER_OUTPUT.RESULT,
+ ... pnl.TRANSFER_OUTPUT.MEAN,
+ ... pnl.TRANSFER_OUTPUT.VARIANCE])
In this example, ``my_mech`` is configured with three OutputStates; the first will be named *RESULT* and will
represent logistic transform of the 2-element input vector; the second will be named *MEAN* and will represent mean
@@ -383,12 +384,17 @@
the OutputState is assigned to a Mechanism, by including *INDEX* and *CALCULATE* entries in a `specification dictionary
` for the OutputState, as in the following example::
- my_mech = DDM(function=BogaczEtAl(),
- output_states=[ DDM.DECISION_VARIABLE,
- DDM.PROB_UPPER_THRESHOLD,
- {NAME: 'DECISION ENTROPY',
- INDEX: 2},
- CALCULATE: Entropy().function }])
+
+ >>> my_mech = pnl.DDM(function=pnl.BogaczEtAl(),
+ ... output_states=[pnl.DDM_OUTPUT.DECISION_VARIABLE,
+ ... pnl.DDM_OUTPUT.PROBABILITY_UPPER_THRESHOLD,
+ ... {pnl.NAME: 'DECISION ENTROPY',
+ ... pnl.INDEX: 2,
+ ... pnl.CALCULATE: pnl.Stability(metric=pnl.ENTROPY).function }])
+
+COMMENT:
+ what is this Entropy() class???
+COMMENT
COMMENT:
ADD VERSION IN WHICH INDEX IS SPECIFICED USING DDM_standard_output_states
@@ -407,24 +413,38 @@
Custom OutputStates can also be created on their own, and separately assigned or added to a Mechanism. For example,
the ``DECISION ENTROPY`` OutputState could be created as follows::
- decision_entropy_output_state = OutputState(name='DECISION ENTROPY',
- index=2,
- calculate=Entropy().function)
+ >>> decision_entropy_output_state = pnl.OutputState(name='DECISION ENTROPY',
+ ... index=2,
+ ... calculate=pnl.Stability(metric=pnl.ENTROPY).function)
and then assigned either as::
- my_mech = DDM(function=BogaczEtAl(),
- output_states=[ DDM.DECISION_VARIABLE,
- DDM.PROB_UPPER_THRESHOLD,
- decision_entropy_output_state])
+ >>> my_mech = pnl.DDM(function=pnl.BogaczEtAl(),
+ ... output_states=[pnl.DDM_OUTPUT.DECISION_VARIABLE,
+ ... pnl.DDM_OUTPUT.PROBABILITY_UPPER_THRESHOLD,
+ ... decision_entropy_output_state])
or::
- my_mech = DDM(function=BogaczEtAl(),
- output_states=[ DDM.DECISION_VARIABLE,
- DDM.PROB_UPPER_THRESHOLD)
+ >>> another_decision_entropy_output_state = pnl.OutputState(name='DECISION ENTROPY',
+ ... index=2,
+ ... calculate=pnl.Stability(metric=pnl.ENTROPY).function)
+ >>> my_mech2 = pnl.DDM(function=pnl.BogaczEtAl(),
+ ... output_states=[pnl.DDM_OUTPUT.DECISION_VARIABLE,
+ ... pnl.DDM_OUTPUT.PROBABILITY_UPPER_THRESHOLD])
- my_mech.add_state(decsion_entropy_output_state)
+ >>> my_mech2.add_states(another_decision_entropy_output_state) # doctest: +SKIP
+
+COMMENT:
+The line after the last command is the `add_state ` method returning the list of States
+added to the Mechanism. Note, also, that another new OutputState had to be used for the second example, as trying to
+add the first one created for ``my_mech`` to ``my_mech2`` would have produce an error (since a State already
+belonging to one Mechanism can't be added to another.
+COMMENT
+
+Note that another new OutputState had to be used for the second example, as trying to
+add the first one created for ``my_mech`` to ``my_mech2`` would have produce an error (since a State already
+belonging to one Mechanism can't be added to another.
.. _OutputState_Structure:
@@ -891,22 +911,45 @@ def _validate_params(self, request_set, target_set=None, context=None):
# IMPLEMENT: VALIDATE THAT CALCULATE FUNCTION ACCEPTS VALUE CONSISTENT WITH
# CORRESPONDING ITEM OF OWNER MECHANISM'S VALUE
if CALCULATE in target_set:
+
try:
if isinstance(target_set[CALCULATE], type):
function = target_set[CALCULATE]().function
else:
function = target_set[CALCULATE]
try:
- function(self.owner.default_value[target_set[INDEX]])
+ index = target_set[INDEX]
+ except KeyError:
+ # Assign default value for index if it was not specified
+ index = self.index
+ # Default index is an index keyword (e.g., SEQUENTIAL) so can't evaluate at present
+ if isinstance(index, str):
+ if not index in StandardOutputStates.keywords:
+ raise OutputStateError("Illegal keyword ({}) found in specification of index for {} of {}".
+ format(index, self.name, self.owner.name))
+ return
+
+ default_value_item_str = self.owner.default_value[index] if isinstance(index, int) else index
+ error_msg = ("Item {} of value for {} ({}) is not compatible with "
+ "the function specified for the {} parameter of {} ({})".
+ format(index,
+ self.owner.name,
+ default_value_item_str,
+ CALCULATE,
+ self.name,
+ target_set[CALCULATE]))
+ try:
+ function(self.owner.default_value[index], context=context)
+ except TypeError:
+ try:
+ function(self.owner.default_value[index])
+ except:
+ raise OutputStateError(error_msg)
+ # except IndexError:
+ # # This handles cases in which index has not yet been assigned
+ # pass
except:
- raise OutputStateError("Item {} of value for {} ({}) is not compatible with the function "
- "specified for the {} parameter of {} ({})".
- format(target_set[INDEX],
- self.owner.name,
- self.owner.default_value[target_set[INDEX]],
- CALCULATE,
- self.name,
- target_set[CALCULATE]))
+ raise OutputStateError(error_msg)
except KeyError:
pass
@@ -1032,6 +1075,9 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
return None, state_specific_spec
elif isinstance(state_specific_spec, ProjectionTuple):
+ # MODIFIED 11/25/17 NEW:
+ state_spec = None
+ # MODIFIED 11/25/17 END:
params_dict[PROJECTIONS] = _parse_connection_specs(self,
owner=owner,
connections=[state_specific_spec])
@@ -1215,7 +1261,7 @@ def _instantiate_output_states(owner, output_states=None, context=None):
# If OutputState's calculate function is specified, use it to determine OutputState's vaue
if CALCULATE in output_state[PARAMS]:
- output_state_value = output_state[PARAMS][CALCULATE](owner_value[index])
+ output_state_value = output_state[PARAMS][CALCULATE](owner_value[index], context=context)
else:
output_state_value = owner_value[index]
@@ -1271,7 +1317,8 @@ class StandardOutputStates():
output_state_dicts : list of dicts
list of dictionaries specifying OutputStates for the Component specified by `owner`
- indices : PRIMARY, SEQUENTIAL, list of ints
+ indices : PRIMARY,
+ SEQUENTIAL, list of ints
specifies how to assign the INDEX entry for each dict listed in `output_state_dicts`;
The effects of each value of indices are as follows:
@@ -1304,6 +1351,8 @@ class StandardOutputStates():
returns a copy of the designated OutputState specification dictionary
"""
+ keywords = {PRIMARY, SEQUENTIAL, ALL}
+
@tc.typecheck
def __init__(self,
owner:Component,
diff --git a/psyneulink/components/states/parameterstate.py b/psyneulink/components/states/parameterstate.py
index 7cf76cd4f04..d06867ecdc1 100644
--- a/psyneulink/components/states/parameterstate.py
+++ b/psyneulink/components/states/parameterstate.py
@@ -105,7 +105,7 @@
* **Modulatory specification** -- this can be an existing `ControlSignal` or `ControlProjection`,
a `LearningSignal` or `LearningProjection`, a constructor or the class name for any of these, or the
- keywords *CONTROL*, *CONTROL_PROJECTION*, *LEARNING*, or *LEARNING_PROJECTION. Any of these create a default
+ keywords *CONTROL*, *CONTROL_PROJECTION*, *LEARNING*, or *LEARNING_PROJECTION*. Any of these create a default
ParameterState, assign the parameter's default value as the ParameterState's `value `,
and assign the parameter's name as the name of the ParameterState. They also create and/or assign the
corresponding ModulatorySignal and ModulatoryProjection, and assign the ParameterState as the
@@ -150,11 +150,18 @@
In the following example, a Mechanism is created by specifying two of its parameters, as well as its
`function ` and two of that function's parameters, each using a different specification format::
- my_mechanism = RecurrentTransferMechanism(size=5,
- noise=ControlSignal),
- function=Logistic(gain=(0.5, ControlSignal),
- bias=(1.0, ControlSignal(
- modulation=ModulationParam.ADDITIVE))))
+ >>> import psyneulink as pnl
+ >>> my_mechanism = pnl.RecurrentTransferMechanism(
+ ... size=5,
+ ... noise=pnl.ControlSignal(),
+ ... function=pnl.Logistic(
+ ... gain=(0.5, pnl.ControlSignal),
+ ... bias=(1.0, pnl.ControlSignal(modulation=pnl.ModulationParam.ADDITIVE))))
+
+COMMENT:
+ If assigning a default ControlSignal makes the noise value the same as the
+ default noise value, why are we using a ControlSignal here??
+COMMENT
The first argument of the constructor for the Mechanism specifies its `size ` parameter by
directly assigning a value to it. The second specifies the `noise ` parameter
@@ -170,9 +177,12 @@
`matrix ` parameter is assigned a random weight matrix (using a
`matrix keyword `) and `LearningSignal`::
- my_mapping_projection = MappingProjection(sender=my_input_mechanism,
- receiver=my_output_mechanism,
- matrix=(RANDOM_CONNECTIVITY_MATRIX, LearningSignal))
+ >>> my_input_mechanism = pnl.TransferMechanism()
+ >>> my_output_mechanism = pnl.TransferMechanism()
+ >>> my_mapping_projection = pnl.MappingProjection(sender=my_input_mechanism,
+ ... receiver=my_output_mechanism,
+ ... matrix=(pnl.RANDOM_CONNECTIVITY_MATRIX,
+ ... pnl.LearningSignal))
.. note::
The `matrix ` parameter belongs to the MappingProjection's
@@ -182,22 +192,22 @@
The example below shows how to specify the parameters in the first example using a parameter specification dictionary::
- my_mechanism = RecurrentTransferMechanism(
- size=5
- params={NOISE:5,
- 'size':ControlSignal,
- FUNCTION:Logistic,
- FUNCTION_PARAMS:{GAIN:(0.5, ControlSignal),
- BIAS:(1.0, ControlSignal(modulation=ModulationParam.ADDITIVE))))
+ >>> my_mechanism = pnl.RecurrentTransferMechanism(
+ ... noise=5,
+ ... params={pnl.NOISE: pnl.CONTROL,
+ ... pnl.FUNCTION: pnl.Logistic,
+ ... pnl.FUNCTION_PARAMS:{
+ ... pnl.GAIN:(0.5,pnl.ControlSignal),
+ ... pnl.BIAS:(1.0,pnl.ControlSignal(modulation=pnl.ModulationParam.ADDITIVE))}})
There are several things to note here. First, the parameter specification dictionary must be assigned to the
**params** argument of the constructor. Second, both methods for specifying a parameter -- directly in an argument
for the parameter, or in an entry of a parameter specification dictionary -- can be used within the same constructor.
-If a particular parameter is specified in both ways (as is the case for **size** in the example), the value in the
+If a particular parameter is specified in both ways (as is the case for **noise** in the example), the value in the
parameter specification dictionary takes priority (i.e., it is the value that will be assigned to the parameter). If
the parameter is specified in a parameter specification dictionary, the key for the parameter must be a string that is
the same as the name of parameter (i.e., identical to how it appears as an arg in the constructor; as is shown
-for **size** in the example), or using a keyword that resolves to such a string (as shown for *NOISE* in the
+for **noise** in the example), or using a keyword that resolves to such a string (as shown for *NOISE* in the
example). Finally, the keyword *FUNCTION_PARAMS* can be used in a parameter specification dictionary to specify
parameters of the Component's `function `, as shown for the **gain** and **bias** parameters of
the Logistic function in the example.
@@ -284,16 +294,19 @@
import numpy as np
import typecheck as tc
-from psyneulink.components.component import Component, function_type, method_type, parameter_keywords
+from psyneulink.components.component import Component, function_type, method_type, parameter_keywords, InitStatus
from psyneulink.components.functions.function import Linear, get_param_value_for_keyword
from psyneulink.components.shellclasses import Mechanism, Projection
from psyneulink.components.states.state import StateError, State_Base, _instantiate_state, state_type_keywords
-from psyneulink.globals.keywords import CONTROL_PROJECTION, FUNCTION, FUNCTION_PARAMS, MECHANISM, PARAMETER_STATE, \
- PARAMETER_STATES, PARAMETER_STATE_PARAMS, PATHWAY_PROJECTION, PROJECTION, PROJECTIONS, PROJECTION_TYPE, VALUE, \
- CONTROL_SIGNAL, CONTROL_SIGNALS, LEARNING_SIGNAL, LEARNING_SIGNALS, SENDER
+from psyneulink.components.states.modulatorysignals.modulatorysignal import ModulatorySignal
+from psyneulink.globals.keywords import \
+ NAME, CONTROL_PROJECTION, FUNCTION, FUNCTION_PARAMS, MECHANISM, PARAMETER_STATE, SENDER, \
+ PARAMETER_STATES, PARAMETER_STATE_PARAMS, PATHWAY_PROJECTION, PROJECTION, PROJECTIONS, PROJECTION_TYPE, \
+ VALUE, REFERENCE_VALUE, CONTROL_SIGNAL, CONTROL_SIGNALS, LEARNING_SIGNAL, LEARNING_SIGNALS
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
-from psyneulink.globals.utilities import ContentAddressableList, ReadOnlyOrderedDict, is_numeric, is_value_spec, iscompatible
+from psyneulink.globals.utilities \
+ import ContentAddressableList, ReadOnlyOrderedDict, is_numeric, is_value_spec, iscompatible
__all__ = [
'ParameterState', 'ParameterStateError', 'state_type_keywords',
@@ -590,7 +603,7 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
Returns params dict with PROJECTIONS entries if any of these was specified.
"""
- from psyneulink.components.projections.projection import _parse_connection_specs
+ from psyneulink.components.projections.projection import _parse_connection_specs, _is_projection_spec
params_dict = {}
state_spec = state_specific_spec
@@ -600,30 +613,163 @@ def _parse_state_specific_specs(self, owner, state_dict, state_specific_spec):
elif isinstance(state_specific_spec, tuple):
+ # # MODIFIED 11/25/17 OLD:
+ # tuple_spec = state_specific_spec
+ # state_spec = tuple_spec[0]
+ #
+ # # Get connection (afferent Projection(s)) specification from tuple
+ # PROJECTIONS_INDEX = len(tuple_spec)-1
+ # # Get projection_spec and parse
+ # try:
+ # projections_spec = tuple_spec[PROJECTIONS_INDEX]
+ # except IndexError:
+ # projections_spec = None
+ #
+ # if projections_spec:
+ # try:
+ # params_dict[PROJECTIONS] = _parse_connection_specs(self,
+ # owner=owner,
+ # connections=projections_spec)
+ # except ParameterStateError:
+ # raise ParameterStateError("Item {} of tuple specification in {} specification dictionary "
+ # "for {} ({}) is not a recognized specification".
+ # format(PROJECTIONS_INDEX,
+ # ParameterState.__name__,
+ # owner.name,
+ # projections_spec))
+
+ # MODIFIED 11/25/17 NEW:
tuple_spec = state_specific_spec
- state_spec = tuple_spec[0]
- # Get connection (afferent Projection(s)) specification from tuple
- PROJECTIONS_INDEX = len(tuple_spec)-1
- # Get projection_spec and parse
- try:
- projections_spec = tuple_spec[PROJECTIONS_INDEX]
- except IndexError:
- projections_spec = None
+ # GET STATE_SPEC (PARAM VALUE) AND ASSIGN PROJECTIONS_SPEC **********************************************
+
+ # 2-item tuple specification
+ if len(tuple_spec) == 2:
+
+ # 1st item is a value, so treat as State spec (and return to _parse_state_spec to be parsed)
+ # and treat 2nd item as Projection specification
+ if is_numeric(tuple_spec[0]):
+ state_spec = tuple_spec[0]
+ reference_value = state_dict[REFERENCE_VALUE]
+ # Assign value so sender_dim is skipped below
+ # (actual assignment is made in _parse_state_spec)
+ if reference_value is None:
+ state_dict[REFERENCE_VALUE]=state_spec
+ elif not iscompatible(state_spec, reference_value):
+ raise StateError("Value in first item of 2-item tuple specification for {} of {} ({}) "
+ "is not compatible with its {} ({})".
+ format(ParameterState.__name__, owner.name, state_spec,
+ REFERENCE_VALUE, reference_value))
+ projections_spec = tuple_spec[1]
+
+ elif _is_projection_spec(tuple_spec[0], include_matrix_spec=True):
+ state_spec, projections_spec = tuple_spec
+
+ # Tuple is Projection specification that is used to specify the State,
+ else:
+ # return None in state_spec to suppress further, recursive parsing of it in _parse_state_spec
+ state_spec = None
+ if tuple_spec[0] != self:
+ # If 1st item is not the current state (self), treat as part of the projection specification
+ projections_spec = tuple_spec
+ else:
+ # Otherwise, just use 2nd item as projection spec
+ state_spec = None
+ projections_spec = tuple_spec[1]
+
+ # 3- or 4-item tuple specification
+ elif len(tuple_spec) in {3,4}:
+ # Tuple is projection specification that is used to specify the State,
+ # so return None in state_spec to suppress further, recursive parsing of it in _parse_state_spec
+ state_spec = None
+ # Reduce to 2-item tuple Projection specification
+ projection_item = tuple_spec[3] if len(tuple_spec)==4 else None
+ projections_spec = (tuple_spec[0],projection_item)
+
+ # GET PROJECTIONS IF SPECIFIED *************************************************************************
- if projections_spec:
+ try:
+ projections_spec
+ except UnboundLocalError:
+ pass
+ else:
try:
params_dict[PROJECTIONS] = _parse_connection_specs(self,
owner=owner,
connections=projections_spec)
+
+ # Parse the value of all of the Projections to get/validate parameter value
+ from psyneulink.components.projections.modulatory.controlprojection import ControlProjection
+ from psyneulink.components.projections.modulatory.learningprojection import LearningProjection
+
+ for projection_spec in params_dict[PROJECTIONS]:
+ if state_dict[REFERENCE_VALUE] is None:
+ # FIX: 10/3/17 - PUTTING THIS HERE IS A HACK...
+ # FIX: MOVE TO _parse_state_spec UNDER PROCESSING OF ProjectionTuple SPEC
+ # FIX: USING _get_state_for_socket
+ # from psyneulink.components.projections.projection import _parse_projection_spec
+
+ mod_signal_value = projection_spec.state.value \
+ if isinstance(projection_spec.state, State_Base) else None
+
+
+ # MODIFIED 11/25/17 OLD:
+ mod_projection = projection_spec.projection
+ if isinstance(mod_projection, dict):
+ if mod_projection[PROJECTION_TYPE] not in {ControlProjection, LearningProjection}:
+ raise ParameterStateError("PROGRAM ERROR: {} other than {} or {} ({}) found "
+ "in specification tuple for {} param of {}".
+ format(Projection.__name__,
+ ControlProjection.__name__,
+ LearningProjection.__name__,
+ mod_projection, state_dict[NAME], owner.name))
+ elif VALUE in mod_projection:
+ mod_proj_value = mod_projection[VALUE]
+ else:
+ mod_proj_value = None
+ elif isinstance(mod_projection, Projection):
+ if not isinstance(mod_projection, (ControlProjection, LearningProjection)):
+ raise ParameterStateError("PROGRAM ERROR: {} other than {} or {} ({}) found "
+ "in specification tuple for {} param of {}".
+ format(Projection.__name__,
+ ControlProjection.__name__,
+ LearningProjection.__name__,
+ mod_projection, state_dict[NAME], owner.name))
+ elif mod_projection.init_status is InitStatus.DEFERRED_INITIALIZATION:
+ continue
+ mod_proj_value = mod_projection.value
+ else:
+ raise ParameterStateError("Unrecognized Projection specification for {} of {} ({})".
+ format(self.name, owner.name, projection_spec))
+
+ # FIX: 11/25/17 THIS IS A MESS: CHECK WHAT IT'S ACTUALLY DOING
+ # If ModulatoryProjection's value is not specified, try to assign one
+ if mod_proj_value is None:
+ # If not specified for State, assign that
+ if VALUE not in state_dict or state_dict[VALUE] is None:
+ state_dict[VALUE] = mod_signal_value
+ # If value has been assigned, make sure value is the same for ModulatorySignal
+ elif state_dict[VALUE] != mod_signal_value:
+ # If the values differ, assign None so that State's default is used
+ state_dict[VALUE] = None
+ # No need to check any more ModulatoryProjections
+ break
+
+ #
+ else:
+ state_dict[VALUE] = mod_proj_value
+
except ParameterStateError:
- raise ParameterStateError("Item {} of tuple specification in {} specification dictionary "
- "for {} ({}) is not a recognized specification".
- format(PROJECTIONS_INDEX,
- ParameterState.__name__,
+ raise ParameterStateError("Tuple specification in {} specification dictionary "
+ "for {} ({}) is not a recognized specification for one or more "
+ "{}s, {}s, or {}s that project to it".
+ format(ParameterState.__name__,
owner.name,
- projections_spec))
-
+ projections_spec,
+ Mechanism.__name__,
+ ModulatorySignal.__name__,
+ Projection.__name__))
+ # MODIFIED 11/25/17 END
elif state_specific_spec is not None:
raise ParameterStateError("PROGRAM ERROR: Expected tuple or dict for {}-specific params but, got: {}".
@@ -720,7 +866,7 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
ParameterState that already exists (e.g., in case of a call from Component.assign_params)
non-numeric value (including NotImplemented, False or True)
unless it is:
- a tuple (could be on specifying ControlProjection, LearningProjection or Modulation)
+ a tuple (could be one specifying Modulatory Component)
a dict with the name FUNCTION_PARAMS (otherwise exclude)
function or method
IMPLEMENTATION NOTE: FUNCTION_RUNTIME_PARAM_NOT_SUPPORTED
@@ -731,7 +877,23 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
If param_name is FUNCTION_PARAMS and param is a matrix (presumably for a MappingProjection)
modify ParameterState's function to be LinearCombination (rather Linear which is the default)
"""
+ from psyneulink.components.states.modulatorysignals.modulatorysignal import _is_modulatory_spec
+ from psyneulink.components.projections.modulatory.modulatoryprojection import ModulatoryProjection_Base
+ from psyneulink.components.states.state import _parse_state_spec
+ def _get_tuple_for_single_item_modulatory_spec(obj, name, value):
+ """Return (, ) for modulatory spec
+ """
+ try:
+ param_default_value = obj.paramClassDefaults[name]
+ # Only assign default value if it is not None
+ if param_default_value is not None:
+ return (param_default_value, value)
+ else:
+ return value
+ except:
+ raise ParameterStateError("Unrecognized specification for {} paramater of {} ({})".
+ format(param_name, owner.name, param_value))
# EXCLUSIONS:
@@ -747,24 +909,27 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
# Only allow a FUNCTION_PARAMS dict
elif isinstance(param_value, ReadOnlyOrderedDict) and param_name is FUNCTION_PARAMS:
pass
- # FIX: UPDATE WITH MODULATION_MODS
- # WHAT ABOUT GatingProjection??
- # Allow ControlProjection, LearningProjection
+ # Allow ModulatoryProjection
elif isinstance(param_value, Projection):
- from psyneulink.components.projections.modulatory.controlprojection import ControlProjection
- from psyneulink.components.projections.modulatory.learningprojection import LearningProjection
- if isinstance(param_value, (ControlProjection, LearningProjection)):
+ if isinstance(param_value, ModulatoryProjection_Base):
pass
else:
return
# Allow Projection class
elif inspect.isclass(param_value) and issubclass(param_value, Projection):
- from psyneulink.components.projections.modulatory.controlprojection import ControlProjection
- from psyneulink.components.projections.modulatory.learningprojection import LearningProjection
- if issubclass(param_value, (ControlProjection, LearningProjection)):
+ if issubclass(param_value, (ModulatoryProjection_Base)):
pass
else:
return
+
+ # MODIFIED 11/25/17 NEW:
+ elif _is_modulatory_spec(param_value, include_matrix_spec=False) and not isinstance(param_value, tuple):
+ # If parameter is a single Modulatory specification (e.g., ControlSignal, or CONTROL, etc.)
+ # (note: exclude matrix since it is allowed as a value specification vs. a projection reference)
+ # Try to place it in a tuple (for interpretation by _parse_state_spec) using default value as 1st item
+ param_value = _get_tuple_for_single_item_modulatory_spec(owner, param_name, param_value)
+ # MODIFIED 11/25/17 END:
+
# Allow tuples (could be spec that includes a Projection or Modulation)
elif isinstance(param_value, tuple):
# # MODIFIED 4/18/17 NEW:
@@ -820,6 +985,18 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
"with the same name as a parameter of the component itself".
format(function_name, owner.name, function_param_name))
+ # MODIFIED 11/25/17 NEW:
+ elif (_is_modulatory_spec(function_param_value, include_matrix_spec=False)
+ and not isinstance(function_param_value, tuple)):
+ # If parameter is a single Modulatory specification (e.g., ControlSignal, or CONTROL, etc.)
+ # (note: exclude matrix since it is allowed as a value specification vs. a projection reference)
+ # Try to place it in a tuple (for interpretation by _parse_state_spec) using default value as 1st item
+ function_param_value = _get_tuple_for_single_item_modulatory_spec(owner.function,
+ function_param_name,
+ function_param_value)
+ # MODIFIED 11/25/17 END:
+
+
# # FIX: 10/3/17 - ??MOVE THIS TO _parse_state_specific_specs ----------------
# # Use function_param_value as constraint
# # IMPLEMENTATION NOTE: need to copy, since _instantiate_state() calls _parse_state_value()
@@ -833,6 +1010,10 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
else:
from copy import deepcopy
reference_value = deepcopy(function_param_value)
+
+
+
+
# # FIX: ----------------------------------------------------------------------
# Assign parameterState for function_param to the component
@@ -851,6 +1032,7 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
state = _instantiate_state(owner=owner,
state_type=ParameterState,
name=param_name,
+ state_spec=param_value,
reference_value=param_value,
reference_value_name=param_name,
params=None,
@@ -861,6 +1043,10 @@ def _instantiate_parameter_state(owner, param_name, param_value, context):
def _is_legal_param_value(owner, value):
+ from psyneulink.components.states.modulatorysignals.modulatorysignal import _is_modulatory_spec
+ from psyneulink.components.mechanisms.adaptive.control.controlmechanism import _is_control_spec
+ from psyneulink.components.mechanisms.adaptive.gating.gatingmechanism import _is_gating_spec
+
# LEGAL PARAMETER VALUES:
# # lists, arrays or numeric values
@@ -877,6 +1063,11 @@ def _is_legal_param_value(owner, value):
if isinstance(value, dict) and VALUE in value:
return True
+ # MODIFIED 11/25/17 NEW:
+ if _is_control_spec(value) or _is_gating_spec(value):
+ return True
+ # MODIFIED 11/25/17 END
+
# keyword that resolves to one of the above
if get_param_value_for_keyword(owner, value) is not None:
return True
@@ -909,3 +1100,26 @@ def _get_parameter_state(sender_owner, sender_type, param_name, component):
"specified in {} for {}".
format(param_name, component.name, sender_type, sender_owner.name))
+# def _assign_default_value():
+#
+# elif _is_modulatory_spec(param_value, include_matrix_spec=False):
+# # If parameter is a single Modulatory specification (e.g., ControlSignal, or CONTROL, etc.)
+# # (note: exclude matrix since it is allowed as a value specification vs. a projection reference)
+# if not isinstance(param_value, tuple):
+# # Try to place it in a tuple (for interpretation by _parse_state_spec) using default value as 1st item
+# try:
+# param_default_value = owner.paramClassDefaults[param_name]
+# # Only assign default value if it is not None
+# if param_default_value is not None:
+# param_value = (param_default_value, param_value)
+# try:
+# # Set actual param (ownner's attribute) to assigned value
+# setattr(owner, param_name, param_default_value)
+# except:
+# raise ParameterStateError("Unable to assign {} as value for {} paramater of {}".
+# format(param_value, param_name, owner.name))
+# except ParameterStateError as e:
+# raise ParameterStateError(e)
+# except:
+# raise ParameterStateError("Unrecognized specification for {} paramater of {} ({})".
+# format(param_name, owner.name, param_value))
diff --git a/psyneulink/components/states/state.py b/psyneulink/components/states/state.py
index 942254e5101..eb208010c38 100644
--- a/psyneulink/components/states/state.py
+++ b/psyneulink/components/states/state.py
@@ -730,7 +730,7 @@ def test_multiple_modulatory_projections_with_mech_and_state_name_specs(self):
from psyneulink.components.component import Component, ComponentError, InitStatus, component_keywords, function_type
from psyneulink.components.functions.function import LinearCombination, ModulationParam, \
- get_matrix, _get_modulated_param, get_param_value_for_function, get_param_value_for_keyword
+ _get_modulated_param, get_param_value_for_keyword
from psyneulink.components.shellclasses import Mechanism, Process_Base, Projection, State
from psyneulink.globals.keywords import DEFERRED_INITIALIZATION, \
CONTEXT, COMMAND_LINE, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, EXECUTING, FUNCTION, FUNCTION_PARAMS, \
@@ -789,6 +789,7 @@ class StateError(Exception):
def __init__(self, error_value):
self.error_value = error_value
+
def __str__(self):
return repr(self.error_value)
@@ -1462,7 +1463,8 @@ def _instantiate_projections_to_state(self, projections, context=None):
sender = _get_state_for_socket(owner=self.owner,
state_spec=proj_sender,
state_types=state)
- projection.init_args[SENDER] = sender
+ else:
+ sender = proj_sender
else:
sender = state
projection.init_args[SENDER] = sender
@@ -1573,6 +1575,7 @@ def _instantiate_projection_from_state(self, projection_spec, receiver=None, con
from psyneulink.components.projections.modulatory.modulatoryprojection import ModulatoryProjection_Base
from psyneulink.components.projections.pathway.pathwayprojection import PathwayProjection_Base
from psyneulink.components.projections.pathway.mappingprojection import MappingProjection
+ from psyneulink.components.projections.modulatory.gatingprojection import GatingProjection
from psyneulink.components.projections.projection import ProjectionTuple, _parse_connection_specs
# FIX: 10/3/17 THIS NEEDS TO BE MADE SPECIFIC TO EFFERENT PROJECTIONS (I.E., FOR WHICH IT CAN BE A SENDER)
@@ -1624,8 +1627,11 @@ def _get_receiver_state(spec):
owner=self.owner,
connections=receiver)
return _get_receiver_state(spec[0].state)
- if isinstance(spec, Projection):
+ elif isinstance(spec, Projection):
return spec.receiver
+ # FIX: 11/25/17 -- NEEDS TO CHECK WHETHER PRIMARY SHOULD BE INPUT_STATE OR PARAMETER_STATE
+ elif isinstance(spec, Mechanism):
+ return spec.input_state
return spec
receiver_state = _get_receiver_state(receiver)
connection_receiver_state = _get_receiver_state(connection)
@@ -1648,7 +1654,7 @@ def _get_receiver_state(spec):
# If receiver is a Mechanism and Projection is a MappingProjection,
# use primary InputState (and warn if verbose is set)
- if isinstance(default_projection_type, MappingProjection):
+ if isinstance(default_projection_type, (MappingProjection, GatingProjection)):
if self.owner.verbosePref:
warnings.warn("Receiver {} of {} from {} is a {} and {} is a {}, "
"so its primary {} will be used".
@@ -1657,7 +1663,6 @@ def _get_receiver_state(spec):
InputState.__name__))
receiver = receiver.input_state
- else:
raise StateError("Receiver {} of {} from {} is a {}, but the specified {} is a {} so "
"target {} can't be determined".
format(receiver, projection_spec, self.name, Mechanism.__name__,
@@ -1700,7 +1705,10 @@ def _get_receiver_state(spec):
if not (projection_spec or len(projection_spec)):
projection_spec = {SENDER: self, RECEIVER: receiver_state}
projection = projection_type(**projection_spec)
- projection.receiver = projection.receiver or receiver
+ try:
+ projection.receiver = projection.receiver
+ except AttributeError:
+ projection.receiver = receiver
proj_recvr = projection.receiver
else:
@@ -2090,13 +2098,14 @@ def all_afferents(self):
def _assign_default_state_name(self, context=None):
return False
+
def _instantiate_state_list(owner,
- state_list, # list of State specs, (state_spec, params) tuples, or None
- state_type, # StateType subclass
- state_param_identifier, # used to specify state_type State(s) in params[]
- reference_value, # value(s) used as default for State and to check compatibility
- reference_value_name, # name of reference_value type (e.g. variable, output...)
- context=None):
+ state_list, # list of State specs, (state_spec, params) tuples, or None
+ state_type, # StateType subclass
+ state_param_identifier, # used to specify state_type State(s) in params[]
+ reference_value, # value(s) used as default for State and to check compatibility
+ reference_value_name, # name of reference_value type (e.g. variable, output...)
+ context=None):
"""Instantiate and return a ContentAddressableList of States specified in state_list
Arguments:
@@ -2147,11 +2156,11 @@ def _instantiate_state_list(owner,
# issue warning if in VERBOSE mode:
if owner.prefs.verbosePref:
- print("No {0} specified for {1}; default will be created using {2} of function ({3})"
- " as its value".format(state_param_identifier,
- owner.__class__.__name__,
- reference_value_name,
- reference_value))
+ print("No {0} specified for {1}; default will be created using {2} "
+ "of function ({3}) as its value".format(state_param_identifier,
+ owner.__class__.__name__,
+ reference_value_name,
+ reference_value))
# States should be either in a list, or possibly an np.array (from reference_value assignment above):
if not isinstance(state_list, (ContentAddressableList, list, np.ndarray)):
@@ -2210,7 +2219,6 @@ def _instantiate_state_list(owner,
state_spec=state_spec,
# name=name,
context=context)
-
# # Get name of state, and use as index to assign to states ContentAddressableList
# default_name = state._assign_default_state_name()
# if default_name:
@@ -2291,9 +2299,6 @@ def _instantiate_state(state_type:_is_state_class, # State's type
context=context,
**state_spec)
- # if isinstance(parsed_state_spec, dict) and parsed_state_spec[NAME] is None:
- # parsed_state_spec[NAME] = state_type.__name__
-
# STATE SPECIFICATION IS A State OBJECT ***************************************
# Validate and return
@@ -2440,27 +2445,6 @@ def _parse_state_type(owner, state_spec):
format(owner.name, state_spec[STATE]))
return state_spec[STATE_TYPE]
- # # Mechanism specification (State inferred from context)
- # if isinstance(state_spec, Mechanism):
-
- # # Projection specification (State inferred from context)
- # if isinstance(state_spec, Projection):
-
- # # 2-item specification (State inferred from context)
- # if isinstance(state_spec, tuple):
- # _is_legal_state_spec_tuple(owner, state_spec)
- # tuple_spec = state_spec[1]
- # if isinstance(tuple_spec, State):
- # # InputState specified as 2nd item of tuple must be a destination, so choose State based on owner:
- # if isinstance(owner, ProcessingMechanism)
- # if isinstance(tuple_spec, InputState):
- # return OutputState
- # if isinstance(tuple_spec, OutputState):
- # return InputState
- # else:
- # # Call recursively to handle other types of specs
- # return _parse_state_type(owner, tuple_spec)
-
raise StateError("{} is not a legal State specification for {}".format(state_spec, owner.name))
@@ -2515,6 +2499,8 @@ def _parse_state_spec(state_type=None,
"""
from psyneulink.components.projections.projection \
import _is_projection_spec, _parse_projection_spec, _parse_connection_specs, ProjectionTuple
+ from psyneulink.components.states.modulatorysignals.modulatorysignal import _is_modulatory_spec
+
# Get all of the standard arguments passed from _instantiate_state (i.e., those other than state_spec) into a dict
standard_args = get_args(inspect.currentframe())
@@ -2581,6 +2567,9 @@ def _parse_state_spec(state_type=None,
if isinstance(state_specification, function_type):
state_specification = state_specification()
+ if _is_modulatory_spec(state_specification):
+ projection = state_type
+
# State or Mechanism object specification:
if isinstance(state_specification, (Mechanism, State)):
@@ -2615,19 +2604,40 @@ def _parse_state_spec(state_type=None,
Mechanism.__name__, state_owner.name))
return state_specification
- # State is not the same as connectee's type, so assume it is for one to connect with
- state_dict[PROJECTIONS] = ProjectionTuple(state=state_specification,
- weight=None,
- exponent=None,
- projection=projection)
+ # Re-process with Projection specified
+ state_dict = _parse_state_spec(state_type=state_type,
+ owner=owner,
+ variable=variable,
+ value=value,
+ reference_value=reference_value,
+ params=params,
+ prefs=prefs,
+ context=context,
+ state_spec=ProjectionTuple(state=state_specification,
+ weight=None,
+ exponent=None,
+ projection=projection))
+
+ # # State class
+ # elif (inspect.isclass(state_specification) and issubclass(state_specification, State)):
+ # # Specified type of State is same as connectee's type (state_type),
+ # # so assume it is a reference to the State itself to be instantiated
+ # if state_specification is not state_type:
+ # raise StateError("Specification of {} for {} (\'{}\') is insufficient to instantiate the {}".
+ # format(state_type_name, owner.name, state_specification.__name__, State.__name__))
+
+ # # MODIFIED 11/25/17 NEW:
+ # # State class
+ # if (inspect.isclass(state_specification) and issubclass(state_specification, State)):
+ # try:
+ # state_specification = (owner.paramClassDefaults[name], state_specification)
+ # except:
+ # pass
+ # else:
+ # state_dict = _parse_state_spec(state_spec=state_specification,
+ # **state_dict)
+ # # MODIFIED 11/25/17 END:
- # State class
- elif (inspect.isclass(state_specification) and issubclass(state_specification, State)):
- # Specified type of State is same as connectee's type (state_type),
- # so assume it is a reference to the State itself to be instantiated
- if state_specification is not state_type:
- raise StateError("Specification of {} for {} (\'{}\') is insufficient to instantiate the {}".
- format(state_type_name, owner.name, state_specification.__name__, State.__name__))
# Projection specification (class, object, or matrix value (matrix keyword processed below):
elif _is_projection_spec(state_specification, include_matrix_spec=False):
@@ -2709,7 +2719,6 @@ def _parse_state_spec(state_type=None,
elif is_value_spec(state_specification):
state_dict[REFERENCE_VALUE] = np.atleast_1d(state_specification)
-
elif isinstance(state_specification, Iterable) or state_specification is None:
# Standard state specification dict
@@ -2725,10 +2734,20 @@ def _parse_state_spec(state_type=None,
exponent=None,
projection=state_type)
- # FIX: HANDLE VALUE AS FIRST ITEM OF TUPLE HERE
- # State specification is a tuple, so let State subclass handle it
+ # State specification is a tuple
elif isinstance(state_specification, tuple):
+
+ # 1st item of tuple is a tuple (presumably a (State name, Mechanism) tuple),
+ # so parse to get specified State (any projection spec should be included as 4th item of outer tuple)
+ if isinstance(state_specification[0],tuple):
+ proj_spec = _parse_connection_specs(connectee_state_type=state_type,
+ owner=owner,
+ connections=state_specification[0])
+ state_specification = (proj_spec[0].state,) + state_specification[1:]
+
+ # Reassign tuple for handling by _parse_state_specific_specs
state_specific_specs = state_specification
+
# Otherwise, just pass params to State subclass
else:
state_specific_specs = params
@@ -2822,8 +2841,12 @@ def _parse_state_spec(state_type=None,
state_dict[PARAMS].update(params)
else:
- raise StateError("PROGRAM ERROR: state_spec for {} of {} is an unrecognized specification ({})".
+ if owner.verbosePref:
+ warnings.warn("PROGRAM ERROR: state_spec for {} of {} is an unrecognized specification ({})".
format(state_type_name, owner.name, state_spec))
+ return
+ # raise StateError("PROGRAM ERROR: state_spec for {} of {} is an unrecognized specification ({})".
+ # format(state_type_name, owner.name, state_spec))
# If variable is none, use value:
if state_dict[VARIABLE] is None:
@@ -2831,6 +2854,18 @@ def _parse_state_spec(state_type=None,
state_dict[VARIABLE] = state_dict[VALUE]
else:
state_dict[VARIABLE] = state_dict[REFERENCE_VALUE]
+ elif state_dict[REFERENCE_VALUE] is not None:
+ if not iscompatible(state_dict[VARIABLE], state_dict[REFERENCE_VALUE]):
+ if context is not None and context in '_parse_arg_input_states':
+ from psyneulink.components.mechanisms.mechanism import MechanismError
+ raise MechanismError('default variable determined from the specified input_states spec ({0}) '
+ 'is not compatible with the specified default variable ({1})'.
+ format(state_dict[VARIABLE], state_dict[REFERENCE_VALUE]))
+ else:
+ name = state_dict[NAME] or state_type.__name__
+ raise StateError("Specification of {} for {} of {} ({}) is not compatible with its {} ({})".
+ format(VARIABLE, name, owner.name,
+ state_dict[VARIABLE], REFERENCE_VALUE, state_dict[REFERENCE_VALUE]))
return state_dict
@@ -2887,24 +2922,46 @@ def _get_state_for_socket(owner,
# - a projection keyword (e.g., 'LEARNING' or 'CONTROL', and it is consistent with projection_socket
# Otherwise, return list of allowable State types for projection_socket (if state_spec is a Projection type)
if _is_projection_spec(state_spec):
+
+ # # MODIFIED 11/25/17 OLD:
+ # # These specifications require that a particular State be specified to assign its default Projection type
+ # if ((is_matrix(state_spec) or (isinstance(state_spec, dict) and not PROJECTION_TYPE in state_spec))
+ # and state_type is 'MULTIPLE'):
+ # raise StateError("PROGRAM ERROR: Projection specified ({}) for object "
+ # "that has multiple possible States {}) for the specified socket ({}).".
+ # format(state_spec, state_types, projection_socket))
+ # proj_spec = _parse_projection_spec(state_spec, owner=owner, state_type=state_type)
+ # if isinstance(proj_spec, Projection):
+ # proj_type = proj_spec.__class__
+ # else:
+ # proj_type = proj_spec[PROJECTION_TYPE]
+ # MODIFIED 11/25/17 NEW:
# These specifications require that a particular State be specified to assign its default Projection type
- if ((is_matrix(state_spec) or (isinstance(state_spec, dict) and not PROJECTION_TYPE in state_spec))
- and state_type is 'MULTIPLE'):
- raise StateError("PROGRAM ERROR: Projection specified ({}) for object "
- "that has multiple possible States {}) for the specified socket ({}).".
- format(state_spec, state_types, projection_socket))
- proj_spec = _parse_projection_spec(state_spec, owner=owner, state_type=state_type)
- if isinstance(proj_spec, Projection):
- proj_type = proj_spec.__class__
+ if ((is_matrix(state_spec) or (isinstance(state_spec, dict) and not PROJECTION_TYPE in state_spec))):
+ for st in state_types:
+ try:
+ proj_spec = _parse_projection_spec(state_spec, owner=owner, state_type=st)
+ if isinstance(proj_spec, Projection):
+ proj_type = proj_spec.__class__
+ else:
+ proj_type = proj_spec[PROJECTION_TYPE]
+ except:
+ continue
else:
- proj_type = proj_spec[PROJECTION_TYPE]
+ proj_spec = _parse_projection_spec(state_spec, owner=owner, state_type=state_type)
+ if isinstance(proj_spec, Projection):
+ proj_type = proj_spec.__class__
+ else:
+ proj_type = proj_spec[PROJECTION_TYPE]
+ # MODIFIED 11/25/17 END:
# Get State type if it is appropriate for the specified socket of the Projection's type
s = next((s for s in state_types if s.__name__ in getattr(proj_type.sockets, projection_socket)), None)
if s:
try:
# Return State associated with projection_socket if proj_spec is an actual Projection
- return getattr(proj_spec, projection_socket)
+ state = getattr(proj_spec, projection_socket)
+ return state
except AttributeError:
# Otherwise, return first state_type (s)
return s
diff --git a/psyneulink/components/system.py b/psyneulink/components/system.py
index dc834f0f838..cb448c590cf 100644
--- a/psyneulink/components/system.py
+++ b/psyneulink/components/system.py
@@ -452,7 +452,7 @@
__all__ = [
'CONTROL_MECHANISM', 'CONTROL_PROJECTION_RECEIVERS', 'defaultInstanceCount', 'INPUT_ARRAY', 'kwSystemInputState',
- 'LEARNING_MECHANISMS', 'LEARNING_PROJECTION_RECEIVERS', 'MECHANISMS', 'NUM_PHASES_PER_TRIAL', 'ORIGIN_MECHANISMS',
+ 'LEARNING_MECHANISMS', 'LEARNING_PROJECTION_RECEIVERS', 'MECHANISMS', 'MonitoredOutputStateTuple', 'NUM_PHASES_PER_TRIAL', 'ORIGIN_MECHANISMS',
'OUTPUT_STATE_NAMES', 'OUTPUT_VALUE_ARRAY', 'PROCESSES', 'RECURRENT_INIT_ARRAY', 'RECURRENT_MECHANISMS', 'SCHEDULER',
'System', 'SYSTEM_TARGET_INPUT_STATE', 'SystemError', 'SystemInputState', 'SystemRegistry',
'SystemWarning', 'TARGET_MECHANISMS', 'TERMINAL_MECHANISMS',
@@ -502,7 +502,7 @@ class MonitoredOutputStatesOption(AutoNumber):
WEIGHT_INDEX = 1
EXPONENT_INDEX = 2
MATRIX_INDEX = 3
-MonitoredOutputStateTuple = namedtuple("MonitoredOutputStateTuple", "output_state, weight exponent matrix")
+MonitoredOutputStateTuple = namedtuple("MonitoredOutputStateTuple", "output_state weight exponent matrix")
class SystemWarning(Warning):
diff --git a/psyneulink/globals/keywords.py b/psyneulink/globals/keywords.py
index 4dea94986e4..bf4f9783ecf 100644
--- a/psyneulink/globals/keywords.py
+++ b/psyneulink/globals/keywords.py
@@ -681,6 +681,10 @@ def _is_metric(metric):
GATING_PROJECTIONS = 'GatingProjections'
GATING_POLICY = 'gating_policy'
+MODULATORY_SPEC_KEYWORDS = {LEARNING, LEARNING_SIGNAL, LEARNING_PROJECTION,
+ CONTROL, CONTROL_SIGNAL, CONTROL_PROJECTION,
+ GATING, GATING_SIGNAL, GATING_PROJECTION}
+
#endregion
#region ---------------------------------------------- STATES ------------------------------------------------------
diff --git a/psyneulink/globals/utilities.py b/psyneulink/globals/utilities.py
index ea67b44d0aa..cf06e9585d6 100644
--- a/psyneulink/globals/utilities.py
+++ b/psyneulink/globals/utilities.py
@@ -206,16 +206,22 @@ def parameter_spec(param):
from psyneulink.components.functions.function import function_type
from psyneulink.components.shellclasses import Projection
from psyneulink.components.component import parameter_keywords
+ from psyneulink.globals.keywords import MODULATORY_SPEC_KEYWORDS
+ from psyneulink.components.component import Component
+ if inspect.isclass(param):
+ param = param.__name__
+ elif isinstance(param, Component):
+ param = param.__class__.__name__
if (isinstance(param, (numbers.Number,
np.ndarray,
list,
tuple,
dict,
function_type,
- Projection)) or
- (inspect.isclass(param) and issubclass(param, Projection)) or
- param in parameter_keywords):
+ Projection))
+ or param in MODULATORY_SPEC_KEYWORDS
+ or param in parameter_keywords):
return True
return False
@@ -851,18 +857,18 @@ def __setitem__(self, key, value):
except TypeError:
# It must be a string
if not isinstance(key, str):
- raise UtilitiesError("Non-numeric key used for {} ({})must be a string)".
- format(self.name, key))
+ raise UtilitiesError("Non-numeric key used for {} ({}) must be "
+ "a string)".format(self.name, key))
# The specified string must also match the value of the attribute of the class used for addressing
if not key == value.name:
# if not key == type(value).__name__:
raise UtilitiesError("The key of the entry for {} {} ({}) "
- "must match the value of its {} attribute ({})".
- format(self.name,
- value.__class__.__name__,
- key,
- self.key,
- getattr(value, self.key)))
+ "must match the value of its {} attribute "
+ "({})".format(self.name,
+ value.__class__.__name__,
+ key,
+ self.key,
+ getattr(value, self.key)))
key_num = self._get_key_for_item(key)
if key_num is not None:
self.data[key_num] = value
@@ -885,8 +891,9 @@ def _get_key_for_item(self, key):
elif isinstance(key, self.component_type):
return self.data.index(key)
else:
- raise UtilitiesError("{} is not a legal key for {} (must be number, string or State)".
- format(key, self.key))
+ raise UtilitiesError("{} is not a legal key for {} (must be "
+ "number, string or State)".format(key,
+ self.key))
def __delitem__(self, key):
if key is None:
diff --git a/psyneulink/library/mechanisms/adaptive/control/lcmechanism.py b/psyneulink/library/mechanisms/adaptive/control/lcmechanism.py
index 9cf15866d09..b13e26d220a 100644
--- a/psyneulink/library/mechanisms/adaptive/control/lcmechanism.py
+++ b/psyneulink/library/mechanisms/adaptive/control/lcmechanism.py
@@ -10,16 +10,6 @@
"""
-.. note::
- **THIS MECHANISM IS ONLY PARTIALLY IMPLEMENTED.**
-
- IT CAN MODULATE MECHANISMS, BUT:
-
- - IT DOES NOT YET AUTOMATICALLY GENERATE A `UtilityIntegrator` AS ITS OBJECTIVE MECHANISM
- ..
- - THE `FitzHughNagumoIntegration` FUNCTION AND ASSOCIATED `mode` PARAMETER HAVE NOT YET BEEN IMPLEMENTED
-
-
Overview
--------
@@ -162,17 +152,34 @@
The following example generates an LCMechanism that modulates the function of two TransferMechanisms, one that uses
a `Linear` function and the other a `Logistic` function::
- my_mech_1 = TransferMechanism(function=Linear,
- name='my_linear_mechanism')
- my_mech_2 = TransferMechanism(function=Logistic,
- name='my_logistic_mechanism')
+ >>> import psyneulink as pnl
+ >>> my_mech_1 = pnl.TransferMechanism(function=pnl.Linear,
+ ... name='my_linear_mechanism')
+ >>> my_mech_2 = pnl.TransferMechanism(function=pnl.Logistic,
+ ... name='my_logistic_mechanism')
- LC = LCMechanism(modulated_mechanisms=[my_mech_1, my_mech_2],
- name='my_LC')
+ >>> LC = pnl.LCMechanism(modulated_mechanisms=[my_mech_1, my_mech_2],
+ ... name='my_LC')
-Calling `my_LC.show()` generates the following report::
+COMMENT:
+# Calling `LC.show()` generates the following report::
+#
+# >>> LC.show()
+#
+# ---------------------------------------------------------
+#
+# my_LC
+#
+# Monitoring the following Mechanism OutputStates:
+# None
+#
+# Modulating the following parameters:
+# my_linear_mechanism: slope
+# my_logistic_mechanism: gain
+#
+# ---------------------------------------------------------
+COMMENT
- my_LC
COMMENT:
Monitoring the following Mechanism OutputStates:
None
@@ -226,12 +233,18 @@
"""
import typecheck as tc
-from psyneulink.components.functions.function import Integrator, MULTIPLICATIVE_PARAM, ModulationParam, _is_modulation_param
-from psyneulink.components.mechanisms.adaptive.adaptivemechanism import AdaptiveMechanism_Base
-from psyneulink.components.mechanisms.adaptive.control.controlmechanism import ControlMechanism
+from psyneulink.components.functions.function import Integrator, \
+ MULTIPLICATIVE_PARAM, ModulationParam, _is_modulation_param
+from psyneulink.components.mechanisms.adaptive.adaptivemechanism import \
+ AdaptiveMechanism_Base
+from psyneulink.components.mechanisms.adaptive.control.controlmechanism import \
+ ControlMechanism
+from psyneulink.components.projections.modulatory.controlprojection import \
+ ControlProjection
from psyneulink.components.shellclasses import Mechanism
from psyneulink.globals.defaults import defaultControlAllocation
-from psyneulink.globals.keywords import ALL, CONTROL_PROJECTIONS, CONTROL_SIGNALS, FUNCTION, INIT__EXECUTE__METHOD_ONLY, INPUT_STATES
+from psyneulink.globals.keywords import ALL, CONTROL_PROJECTIONS, \
+ CONTROL_SIGNALS, FUNCTION, INIT__EXECUTE__METHOD_ONLY, INPUT_STATES
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.scheduling.timescale import CentralClock, TimeScale
@@ -245,6 +258,7 @@
ControlMechanismRegistry = {}
+
class LCMechanismError(Exception):
def __init__(self, error_value):
self.error_value = error_value
@@ -376,7 +390,7 @@ class ClassDefaults(AdaptiveMechanism_Base.ClassDefaults):
variable = defaultControlAllocation
paramClassDefaults = ControlMechanism.paramClassDefaults.copy()
- paramClassDefaults.update({FUNCTION:Integrator,
+ paramClassDefaults.update({FUNCTION: Integrator,
CONTROL_SIGNALS: None,
CONTROL_PROJECTIONS: None,
})
@@ -385,7 +399,7 @@ class ClassDefaults(AdaptiveMechanism_Base.ClassDefaults):
def __init__(self,
default_variable=None,
size=None,
- monitor_for_control:tc.optional(list)=None,
+ # monitor_for_control:tc.optional(list)=None,
mode:tc.optional(float)=0.0,
modulated_mechanisms:tc.optional(tc.any(list,str)) = None,
modulation:tc.optional(_is_modulation_param)=ModulationParam.MULTIPLICATIVE,
@@ -401,7 +415,7 @@ def __init__(self,
super().__init__(default_variable=default_variable,
size=size,
- monitor_for_control=monitor_for_control,
+ # monitor_for_control=monitor_for_control,
modulation=modulation,
params=params,
name=name,
@@ -446,10 +460,12 @@ def _validate_params(self, request_set, target_set=None, context=None):
spec = target_set[MODULATED_MECHANISMS]
- if isinstance (spec, str):
+ if isinstance(spec, str):
if not spec == ALL:
- raise LCMechanismError("A string other than the keyword \'ALL\' was specified for the {} argument "
- "the constructor for {}".format(MODULATED_MECHANISMS, self.name))
+ raise LCMechanismError("A string other than the keyword "
+ "\'ALL\' was specified for the {} "
+ "argument the constructor for {}".format(MODULATED_MECHANISMS,
+ self.name))
if not isinstance(spec, list):
spec = [spec]
@@ -480,8 +496,7 @@ def _instantiate_input_states(self, context=None):
method should be implemented that also implements an _instantiate_monitored_output_states
method, and that can be used to add OutputStates/Mechanisms to be monitored.
"""
-
- self.monitored_output_states = []
+ self._monitored_output_states = []
if not hasattr(self, INPUT_STATES):
self._input_states = None
@@ -490,8 +505,6 @@ def _instantiate_input_states(self, context=None):
for projection in input_state.path_afferents:
self.monitored_output_states.append(projection.sender)
-
-
def _instantiate_output_states(self, context=None):
"""Instantiate ControlSignal and assign ControlProjections to Mechanisms in self.modulated_mechanisms
@@ -628,10 +641,10 @@ def show(self):
print ("\n{0}".format(self.name))
print("\n\tMonitoring the following Mechanism OutputStates:")
- if self.monitoring_mechanism is None:
+ if self.monitored_output_states is None:
print ("\t\tNone")
else:
- for state in self.monitoring_mechanism.input_states:
+ for state in self.monitored_output_states:
for projection in state.path_afferents:
monitored_state = projection.sender
monitored_state_mech = projection.sender.owner
diff --git a/psyneulink/library/mechanisms/processing/integrator/ddm.py b/psyneulink/library/mechanisms/processing/integrator/ddm.py
index 3192af2a3b8..9347ecd7aee 100644
--- a/psyneulink/library/mechanisms/processing/integrator/ddm.py
+++ b/psyneulink/library/mechanisms/processing/integrator/ddm.py
@@ -36,11 +36,12 @@
specifying DDM as its **mech_spec** argument. The model implementation is selected using the `function `
argument. The function selection can be simply the name of a DDM function::
- my_DDM = DDM(function=BogaczEtAl)
+ >>> import psyneulink as pnl
+ >>> my_DDM = pnl.DDM(function=pnl.BogaczEtAl)
or a call to the function with arguments specifying its parameters::
- my_DDM = DDM(function=BogaczEtAl(drift_rate=0.2, threshold=1.0))
+ >>> my_DDM = pnl.DDM(function=pnl.BogaczEtAl(drift_rate=0.2, threshold=1.0))
COMMENT:
@@ -124,21 +125,24 @@
`BogaczEtAl ` Function::
- my_DDM_BogaczEtAl = DDM(function=BogaczEtAl(drift_rate=3.0,
- starting_point=1.0,
- threshold=30.0,
- noise=1.5,
- t0 = 2.0),
- name='my_DDM_BogaczEtAl')
+ >>> my_DDM_BogaczEtAl = pnl.DDM(function=pnl.BogaczEtAl(drift_rate=3.0,
+ ... starting_point=1.0,
+ ... threshold=30.0,
+ ... noise=1.5,
+ ... t0 = 2.0),
+ ... name='my_DDM_BogaczEtAl')
-`NavarroAndFuss ` Function::
+`NavarroAndFuss ` Function (requires MATLAB engine)::
- my_DDM_NavarroAndFuss = DDM(function=NavarroAndFuss(drift_rate=3.0,
- starting_point=1.0,
- threshold=30.0,
- noise=1.5,
- t0 = 2.0),
- name='my_DDM_NavarroAndFuss')
+ >>> import matlab.engine # doctest: +SKIP
+ >>> self.eng1 = matlab.engine.start_matlab('-nojvm') # doctest: +SKIP
+
+ >>> my_DDM_NavarroAndFuss = pnl.DDM(function=pnl.NavarroAndFuss(drift_rate=3.0, # doctest: +SKIP
+ ... starting_point=1.0, # doctest: +SKIP
+ ... threshold=30.0, # doctest: +SKIP
+ ... noise=1.5, # doctest: +SKIP
+ ... t0 = 2.0), # doctest: +SKIP
+ ... name='my_DDM_NavarroAndFuss') # doctest: +SKIP
.. _DDM_Integration_Mode:
@@ -152,11 +156,11 @@
`Integrator ` Function::
- my_DDM_path_integrator = DDM(function=DriftDiffusionIntegrator(noise=0.5,
- initializer = 1.0,
- t0 = 2.0,
- rate = 3.0),
- name='my_DDM_path_integrator')
+ >>> my_DDM_path_integrator = pnl.DDM(function=pnl.DriftDiffusionIntegrator(noise=0.5,
+ ... initializer=1.0,
+ ... t0=2.0,
+ ... rate=3.0),
+ ... name='my_DDM_path_integrator')
COMMENT:
[TBI - MULTIPROCESS DDM - REPLACE ABOVE]
@@ -296,7 +300,7 @@
from psyneulink.components.functions.function import BogaczEtAl, DriftDiffusionIntegrator, Integrator, NF_Results, NavarroAndFuss, STARTING_POINT, THRESHOLD
from psyneulink.components.mechanisms.mechanism import Mechanism_Base
from psyneulink.components.mechanisms.processing.processingmechanism import ProcessingMechanism_Base
-from psyneulink.components.states.outputstate import SEQUENTIAL
+from psyneulink.components.states.outputstate import StandardOutputStates, SEQUENTIAL
from psyneulink.globals.keywords import FUNCTION, FUNCTION_PARAMS, INITIALIZING, NAME, OUTPUT_STATES, TIME_SCALE, kwPreferenceSetName
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set, kpReportOutputPref
from psyneulink.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel
@@ -636,6 +640,10 @@ def __init__(self,
context=componentType + INITIALIZING
):
+ self.standard_output_states = StandardOutputStates(self,
+ DDM_standard_output_states,
+ indices=SEQUENTIAL)
+
# Default output_states is specified in constructor as a tuple rather than a list
# to avoid "gotcha" associated with mutable default arguments
# (see: bit.ly/2uID3s3 and http://docs.python-guide.org/en/latest/writing/gotchas/)
@@ -660,9 +668,6 @@ def __init__(self,
# self.size = size
self.threshold = thresh
- from psyneulink.components.states.outputstate import StandardOutputStates
- self.standard_output_states = StandardOutputStates(self, DDM_standard_output_states, SEQUENTIAL)
-
super(DDM, self).__init__(variable=default_variable,
output_states=output_states,
params=params,
diff --git a/psyneulink/library/mechanisms/processing/objective/comparatormechanism.py b/psyneulink/library/mechanisms/processing/objective/comparatormechanism.py
index fb7b76f6eaf..6dd4183f5dd 100644
--- a/psyneulink/library/mechanisms/processing/objective/comparatormechanism.py
+++ b/psyneulink/library/mechanisms/processing/objective/comparatormechanism.py
@@ -87,7 +87,7 @@
*Formatting InputState values*
-The **variable** argument can be used to specify a particular format for the SAMPLE and/or TARGET InputStates
+The **default_variable** argument can be used to specify a particular format for the SAMPLE and/or TARGET InputStates
of a ComparatorMechanism. This can be useful when one or both of these differ from the format of the
OutputState(s) specified in the **sample** and **target** arguments. For example, for `Reinforcement Learning
`, a ComparatorMechanism is used to monitor an action selection Mechanism (the sample), and compare
@@ -97,25 +97,27 @@
specifying it as the ComparatorMechanism's **sample** argument will generate a corresponding InputState with a vector
as its value. This will not match the reward signal specified in the ComparatorMechanism's **target** argument, the
value of which is a single scalar. This can be dealt with by explicitly specifying the format for the SAMPLE and
-TARGET InputStates in the **variable** argument of the ComparatorMechanism's constructor, as follows::
+TARGET InputStates in the **default_variable** argument of the ComparatorMechanism's constructor, as follows::
- my_action_selection_mech = TransferMechanism(size=5,
- function=SoftMax(output=PROB))
+ >>> import psyneulink as pnl
+ >>> my_action_selection_mech = pnl.TransferMechanism(size=5,
+ ... function=pnl.SoftMax(output=pnl.PROB))
- my_reward_mech = TransferMechanism(default_variable = [0])
+ >>> my_reward_mech = pnl.TransferMechanism()
- my_comparator_mech = ComparatorMechanism(sample=my_action_selection_mech,
- target=my_reward_mech,
- variable = [[0],[0]])
+ >>> my_comparator_mech = pnl.ComparatorMechanism(default_variable = [[0],[0]],
+ ... sample=my_action_selection_mech,
+ ... target=my_reward_mech)
Note that ``my_action_selection_mechanism`` is specified to take an array of length 5 as its input, and therefore
generate one of the same length as its `primary output `. Since it is assigned as the **sample**
of the ComparatorMechanism, by default this will create a *SAMPLE* InputState of length 5, that will not match the
-length of the *TARGET* InputState (which is 1). This is taken care of, by specifying the **variable** argument
-as an array with two single-value arrays (corresponding to the *SAMPLE* and *TARGET* InputStates). (In this
-example, the **sample** and **target** arguments are specified as Mechanisms since, by default, each has only a single
-(`primary `) OutputState, that will be used; if either had more than one OutputState, and
-one of those was desired, it would have had to be specified explicitly in the **sample** or **target** argument).
+length of the *TARGET* InputState (the default for which is length 1). This is taken care of, by specifying the
+**default_variable** argument as an array with two single-value arrays (corresponding to the *SAMPLE* and *TARGET*
+InputStates). (In this example, the **sample** and **target** arguments are specified as Mechanisms since,
+by default, each has only a single (`primary `) OutputState, that will be used; if either had
+more than one OutputState, and one of those was desired, it would have had to be specified explicitly in the
+**sample** or **target** argument).
.. _ComparatorMechanism_Class_Reference:
@@ -314,16 +316,17 @@ class ClassDefaults(ObjectiveMechanism.ClassDefaults):
paramClassDefaults.update({TIME_SCALE: TimeScale.TRIAL})
standard_output_states = ObjectiveMechanism.standard_output_states.copy()
- standard_output_states.extend([{NAME:SSE,
- CALCULATE:lambda x: np.sum(x*x)},
- {NAME:MSE,
- CALCULATE:lambda x: np.sum(x*x)/len(x)}])
+ standard_output_states.extend([{NAME: SSE,
+ CALCULATE: lambda x: np.sum(x*x)},
+ {NAME: MSE,
+ CALCULATE: lambda x: np.sum(x*x)/len(x)}])
# MODIFIED 10/10/17 OLD:
@tc.typecheck
def __init__(self,
- sample:tc.optional(tc.any(OutputState, Mechanism_Base, dict, is_numeric, str))=None,
- target:tc.optional(tc.any(OutputState, Mechanism_Base, dict, is_numeric, str))=None,
+ default_variable=None,
+ sample: tc.optional(tc.any(OutputState, Mechanism_Base, dict, is_numeric, str))=None,
+ target: tc.optional(tc.any(OutputState, Mechanism_Base, dict, is_numeric, str))=None,
function=LinearCombination(weights=[[-1], [1]]),
output_states:tc.optional(tc.any(str, Iterable))=(OUTCOME, MSE),
params=None,
@@ -333,7 +336,7 @@ def __init__(self,
**input_states # IMPLEMENTATION NOTE: this is for backward compatibility
):
- input_states = self._merge_legacy_constructor_args(sample, target, input_states)
+ input_states = self._merge_legacy_constructor_args(sample, target, default_variable, input_states)
# Default output_states is specified in constructor as a tuple rather than a list
# to avoid "gotcha" associated with mutable default arguments
@@ -352,7 +355,7 @@ def __init__(self,
indices=PRIMARY)
super().__init__(# monitored_output_states=[sample, target],
- monitored_output_states = input_states,
+ monitored_output_states=input_states,
function=function,
output_states=output_states.copy(), # prevent default from getting overwritten by later assign
params=params,
@@ -436,7 +439,7 @@ def _validate_params(self, request_set, target_set=None, context=None):
target_set=target_set,
context=context)
- def _merge_legacy_constructor_args(self, sample, target, input_states):
+ def _merge_legacy_constructor_args(self, sample, target, default_variable=None, input_states=None):
# USE sample and target TO CREATE AN InputState specfication dictionary for each;
# DO SAME FOR InputStates argument, USE TO OVERWRITE ANY SPECIFICATIONS IN sample AND target DICTS
@@ -452,12 +455,24 @@ def _merge_legacy_constructor_args(self, sample, target, input_states):
state_spec=target,
name=TARGET)
- # If input_states arg is provided, parse it and use it to upate sample and target dicts
+ # If either the default_variable arg or the input_states arg is provided:
+ # - validate that there are exactly two items in default_variable or input_states list
+ # - if there is an input_states list, parse it and use it to update sample and target dicts
if input_states:
-
- if len(input_states) != 2:
- raise ComparatorMechanismError("If an \'input_states\' arg is included in the constructor for a {}"
- "it must be a list with exactly two items (not {})".
+ input_states[INPUT_STATES]
+ if not isinstance(input_states, list):
+ raise ComparatorMechanismError("If an \'{}\' argument is included in the constructor for a {} "
+ "it must be a list with two {} specifications.".
+ format(INPUT_STATES, ComparatorMechanism.__name__, InputState.__name__))
+
+ input_states = input_states or default_variable
+
+ if input_states is not None:
+ if len(input_states)!=2:
+ raise ComparatorMechanismError("If an \'input_states\' arg is "
+ "included in the constructor for "
+ "a {}, it must be a list with "
+ "exactly two items (not {})".
format(ComparatorMechanism.__name__, len(input_states)))
sample_input_state_dict = _parse_state_spec(owner=self,
diff --git a/psyneulink/library/subsystems/agt/lccontrolmechanism.py b/psyneulink/library/subsystems/agt/lccontrolmechanism.py
index b2279293b39..07bda2e81b4 100644
--- a/psyneulink/library/subsystems/agt/lccontrolmechanism.py
+++ b/psyneulink/library/subsystems/agt/lccontrolmechanism.py
@@ -178,7 +178,9 @@
Function
~~~~~~~~
+COMMENT:
XXX ADD MENTION OF allocation_policy HERE
+COMMENT
An LCControlMechanism uses the `FHNIntegrator` as its `function `_ often used to describe the spiking of a neuron,
@@ -263,25 +265,44 @@
The following example generates an LCControlMechanism that modulates the function of two TransferMechanisms, one that uses
a `Linear` function and the other a `Logistic` function::
- my_mech_1 = TransferMechanism(function=Linear,
- name='my_linear_mechanism')
- my_mech_2 = TransferMechanism(function=Logistic,
- name='my_logistic_mechanism')
-
- LC = LCControlMechanism(modulated_mechanisms=[my_mech_1, my_mech_2],
- name='my_LC')
+ >>> import psyneulink as pnl
+ >>> my_mech_1 = pnl.TransferMechanism(function=pnl.Linear,
+ ... name='my_linear_mechanism')
+ >>> my_mech_2 = pnl.TransferMechanism(function=pnl.Logistic,
+ ... name='my_logistic_mechanism')
-Calling `my_LC.show()` generates the following report::
+ >>> LC = LCControlMechanism(modulated_mechanisms=[my_mech_1, my_mech_2],
+ ... name='my_LC')
- my_LC
COMMENT:
- Monitoring the following Mechanism OutputStates:
- None
+# Calling `LC.show()` generates the following report::
+#
+# >>> LC.show()
+#
+# ---------------------------------------------------------
+#
+# my_LC
+#
+# Monitoring the following Mechanism OutputStates:
+#
+# Modulating the following parameters:
+# my_logistic_mechanism: gain
+# my_linear_mechanism: slope
+#
+# ---------------------------------------------------------
COMMENT
- Modulating the following Mechanism parameters:
- my_logistic_mechanism: gain
- my_linear_mechanism: slope
+Calling `LC.show()` generates the following report::
+
+ my_LC
+
+ Monitoring the following Mechanism OutputStates:
+
+ Modulating the following parameters:
+ my_logistic_mechanism: gain
+ my_linear_mechanism: slope
+
+
Note that the LCControlMechanism controls the `multiplicative_param ` of the `function
` of each Mechanism: the `gain ` parameter for ``my_mech_1``, since it uses
@@ -742,12 +763,12 @@ def show(self):
and the `multiplicative_params ` modulated by the LCControlMechanism.
"""
- print ("\n---------------------------------------------------------")
+ print("\n---------------------------------------------------------")
- print ("\n{0}".format(self.name))
+ print("\n{0}".format(self.name))
print("\n\tMonitoring the following Mechanism OutputStates:")
if self.objective_mechanism is None:
- print ("\t\tNone")
+ print("\t\tNone")
else:
for state in self.objective_mechanism.input_states:
for projection in state.path_afferents:
diff --git a/psyneulink/library/subsystems/evc/evccontrolmechanism.py b/psyneulink/library/subsystems/evc/evccontrolmechanism.py
index 93f166b4c49..fe95247f935 100644
--- a/psyneulink/library/subsystems/evc/evccontrolmechanism.py
+++ b/psyneulink/library/subsystems/evc/evccontrolmechanism.py
@@ -99,25 +99,25 @@
Like any ControlMechanism, an EVCControlMechanism receives its input from the *OUTCOME* `OutputState
` of an `ObjectiveMechanism`, via a MappingProjection to its `primary InputState
-`. The ObjectiveFunction is listed in the EVCControlMechanism's `objective_mechanism
+`. The ObjectiveFunction is listed in the EVCControlMechanism's `objective_mechanism
` attribute. By default, the ObjectiveMechanism's function is a `LinearCombination`
-function with its `operation ` attribute assigned as *PRODUCT*, which takes the product of
+function with its `operation ` attribute assigned as *PRODUCT*; this takes the product of
the `value `\\s of the OutputStates that it monitors (listed in its `monitored_output_states
` attribute. However, this can be customized in a variety of ways:
* by specifying a different `function ` for the ObjectiveMechanism
- (see `ObjectiveMechanism_Weights_and_Exponents_Example` for an example);
+ (see `Objective Mechanism Examples ` for an example);
..
* using a list to specify the OutputStates to be monitored (and the `tuples format
` to specify weights and/or exponents for them) in the
**objective_mechanism** argument of the EVCControlMechanism's constructor;
..
- * using the **monitored_output_states** argument of the `objective_mechanism `'s
- constructor;
+ * using the **monitored_output_states** argument for an ObjectiveMechanism specified in the `objective_mechanism
+ ` argument of the EVCMechanism's constructor;
..
- * specifying a different `ObjectiveMechanism` in the EVCControlMechanism's **objective_mechanism** argument of the
- EVCControlMechanism's constructor. The result of the `objective_mechanism `'s
- `function ` is used as the outcome in the calculations described below.
+ * specifying a different `ObjectiveMechanism` in the **objective_mechanism** argument of the EVCControlMechanism's
+ constructor. The result of the `objective_mechanism `'s `function
+ ` is used as the outcome in the calculations described below.
.. _EVCControlMechanism_Objective_Mechanism_Function_Note:
@@ -185,7 +185,7 @@
The default `function ` of an EVCControlMechanism is `ControlSignalGridSearch`. It identifies the
`allocation_policy` with the maximum `EVC ` by a conducting an exhaustive search over every possible
-`allocation_policy`— that is, all combinations of `allocation ` values for its `ControlSignal
+`allocation_policy`— that is, all combinations of `allocation ` values for its `ControlSignals
`, where the `allocation ` values sampled for each ControlSignal
are determined by its `allocation_samples` attribute. For each `allocation_policy`, the EVCControlMechanism executes the
`system `, evaluates the `EVC ` for that policy, and returns the
@@ -203,23 +203,24 @@
` attribute (and the `objective_mechanism
`'s `monitored_output_states ` attribute)
by taking their elementwise (Hadamard) product. However, this behavior can be customized in a variety of ways,
- as described `above EVCControlMechanism_ObjectiveMechanism`.
-
+ as described `above `.
+ |
* **Calculate EVC** - call the EVCControlMechanism's `value_function ` passing it the
outcome (received from the `objective_mechanism`) and a list of the `costs ` \\s of its
- `ControlSignals `; the default `value_function ` calls
- two additional auxiliary functions, in the following order:
-
+ `ControlSignals `. The default `value_function
+ ` calls two additional auxiliary functions, in the following order:
+ |
- `cost_function `, which sums the costs; this can be configured to weight and/or
exponentiate individual costs (see `cost_function ` attribute);
-
+ |
- `combine_outcome_and_cost_function `, which subtracts the sum of
the costs from the outcome to generate the EVC; this too can be configured (see
`combine_outcome_and_cost_function `).
In addition to modifying the default functions (as noted above), any or all of them can be replaced with a custom
function to modify how the `allocation_policy ` is determined, so long as the custom
-function accepts arguments and return values that are compatible with any that call that function (see note below).
+function accepts arguments and returns values that are compatible with any other functions that call that function (see
+note below).
.. _EVCControlMechanism_Calling_and_Assigning_Functions:
@@ -293,11 +294,17 @@
The following example implements a System with an EVCControlMechanism (and two processes not shown)::
- mySystem = System(processes=[myRewardProcess, myDecisionProcess],
- controller=EVCControlMechanism,
- monitor_for_control=[Reward, DDM_DECISION_VARIABLE,(RESPONSE_TIME, 1, -1)],
-It uses the System's `monitor_for_control` argument to assign three OutputStates to be monitored. The first one
+ >>> import psyneulink as pnl #doctest: +SKIP
+ >>> myRewardProcess = pnl.Process(...) #doctest: +SKIP
+ >>> myDecisionProcess = pnl.Process(...) #doctest: +SKIP
+ >>> mySystem = pnl.System(processes=[myRewardProcess, myDecisionProcess], #doctest: +SKIP
+ ... controller=pnl.EVCControlMechanism, #doctest: +SKIP
+ ... monitor_for_control=[Reward, #doctest: +SKIP
+ ... pnl.DDM_OUTPUT.DECISION_VARIABLE, #doctest: +SKIP
+ ... (pnl.RESPONSE_TIME, 1, -1)], #doctest: +SKIP
+
+It uses the System's **monitor_for_control** argument to assign three OutputStates to be monitored. The first one
references the Reward Mechanism (not shown); its `primary OutputState ` will be used by default.
The second and third use keywords that are the names of outputStates of a `DDM` Mechanism (also not shown).
The last one (RESPONSE_TIME) is assigned a weight of 1 and an exponent of -1. As a result, each calculation of the EVC
diff --git a/psyneulink/scheduling/scheduler.py b/psyneulink/scheduling/scheduler.py
index 0c629ca8298..6fdfd42a4f0 100644
--- a/psyneulink/scheduling/scheduler.py
+++ b/psyneulink/scheduling/scheduler.py
@@ -132,10 +132,10 @@
C
↗ ↖
- A B
+ A B
- scheduler.add_condition(B, EveryNCalls(A, 2))
- scheduler.add_condition(C, EveryNCalls(B, 1))
+ scheduler.add_condition(B, pnl.scheduling.condition.EveryNCalls(A, 2))
+ scheduler.add_condition(C, pnl.scheduling.condition.EveryNCalls(B, 1))
time steps: [{A}, {A, B}, {C}, ...]
@@ -189,80 +189,95 @@
* Basic phasing in a linear process::
- A = TransferMechanism(function=Linear(), name='A')
- B = TransferMechanism(function=Linear(), name='B')
- C = TransferMechanism(function=Linear(), name='C')
+ >>> import psyneulink as pnl
- p = Process(
- pathway=[A, B, C],
- name = 'p'
- )
- s = System(
- processes=[p],
- name='s'
- )
- my_scheduler = Scheduler(system=s)
+ >>> A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+ >>> B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+ >>> C = pnl.TransferMechanism(function=pnl.Linear(), name='C')
- #impicit condition of Always for A
- my_scheduler.add_condition(B, EveryNCalls(A, 2))
- my_scheduler.add_condition(C, EveryNCalls(B, 3))
+ >>> p = pnl.Process(
+ ... pathway=[A, B, C],
+ ... name = 'p'
+ ... )
+ >>> s = pnl.System(
+ ... processes=[p],
+ ... name='s'
+ ... )
+ >>> my_scheduler = pnl.Scheduler(system=s)
- # implicit AllHaveRun Termination condition
- execution_sequence = list(my_scheduler.run())
+ >>> # implicit condition of Always for A
+ >>> my_scheduler.add_condition(B, pnl.scheduling.condition.EveryNCalls(A, 2))
+ >>> my_scheduler.add_condition(C, pnl.scheduling.condition.EveryNCalls(B, 3))
- execution_sequence: [A, A, B, A, A, B, A, A, B, C]
+ >>> # implicit AllHaveRun Termination condition
+ >>> execution_sequence = list(my_scheduler.run())
+ >>> execution_sequence
+ [{(TransferMechanism A)}, {(TransferMechanism A)}, {(TransferMechanism B)}, {(TransferMechanism A)}, {(TransferMechanism A)}, {(TransferMechanism B)}, {(TransferMechanism A)}, {(TransferMechanism A)}, {(TransferMechanism B)}, {(TransferMechanism C)}]
* Alternate basic phasing in a linear process::
- A = TransferMechanism(function=Linear(), name='A')
- B = TransferMechanism(function=Linear(), name='B')
-
- p = Process(
- pathway=[A, B],
- name = 'p'
- )
- s = System(
- processes=[p],
- name='s'
- )
- my_scheduler = Scheduler(system=s)
-
- my_scheduler.add_condition(A, Any(AtPass(0), EveryNCalls(B, 2)))
- my_scheduler.add_condition(B, Any(EveryNCalls(A, 1), EveryNCalls(B, 1)))
-
- termination_conds = {ts: None for ts in TimeScale}
- termination_conds[TimeScale.TRIAL] = AfterNCalls(B, 4, time_scale=TimeScale.TRIAL)
- execution_sequence = list(my_scheduler.run(termination_conds=termination_conds))
+ >>> A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+ >>> B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+
+ >>> p = pnl.Process(
+ ... pathway=[A, B],
+ ... name = 'p'
+ ... )
+ >>> s = pnl.System(
+ ... processes=[p],
+ ... name='s'
+ ... )
+ >>> my_scheduler = pnl.Scheduler(system=s)
+
+ >>> my_scheduler.add_condition(A,
+ ... pnl.scheduling.condition.Any(pnl.scheduling.condition.AtPass(0),
+ ... pnl.scheduling.condition.EveryNCalls(B, 2)))
+ >>> my_scheduler.add_condition(B,
+ ... pnl.scheduling.condition.Any(pnl.scheduling.condition.EveryNCalls(A, 1),
+ ... pnl.scheduling.condition.EveryNCalls(B, 1)))
+
+ >>> termination_conds = {ts: None for ts in pnl.TimeScale}
+ >>> termination_conds[pnl.TimeScale.TRIAL] = pnl.scheduling.condition.AfterNCalls(B,
+ ... 4,
+ ... time_scale=pnl.TimeScale.TRIAL)
+ >>> execution_sequence = list(my_scheduler.run(termination_conds=termination_conds)) # doctest: +SKIP
+ COMMENT:
+ TODO: Add output for execution sequence
+ COMMENT
execution_sequence: [A, B, B, A, B, B]
* Basic phasing in two processes::
- A = TransferMechanism(function=Linear(), name='A')
- B = TransferMechanism(function=Linear(), name='B')
- C = TransferMechanism(function=Linear(), name='C')
-
- p = Process(
- pathway=[A, C],
- name = 'p'
- )
- q = Process(
- pathway=[B, C],
- name = 'q'
- )
- s = System(
- processes=[p, q],
- name='s'
- )
- my_scheduler = Scheduler(system=s)
-
- my_scheduler.add_condition(A, EveryNPasses(1))
- my_scheduler.add_condition(B, EveryNCalls(A, 2))
- my_scheduler.add_condition(C, Any(AfterNCalls(A, 3), AfterNCalls(B, 3)))
-
- termination_conds = {ts: None for ts in TimeScale}
- termination_conds[TimeScale.TRIAL] = AfterNCalls(C, 4, time_scale=TimeScale.TRIAL)
- execution_sequence = list(my_scheduler.run(termination_conds=termination_conds))
+ >>> A = pnl.TransferMechanism(function=pnl.Linear(), name='A')
+ >>> B = pnl.TransferMechanism(function=pnl.Linear(), name='B')
+ >>> C = pnl.TransferMechanism(function=pnl.Linear(), name='C')
+
+ >>> p = pnl.Process(
+ ... pathway=[A, C],
+ ... name = 'p'
+ ... )
+ >>> q = pnl.Process(
+ ... pathway=[B, C],
+ ... name = 'q'
+ ... )
+ >>> s = pnl.System(
+ ... processes=[p, q],
+ ... name='s'
+ ... )
+ >>> my_scheduler = pnl.Scheduler(system=s)
+
+ >>> my_scheduler.add_condition(A, pnl.scheduling.condition.EveryNPasses(1))
+ >>> my_scheduler.add_condition(B, pnl.scheduling.condition.EveryNCalls(A, 2))
+ >>> my_scheduler.add_condition(C,
+ ... pnl.scheduling.condition.Any(pnl.scheduling.condition.AfterNCalls(A, 3),
+ ... pnl.scheduling.condition.AfterNCalls(B, 3)))
+
+ >>> termination_conds = {ts: None for ts in pnl.TimeScale}
+ >>> termination_conds[pnl.TimeScale.TRIAL] = pnl.scheduling.condition.AfterNCalls(C,
+ ... 4,
+ ... time_scale=pnl.TimeScale.TRIAL)
+ >>> execution_sequence = list(my_scheduler.run(termination_conds=termination_conds)) # doctest: +SKIP
execution_sequence: [A, {A,B}, A, C, {A,B}, C, A, C, {A,B}, C]
diff --git a/tests/documentation/core/test_mechanism_docs.py b/tests/documentation/core/test_mechanism_docs.py
new file mode 100644
index 00000000000..8c31af4bf0b
--- /dev/null
+++ b/tests/documentation/core/test_mechanism_docs.py
@@ -0,0 +1,44 @@
+import pytest
+
+import psyneulink as pnl
+import doctest
+
+
+def test_mechanisms():
+ fail, total = doctest.testmod(pnl.components.mechanisms.mechanism)
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+def test_transfer_mechanism():
+ fail, total = doctest.testmod(
+ pnl.components.mechanisms.processing.transfermechanism)
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+def test_integrator_mechanism():
+ fail, total = doctest.testmod(
+ pnl.components.mechanisms.processing.integratormechanism)
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+# FAILS: AttributeError: 'InputState' object has no attribute '_name'
+def test_objective_mechanism():
+ fail, total = doctest.testmod(
+ pnl.components.mechanisms.processing.objectivemechanism)
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+def test_control_mechanism():
+ fail, total = doctest.testmod(
+ pnl.components.mechanisms.adaptive.control.controlmechanism)
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
diff --git a/tests/documentation/core/test_state_docs.py b/tests/documentation/core/test_state_docs.py
new file mode 100644
index 00000000000..767d366907b
--- /dev/null
+++ b/tests/documentation/core/test_state_docs.py
@@ -0,0 +1,38 @@
+import pytest
+
+import psyneulink as pnl
+import doctest
+
+
+def test_state_docs():
+ # get examples of mechanisms that can be used with GatingSignals/Mechanisms
+ pass
+
+
+def test_parameter_state_docs():
+ fail, total = doctest.testmod(pnl.components.states.parameterstate)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+def test_output_state_docs():
+ fail, total = doctest.testmod(pnl.components.states.outputstate)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total))
+
+
+def test_control_signal_docs():
+ fail, total = doctest.testmod(pnl.components.states.modulatorysignals.controlsignal)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total))
+
+
+def test_gating_signal_docs():
+ fail, total = doctest.testmod(pnl.components.states.modulatorysignals.gatingsignal)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total))
diff --git a/tests/documentation/library/test_lib_mechanism_docs.py b/tests/documentation/library/test_lib_mechanism_docs.py
new file mode 100644
index 00000000000..75ad92740a3
--- /dev/null
+++ b/tests/documentation/library/test_lib_mechanism_docs.py
@@ -0,0 +1,28 @@
+import doctest
+
+import os
+import pytest
+
+import psyneulink as pnl
+
+
+def test_ddm_docs():
+ # FIXME: Does this run outside of the test directory?
+ # os.chdir('../../Matlab/DDMFunctions')
+ # print("current dir = {}".format(os.getcwd()))
+ # ALSO FIXME: ValueError cannot convert float NaN integer
+ fail, total = doctest.testmod(
+ pnl.library.mechanisms.processing.integrator.ddm)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
+
+
+def test_comparator_mechanism_docs():
+ fail, total = doctest.testmod(
+ pnl.library.mechanisms.processing.objective.comparatormechanism)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, total),
+ pytrace=False)
diff --git a/tests/documentation/library/test_subsystem_docs.py b/tests/documentation/library/test_subsystem_docs.py
new file mode 100644
index 00000000000..a84f3680c79
--- /dev/null
+++ b/tests/documentation/library/test_subsystem_docs.py
@@ -0,0 +1,23 @@
+import doctest
+
+import pytest
+
+import psyneulink as pnl
+
+
+def test_lc_control_mechanism_docs():
+ fail, test = doctest.testmod(pnl.library.subsystems.agt.lccontrolmechanism,
+ optionflags=doctest.REPORT_NDIFF)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, test),
+ pytrace=False)
+
+
+def test_evc_control_mechanism_docs():
+ fail, test = doctest.testmod(pnl.library.subsystems.evc.evccontrolmechanism,
+ optionflags=doctest.REPORT_NDIFF)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, test),
+ pytrace=False)
diff --git a/tests/documentation/scheduling/test_scheduling_docs.py b/tests/documentation/scheduling/test_scheduling_docs.py
new file mode 100644
index 00000000000..8bb63321245
--- /dev/null
+++ b/tests/documentation/scheduling/test_scheduling_docs.py
@@ -0,0 +1,13 @@
+import doctest
+
+import pytest
+
+import psyneulink as pnl
+
+
+def test_scheduler_docs():
+ fail, test = doctest.testmod(pnl.scheduling.scheduler,
+ optionflags=doctest.REPORT_NDIFF)
+
+ if fail > 0:
+ pytest.fail("{} out of {} examples failed".format(fail, test))
diff --git a/tests/documentation/test_homepage.py b/tests/documentation/test_homepage.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/mechanisms/test_input_state_spec.py b/tests/mechanisms/test_input_state_spec.py
index cb28eb357f6..537c44d35e2 100644
--- a/tests/mechanisms/test_input_state_spec.py
+++ b/tests/mechanisms/test_input_state_spec.py
@@ -8,7 +8,7 @@
from psyneulink.components.projections.pathway.mappingprojection import MappingProjection
from psyneulink.components.states.state import StateError
from psyneulink.components.states.inputstate import InputState
-from psyneulink.globals.keywords import INPUT_STATES, MECHANISM, NAME, OUTPUT_STATES, PROJECTIONS, VARIABLE, RESULT
+from psyneulink.globals.keywords import INPUT_STATES, MECHANISM, NAME, OUTPUT_STATES, PROJECTIONS, VARIABLE, RESULTS
mismatches_default_variable_error_text = 'not compatible with the specified default variable'
mismatches_size_error_text = 'not compatible with the default variable determined from size parameter'
@@ -239,9 +239,9 @@ def test_2_item_tuple_value_for_first_item(self):
# ------------------------------------------------------------------------------------------------
# TEST 13
- # ProjectionTuple Specification
+ # 4-item tuple Specification
- def test_projection_tuple_spec(self):
+ def test_projection_tuple_with_matrix_spec(self):
R2 = TransferMechanism(size=3)
T = TransferMechanism(size=2, input_states=[(R2, None, None, np.zeros((3, 2)))])
np.testing.assert_array_equal(T.instance_defaults.variable, np.array([[0, 0]]))
@@ -614,7 +614,7 @@ def test_use_set_to_specify_projections_for_input_state_error(self):
# ------------------------------------------------------------------------------------------------
# TEST 31
- def test_multiple_states_specified_using_STATE_NAME_format_error(self):
+ def test_multiple_states_specified_using_state_name_format_error(self):
with pytest.raises(StateError) as error_text:
# Don't bother to specify anything as the value for each entry in the dict, since doesn't get there
TransferMechanism(input_states=[{'MY STATE A':{},
@@ -669,7 +669,7 @@ def test_lists_of_mechanisms_and_output_states(self):
assert T2.input_states[0].path_afferents[1].sender.owner.name=='T1'
assert T2.input_states[0].path_afferents[1].matrix.shape == (2,1)
- # Test list of Mechanisms in ProjectionTuple
+ # Test list of Mechanisms in 4-item tuple specification
T3 = TransferMechanism(name='T3', input_states=[([T0, T1],None,None,InputState)])
assert len(T3.input_states[0].path_afferents)==2
assert T3.input_states[0].path_afferents[0].sender.owner.name=='T0'
@@ -683,7 +683,7 @@ def test_lists_of_mechanisms_and_output_states(self):
assert T4.input_states[0].path_afferents[1].sender.owner.name=='T1'
assert T4.input_states[0].path_afferents[1].matrix.shape == (3,1)
- # Test list of OutputStates in ProjectionTuple
+ # Test list of OutputStates in 4-item tuple specification
T5 = TransferMechanism(name='T5', input_states=[([T0.output_states[0], T1.output_states[1]],
None,None,
InputState)])
@@ -704,3 +704,93 @@ def test_list_of_mechanisms_with_gating_mechanism(self):
assert T2.input_states[0].path_afferents[0].sender.owner.name=='T6'
assert T2.input_states[0].mod_afferents[0].sender.name=='a'
assert T2.output_states[0].mod_afferents[0].sender.name=='b'
+
+ # ------------------------------------------------------------------------------------------------
+ # THOROUGH TESTING OF mech, 2-item, 3-item and 4-item tuple specifications with and without default_variable/size
+ # (some of these may be duplicative of tests above)
+
+ def test_mech_and_tuple_specifications_with_and_without_default_variable_or_size(self):
+
+ t = TransferMechanism(size=3)
+
+ # ADD TESTING WITH THIS IN PLACE OF t:
+ # p = MappingProjection(sender=t)
+
+
+ # Specification of default_variable or size (constrain InputState variable)
+
+ # default_variable, mech
+ T = TransferMechanism(
+ default_variable=[0, 0],
+ input_states=[t])
+ assert len(T.input_states[0].variable)==2
+
+ # default_variable, 2-item tuple
+ T = TransferMechanism(
+ default_variable=[0, 0],
+ input_states=[(t,None)])
+ assert len(T.input_states[0].variable)==2
+
+ # default_variable, 3-item tuple
+ T = TransferMechanism(
+ default_variable=[0, 0],
+ input_states=[(t,1,1)])
+ assert len(T.input_states[0].variable)==2
+
+ # default_variable, 3-item tuple with embedded (State name, Mechanism) tuple
+ T = TransferMechanism(
+ default_variable=[0, 0],
+ input_states=[((RESULTS, t),1,1)])
+ assert len(T.input_states[0].variable)==2
+
+ # default_variable, 4-item tuple
+ T = TransferMechanism(
+ default_variable=[0, 0],
+ input_states=[(t,1,1,None)])
+ assert len(T.input_states[0].variable)==2
+
+ # size, mech
+ T = TransferMechanism(
+ size=2,
+ input_states=[t])
+ assert len(T.input_states[0].variable)==2
+
+ # size, 2-item tuple
+ T = TransferMechanism(
+ size=2,
+ input_states=[(t,None)])
+ assert len(T.input_states[0].variable)==2
+
+ # size, 3-item tuple
+ T = TransferMechanism(
+ size=2,
+ input_states=[(t,1,1)])
+ assert len(T.input_states[0].variable)==2
+
+ # size, 4-item tuple
+ T = TransferMechanism(
+ size=2,
+ input_states=[(t,1,1,None)])
+ assert len(T.input_states[0].variable)==2
+
+ # No specification of default_variable or size (use sender's value)
+
+ # no variable spec, mech
+ T = TransferMechanism(
+ input_states=[t])
+ assert len(T.input_states[0].variable)==3
+
+ # no variable spec, 2-item tuple
+ T = TransferMechanism(
+ input_states=[(t,None)])
+ assert len(T.input_states[0].variable)==3
+
+ # no variable spec, 3-item tuple
+ T = TransferMechanism(
+ input_states=[(t,1,1)])
+ assert len(T.input_states[0].variable)==3
+
+ # no variable spec, 4-item tuple
+ T = TransferMechanism(
+ input_states=[(t,1,1,None)])
+ assert len(T.input_states[0].variable)==3
diff --git a/tests/projections/test_projections_specifications.py b/tests/projections/test_projections_specifications.py
index 4d392c360d9..5f49bbef6ca 100644
--- a/tests/projections/test_projections_specifications.py
+++ b/tests/projections/test_projections_specifications.py
@@ -147,3 +147,51 @@ def test_2_item_tuple_from_input_and_output_states_to_gating_signals(self):
assert T.input_states[0].mod_afferents[0].sender==G.gating_signals[0]
assert T.output_states[0].mod_afferents[0].sender==G.gating_signals[1]
+
+ def test_formats_for_control_specification(self):
+
+ control_spec_list = [
+ pnl.CONTROL,
+ pnl.CONTROL_SIGNAL,
+ pnl.ControlSignal,
+ pnl.ControlSignal(),
+ (0.3, pnl.CONTROL),
+ (0.3, pnl.CONTROL_SIGNAL),
+ (0.3, pnl.ControlSignal),
+ (0.3, pnl.ControlSignal()),
+ (0.3, pnl.ControlProjection),
+ (0.3, pnl.ControlProjection())
+ ]
+ for i, ctl_tuple in enumerate([i for i in zip(control_spec_list, reversed(control_spec_list))]):
+ C1, C2 = ctl_tuple
+ R = pnl.RecurrentTransferMechanism(noise=C1,
+ function=pnl.Logistic(gain=C2))
+ assert R.parameter_states[pnl.NOISE].mod_afferents[0].name in \
+ 'ControlProjection for RecurrentTransferMechanism-{}[noise]'.format(i)
+ assert R.parameter_states[pnl.GAIN].mod_afferents[0].name in \
+ 'ControlProjection for RecurrentTransferMechanism-{}[gain]'.format(i)
+
+ # def test_formats_for_gating_specification(self):
+ #
+ # gating_spec_list = [
+ # pnl.GATING,
+ # pnl.GATING_SIGNAL,
+ # pnl.GatingSignal,
+ # pnl.GatingSignal(),
+ # (0.3, pnl.GATING),
+ # (0.3, pnl.GATING_SIGNAL),
+ # (0.3, pnl.GatingSignal),
+ # (0.3, pnl.GatingSignal()),
+ # (0.3, pnl.GatingProjection),
+ # (0.3, pnl.GatingProjection())
+ # ]
+ #
+ # for i, gating_tuple in enumerate([i for i in zip(gating_spec_list, reversed(gating_spec_list))]):
+ # G1, G2 = gating_tuple
+ # T = pnl.TransferMechanism(input_states=[G1],
+ # output_states=[G2])
+ # assert T.input_states[0].mod_afferents[0].name in \
+ # 'GatingProjection for TransferMechanism-0[InputState-{}]'.format(i)
+ #
+ # assert T.output_states[0].mod_afferents[0].name in \
+ # 'GatingProjection for TransferMechanism-0[OutputState-{}]'.format(i)