Skip to content

Commit

Permalink
Fix/controlmechanism/assign as controller (#618)
Browse files Browse the repository at this point in the history
* • System
  - show_graph():
    fixed bug producing empty image for graphs with just one Mechanism
    added auto-recurrent projections

* • Log
  - added numpy_array output method

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* • Log
  - fixed bugs preventing logging during INITIALIZATION

* • Log
  - fixed bugs preventing logging during INITIALIZATION

* -

* -

* -

* -

* -

* • Log
  - logged_item, print_entries:  corrected to use 'value' rather than owner's name in reports

* • Log
  - logged_item, print_entries:  corrected to use 'value' rather than owner's name in reports

• Tests
  test_log:  added test_log_initialization

* • Log
  - _alias_owner_name:  added to used 'value' rather than owner's name in reports
  - nparray: bug fix to handle None values

• Tests
  test_log:  added test_log_initialization

* • Log
  - log_value: added

• Tests
  - test_multilayer: added test of log_value

* • Log
  - log_value: implemented
  - logged_item, print_entries:  corrected to use 'value' rather than owner's name in reports

• Tests
  - test_multilayer: added test of log_value

* • Log
  - log_value: implemented
  - logged_item, print_entries:  corrected to use 'value' rather than owner's name in reports

• Tests
  - test_multilayer: added test of log_value

* -

* • Component
  - moved value property to Component (from Mechanism, Projection and State)
    (left an override on ControlSignal that needs it for the getter)

* • Log
  - docstring: added hint about using call_before_trial and call_after_trial
    to log values

* -

* -

* • ObjectiveMechanism
  _instantiate_input_states():  corrected to used monitored_output_state_specs

* • System
  _instantiate_controller():
      added assignment of controller.control_signals to self.control_signals

* -

* • ControlMechanism
  _instantiate_output_states():
      modifed to not instantiate a default OutputState
      if no ControlSignals are specified

* • ControlMechanism
  _instantiate_output_states():
      reverted to ordinary instantation of default ControlSignal if none specified

* • ControlMechanism
  _instantiate_output_states():
      reverted to ordinary instantation of default ControlSignal if none specified

* • System
  _get_monitored_output_states_for_system():
    fixed bug in which string and tuple specifications were ignored

* • System
  _get_monitored_output_states_for_system():
    fixed bug in which string and tuple specifications were ignored

* • System
  _get_monitored_output_states_for_system():
    fixed bug in which string and tuple specifications were ignored

* -

* -

* -
  • Loading branch information
jdcpni committed Jan 16, 2018
1 parent dd9bef5 commit 5a2e444
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 167 deletions.
10 changes: 9 additions & 1 deletion Scripts/Examples/EVC-Gratton.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,18 @@
name='EVC Gratton System'
)

# mySystem.show_graph(show_control=pnl.ALL, show_dimensions=pnl.ALL)


control_mech = pnl.EVCControlMechanism(name='NEW CONTROLLER')
mySystem.controller = control_mech
# control_mech.assign_as_controller(mySystem)

# mySystem.show_graph(show_control=pnl.ALL, show_dimensions=pnl.ALL)

# Show characteristics of system:
mySystem.show()
mySystem.controller.show()
# mySystem.show_graph(show_control=pnl.ALL, show_dimensions=pnl.ALL)

# configure EVC components
mySystem.controller.control_signals[0].intensity_cost_function = pnl.Exponential(rate=0.8046).function
Expand Down
2 changes: 1 addition & 1 deletion psyneulink/components/functions/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ def _validate_params(self, request_set, target_set=None, context=None):
if EXPONENTS in target_set and target_set[EXPONENTS] is not None:
self._validate_parameter_spec(target_set[EXPONENTS], EXPONENTS, numeric_only=True)
target_set[EXPONENTS] = np.atleast_2d(target_set[EXPONENTS]).reshape(-1, 1)
if (c in context for c in {EXECUTING, LEARNING}):
if any(c in context for c in {EXECUTING, LEARNING}):
if len(target_set[EXPONENTS]) != len(self.instance_defaults.variable):
raise FunctionError("Number of exponents ({0}) does not equal number of items in variable ({1})".
format(len(target_set[EXPONENTS]), len(self.instance_defaults.variable.shape)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@
from psyneulink.components.states.modulatorysignals.controlsignal import ControlSignal
from psyneulink.components.states.outputstate import INDEX, SEQUENTIAL
from psyneulink.globals.defaults import defaultControlAllocation
from psyneulink.globals.keywords import AUTO_ASSIGN_MATRIX, CONTROL, CONTROL_PROJECTION, CONTROL_PROJECTIONS, CONTROL_SIGNAL, CONTROL_SIGNALS, EXPONENT, INIT__EXECUTE__METHOD_ONLY, NAME, OBJECTIVE_MECHANISM, PRODUCT, PROJECTIONS, PROJECTION_TYPE, SYSTEM, VARIABLE, WEIGHT
from psyneulink.globals.keywords import AUTO_ASSIGN_MATRIX, CONTROL, CONTROL_PROJECTION, CONTROL_PROJECTIONS, \
CONTROL_SIGNAL, CONTROL_SIGNALS, EXPONENT, INIT__EXECUTE__METHOD_ONLY, NAME, OBJECTIVE_MECHANISM, PRODUCT, \
PROJECTIONS, PROJECTION_TYPE, SYSTEM, VARIABLE, WEIGHT, COMMAND_LINE
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.globals.utilities import ContentAddressableList
Expand Down Expand Up @@ -647,7 +649,7 @@ def _validate_params(self, request_set, target_set=None, context=None):
# If ControlMechanism has been assigned to a System, check that
# all the items in the list used to specify objective_mechanism are in the same System
if self.system:
self.system._validate_monitored_state_in_system([spec], context=context)
self.system._validate_monitored_states_in_system([spec], context=context)

if not isinstance(target_set[OBJECTIVE_MECHANISM], (ObjectiveMechanism, list)):
raise ControlMechanismError("Specification of {} arg for {} ({}) must be an {}"
Expand Down Expand Up @@ -719,7 +721,7 @@ def _instantiate_objective_mechanism(self, context=None):

# INSTANTIATE ObjectiveMechanism

# If *objective_mechanism* argument si an ObjectiveMechanism, add monitored_output_states to it
# If *objective_mechanism* argument is an ObjectiveMechanism, add monitored_output_states to it
if isinstance(self.objective_mechanism, ObjectiveMechanism):
if monitored_output_states:
self.objective_mechanism.add_monitored_output_states(
Expand Down Expand Up @@ -788,9 +790,6 @@ def _instantiate_output_states(self, context=None):
# ---------------------------------------------------

if self.control_signals:



self._output_states = []

# for i, control_signal in enumerate(self.control_signals):
Expand All @@ -800,7 +799,7 @@ def _instantiate_output_states(self, context=None):

super()._instantiate_output_states(context=context)

# Reassign control_signals to capture any user_defined ControlSignals instantiated by in call to super
# Reassign control_signals to capture any user_defined ControlSignals instantiated in call to super
# and assign to ContentAddressableList
self._control_signals = ContentAddressableList(component_type=ControlSignal,
list=[state for state in self.output_states
Expand Down Expand Up @@ -885,8 +884,6 @@ def _instantiate_control_signal(self, control_signal, context=None):

return control_signal



def _execute(self,
variable=None,
runtime_params=None,
Expand Down Expand Up @@ -938,25 +935,32 @@ def add_monitored_output_states(self, monitored_output_states, context=None):
"""Instantiate OutputStates to be monitored by ControlMechanism's `objective_mechanism
<ControlMechanism.objective_mechanism>`.
**monitored_output_states** can be a `Mechanism`, `OutputState`, `tuple specification
<InputState_Tuple_Specification>`, a `State specification dicionary <InputState_Specification_Dictionary>`,
or list with any of these. If item is a Mechanism, its `primary OutputState <OutputState_Primary>` is used.
**monitored_output_states** can be any of the following:
- `Mechanism`;
- `OutputState`;
- `tuple specification <InputState_Tuple_Specification>`;
- `State specification dictionary <InputState_Specification_Dictionary>`;
- list with any of the above.
If any item is a Mechanism, its `primary OutputState <OutputState_Primary>` is used.
OutputStates must belong to Mechanisms in the same `System` as the ControlMechanism.
"""
output_states = self.objective_mechanism.add_monitored_output_states(
monitored_output_states_specs=monitored_output_states,
context=context)
if self.system:
self.system._validate_monitored_state_in_system(output_states, context=context)
self.system._validate_monitored_states_in_system(output_states, context=context)

@tc.typecheck
def assign_as_controller(self, system:System_Base, context=None):
def assign_as_controller(self, system:System_Base, context=COMMAND_LINE):
"""Assign ControlMechanism as `controller <System.controller>` for a `System`.
**system** must be a System for which the ControlMechanism should be assigned as the `controller
<System.controller>`; if the specified System already has a `controller <System.controller>`,
it will be replaced by the current one; if the current one is already the `controller <System.controller>`
for another System, it will be disabled for that System.
<System.controller>`.
If the specified System already has a `controller <System.controller>`, it will be replaced by the current
one, and the current one will inherit any ControlSignals previously specified for the old controller or the
System itself.
If the current one is already the `controller <System.controller>` for another System, it will be disabled
for that System.
COMMENT:
[TBI:
The ControlMechanism's `objective_mechanism <ControlMechanism.objective_mechanism>`,
Expand All @@ -980,6 +984,10 @@ def assign_as_controller(self, system:System_Base, context=None):
COMMENT
"""

if context==COMMAND_LINE:
system.controller = self
return

# NEED TO BUFFER OBJECTIVE_MECHANISM AND CONTROL_SIGNAL ARGUMENTS FOR USE IN REINSTANTIATION HERE
# DETACH AS CONTROLLER FOR ANY EXISTING SYSTEM (AND SET THAT ONE'S CONTROLLER ATTRIBUTE TO None)
# DELETE ALL EXISTING OBJECTIVE_MECHANISM AND CONTROL_SIGNAL ASSIGNMENTS
Expand All @@ -990,7 +998,7 @@ def assign_as_controller(self, system:System_Base, context=None):

# First, validate that all of the ControlMechanism's monitored_output_states and controlled parameters
# are in the new System
system._validate_monitored_state_in_system(self.monitored_output_states)
system._validate_monitored_states_in_system(self.monitored_output_states)
system._validate_control_signals(self.control_signals)

# Next, get any OutputStates specified in the **monitored_output_states** argument of the System's
Expand All @@ -1000,10 +1008,28 @@ def assign_as_controller(self, system:System_Base, context=None):
monitored_output_states = list(system._get_monitored_output_states_for_system(controller=self, context=context))
self.add_monitored_output_states(monitored_output_states)

# Then, assign it ControlSignals for any parameters in the current System specified for control
system_control_signals = system._get_control_signals_for_system(system.control_signals, context=context)
# The system does NOT already have a controller,
# so assign it ControlSignals for any parameters in the System specified for control
if system.controller is None:
system_control_signals = system._get_control_signals_for_system(system.control_signals, context=context)
# The system DOES already have a controller,
# so assign it the old controller's ControlSignals
else:
system_control_signals = system.control_signals
for control_signal in system_control_signals:
control_signal.owner = None
# Get rid of default ControlSignal if it has no ControlProjections
if (len(self.control_signals)==1
and self.control_signals[0].name=='ControlSignal-0'
and not self.control_signals[0].efferents):
del self._output_states[0]
del self.control_signals[0]
self.allocation_policy = None

for control_signal_spec in system_control_signals:
self._instantiate_control_signal(control_signal=control_signal_spec, context=context)
control_signal = self._instantiate_control_signal(control_signal=control_signal_spec, context=context)
control_signal.owner = self
self.control_signals.append(control_signal)

# If it HAS been assigned a System, make sure it is the current one
if self.system and not self.system is system:
Expand All @@ -1017,6 +1043,14 @@ 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

# Finally, assign the self as controller for system
# # MODIFIED 1/14/18 OLD:
# system.controller = self
# MODIFIED 1/14/18 NEW:
if context != 'System.controller setter':
system._controller = self
# MODIFIED 1/14/18 END

@property
def monitored_output_states(self):
try:
Expand Down
7 changes: 5 additions & 2 deletions psyneulink/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -1761,14 +1761,17 @@ def _instantiate_attributes_after_function(self, context=None):
self._instantiate_output_states(context=context)
super()._instantiate_attributes_after_function(context=context)

def _instantiate_input_states(self, input_states=None, context=None):
def _instantiate_input_states(self, input_states=None, reference_value=None, context=None):
"""Call State._instantiate_input_states to instantiate orderedDict of InputState(s)
This is a stub, implemented to allow Mechanism subclasses to override _instantiate_input_states
or process InputStates before and/or after call to _instantiate_input_states
"""
from psyneulink.components.states.inputstate import _instantiate_input_states
return _instantiate_input_states(owner=self, input_states=input_states or self.input_states, context=context)
return _instantiate_input_states(owner=self,
input_states=input_states or self.input_states,
reference_value=reference_value,
context=context)

def _instantiate_parameter_states(self, context=None):
"""Call State._instantiate_parameter_states to instantiate a ParameterState for each parameter in user_params
Expand Down
Loading

0 comments on commit 5a2e444

Please sign in to comment.