Skip to content

Commit

Permalink
Fix/controlmechanism/assign as controller (#620)
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

* -

* -

* -

* • Function
  LinearCombination()._validate_params:
      fixed check on length of weight and exponents

* • Function
  LinearCombination()._validate_params:
      fixed check on length of weight and exponents

* • IntegratorMechanism
  - added input_states as argument to constructor

* • IntegratorMechanism
  - added input_states as argument to constructor

* • IntegratorMechanism
  - added input_states as argument to constructor

• System, ControlMechanism, EVCControlMechanism
  - clean-up for assign_as_controller

* -
  • Loading branch information
jdcpni authored Jan 17, 2018
1 parent 32673e3 commit e0f9fbd
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1043,13 +1043,8 @@ def assign_as_controller(self, system:System_Base, context=COMMAND_LINE):
# 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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class ClassDefaults(ProcessingMechanism_Base.ClassDefaults):
def __init__(self,
default_variable=None,
size=None,
input_states:tc.optional(tc.any(list, dict))=None,
function=AdaptiveIntegrator(rate=0.5),
params=None,
name=None,
Expand All @@ -219,6 +220,7 @@ def __init__(self,

super(IntegratorMechanism, self).__init__(variable=default_variable,
size=size,
input_states=input_states,
params=params,
name=name,
prefs=prefs,
Expand Down
47 changes: 22 additions & 25 deletions psyneulink/components/mechanisms/processing/objectivemechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,11 +687,10 @@ def add_monitored_output_states(self, monitored_output_states_specs, context=Non
"""
monitored_output_states_specs = list(monitored_output_states_specs)

# If ObjectiveMechanism has only its default InputState and it has no afferent Projections:
# If ObjectiveMechanism has only its default InputState and that has no afferent Projections:
# delete it and first item of variable
if len(self.input_states)==1 and self.input_state.name=='InputState-0' and not self.input_state.path_afferents:
del self.input_states[0]
# FIX: 1/14/18 - CHECK WITH KEVIN WHETHER THIS IS THE THING TO DO HERE
self.instance_defaults.variable = []
self._update_variable(self.instance_defaults.variable)

Expand All @@ -704,30 +703,28 @@ def add_monitored_output_states(self, monitored_output_states_specs, context=Non

# If it is a MonitoredOutputStateTuple, create InputState specification dictionary
if isinstance(spec, MonitoredOutputStateTuple):
# Create InputState specification dictionary:
monitored_output_states_specs[i] = {NAME: spec.output_state.name,
VARIABLE: spec.output_state.value,
WEIGHT: spec.weight,
EXPONENT: spec.exponent,
PROJECTIONS: [(spec.output_state, spec.matrix)]}
reference_value.append(spec.output_state.value)

spec = {NAME: spec.output_state.name,
VARIABLE: spec.output_state.value,
WEIGHT: spec.weight,
EXPONENT: spec.exponent,
PROJECTIONS: [(spec.output_state, spec.matrix)]}
monitored_output_states_specs[i] = spec

# Parse spec to get value of OutputState and (possibly) the Projection from it
input_state = _parse_state_spec(owner=self, state_type = InputState, state_spec=spec)

# There should be only one ProjectionTuple specified,
# that designates the OutputState and (possibly) a Projection from it
if len(input_state[PARAMS][PROJECTIONS])!=1:
raise ObjectiveMechanismError("PROGRAM ERROR: Failure to parse item in monitored_output_states_specs "
"for {} (item: {})".format(self.name, spec))
projection_tuple = input_state[PARAMS][PROJECTIONS][0]
# If Projection is specified, use its value
if PROJECTION in projection_tuple.projection:
reference_value.append(projection_tuple.projection[PROJECTION].value)
# Otherwise, use its sender's (OutputState) value
else:
# Otherwise, parse spec to get value of OutputState and (possibly) the Projection from it
input_state = _parse_state_spec(owner=self, state_type = InputState, state_spec=spec)

# There should be only one ProjectionTuple specified,
# that designates the OutputState and (possibly) a Projection from it
if len(input_state[PARAMS][PROJECTIONS])!=1:
raise ObjectiveMechanismError("PROGRAM ERROR: Failure to parse item in monitored_output_states_specs "
"for {} (item: {})".format(self.name, spec))
projection_tuple = input_state[PARAMS][PROJECTIONS][0]
# If Projection is specified, use its value
if PROJECTION in projection_tuple.projection:
reference_value.append(projection_tuple.projection[PROJECTION].value)
# Otherwise, use its sender's (OutputState) value
else:
reference_value.append(projection_tuple.state.value)
reference_value.append(projection_tuple.state.value)

input_states = self._instantiate_input_states(monitored_output_states_specs=monitored_output_states_specs,
reference_value=reference_value,
Expand Down
4 changes: 2 additions & 2 deletions psyneulink/components/projections/projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1070,8 +1070,8 @@ def _parse_projection_spec(projection_spec,
All keys in kwargs must be from PROJECTION_ARGS
If projection_spec is or resolves to a Projection object, returns State object.
Otherwise, return State specification dictionary using any arguments provided as defaults
If projection_spec is or resolves to a Projection object, returns Projection object.
Otherwise, return Projection specification dictionary using any arguments provided as defaults
"""

bad_arg = next((key for key in kwargs if not key in PROJECTION_ARGS), None)
Expand Down
17 changes: 6 additions & 11 deletions psyneulink/components/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -2063,8 +2063,9 @@ def _get_monitored_output_states_for_system(self, controller=None, context=None)
# - a MonitoredOutputStatesTuple (returned by _get_monitored_states_for_system when
# specs were initially processed by the System to parse its *monitor_for_control* argument;
# - a specification for an existing Mechanism or OutputStates from the *monitor_for_control* arg of System.
all_specs_extracted_from_tuples = []
for i, spec in enumerate(all_specs.copy()):
all_specs_extracted_from_tuples=[]
all_specs_parsed=[]
for i, spec in enumerate(all_specs):

# Leave MonitoredOutputStatesOption and MonitoredOutputStatesTuple spec in place;
# these are parsed later on
Expand Down Expand Up @@ -2137,17 +2138,11 @@ def _get_monitored_output_states_for_system(self, controller=None, context=None)
"is not a recognized specification for an {}".
format(MONITOR_FOR_CONTROL, self.name, spec, OutputState.__name__))

# Delete original item of all_specs, and assign ones parsed into monitored_output_state_tuple(s)
del all_specs[i]
# # MODIFIED 1/15/17 OLD:
# all_specs.insert(i, monitored_output_state_tuples)
# MODIFIED 1/15/17 NEW:
all_specs = insert_list(all_specs, i, monitored_output_state_tuples)
# MODIFIED 1/15/17 END


all_specs_parsed.extend(monitored_output_state_tuples)
all_specs_extracted_from_tuples.extend([item.output_state for item in monitored_output_state_tuples])

all_specs = all_specs_parsed

try:
all (isinstance(item, (OutputState, MonitoredOutputStatesOption))
for item in all_specs_extracted_from_tuples)
Expand Down
4 changes: 3 additions & 1 deletion psyneulink/globals/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
'OFF', 'OFFSET', 'ON', 'OPERATION', 'ORIGIN', 'ORNSTEIN_UHLENBECK_INTEGRATOR_FUNCTION', 'OUTCOME_FUNCTION',
'OUTPUT_STATE', 'OUTPUT_STATE_PARAMS', 'OUTPUT_STATES', 'OUTPUT_TYPE', 'OWNER', 'PARAM_CLASS_DEFAULTS',
'PARAM_INSTANCE_DEFAULTS', 'PARAMETER_STATE', 'PARAMETER_STATE_PARAMS', 'PARAMETER_STATES', 'PARAMS',
'PARAMS_CURRENT', 'PATHWAY', 'PATHWAY_PROJECTION', 'PEARSON', 'PREDICTION_MECHANISM', 'PREDICTION_MECHANISM_OUTPUT',
'PARAMS_CURRENT', 'PATHWAY', 'PATHWAY_PROJECTION', 'PEARSON',
'PREDICTION_MECHANISM', 'PREDICTION_MECHANISMS', 'PREDICTION_MECHANISM_OUTPUT',
'PREDICTION_MECHANISM_PARAMS', 'PREDICTION_MECHANISM_TYPE', 'PREFS_ARG', 'PRIMARY', 'PROB', 'PROCESS', 'PROCESSING',
'PROCESS_INIT', 'PROCESSES', 'PROCESSES_DIM', 'PROCESSING_MECHANISM', 'PRODUCT', 'PROJECTION',
'PROJECTION_PARAMS', 'PROJECTION_SENDER', 'PROJECTION_SENDER_VALUE', 'PROJECTION_TYPE', 'PROJECTIONS',
Expand Down Expand Up @@ -662,6 +663,7 @@ def _is_metric(metric):
OBJECTIVE_MECHANISM = "objective_mechanism"
MONITOR_FOR_CONTROL = "monitor_for_control"
PREDICTION_MECHANISM = "Prediction Mechanism"
PREDICTION_MECHANISMS = "prediction_mechanisms"
PREDICTION_MECHANISM_TYPE = "prediction_mechanism_type"
PREDICTION_MECHANISM_PARAMS = "prediction_mechanism_params"
PREDICTION_MECHANISM_OUTPUT = "PredictionMechanismOutput"
Expand Down
75 changes: 33 additions & 42 deletions psyneulink/library/subsystems/evc/evccontrolmechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@
from psyneulink.components.shellclasses import Function, System_Base
from psyneulink.globals.defaults import defaultControlAllocation
from psyneulink.globals.keywords import COMMAND_LINE, CONTROL, COST_FUNCTION, EVC_MECHANISM, FUNCTION, INITIALIZING, \
INIT_FUNCTION_METHOD_ONLY, PARAMETER_STATES, PREDICTION_MECHANISM, PREDICTION_MECHANISM_PARAMS, PREDICTION_MECHANISM_TYPE, SUM
INIT_FUNCTION_METHOD_ONLY, PARAMETER_STATES, PREDICTION_MECHANISM, PREDICTION_MECHANISMS, \
PREDICTION_MECHANISM_PARAMS, PREDICTION_MECHANISM_TYPE, SUM
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 @@ -740,37 +741,43 @@ def _instantiate_input_states(self, context=None):
"""Instantiate PredictionMechanisms
"""
if self.system is not None:
self._instantiate_prediction_mechanisms(context=context)
self._instantiate_prediction_mechanisms(system=self.system, context=context)
super()._instantiate_input_states(context=context)

def _instantiate_prediction_mechanisms(self, context=None):
"""Add prediction Mechanism and associated process for each `ORIGIN` (input) Mechanism in the System
def _instantiate_prediction_mechanisms(self, system:System_Base, context=None):
"""Add prediction Mechanism and associated process for each `ORIGIN` (input) Mechanism in system
Instantiate prediction_mechanisms for `ORIGIN` Mechanisms in self.system; these will now be `TERMINAL`
Instantiate prediction_mechanisms for `ORIGIN` Mechanisms in system; these will now be `TERMINAL`
Mechanisms:
- if their associated input mechanisms were TERMINAL MECHANISMS, they will no longer be so; therefore...
- if an associated input Mechanism must be monitored by the EVCControlMechanism, it must be specified
explicitly in an OutputState, Mechanism, controller or System OBJECTIVE_MECHANISM param (see below)
explicitly in an OutputState, Mechanism, controller or system OBJECTIVE_MECHANISM param (see below)
For each `ORIGIN` Mechanism in self.system:
For each `ORIGIN` Mechanism in system:
- instantiate a corresponding predictionMechanism
- instantiate a Process, with a pathway that projects from the ORIGIN to the prediction Mechanism
- add the process to self.system.processes
- add the Process to system.processes
Instantiate self.predicted_input dict:
- key for each entry is an `ORIGIN` Mechanism of the System
- key for each entry is an `ORIGIN` Mechanism of system
- value of each entry is the value of the corresponding predictionMechanism:
each value is a 2d array, each item of which is the value of an InputState of the predictionMechanism
Args:
context:
"""

# FIX: 1/16/18 - Should should check for any new origin_mechs? What if origin_mech deleted?
# If system's controller already has prediction_mechanisms, use those
if hasattr(system.controller, PREDICTION_MECHANISMS):
self.prediction_mechanisms = system.controller.prediction_mechanisms
self.origin_prediction_mechanisms = system.controller.origin_prediction_mechanisms
self.predicted_input = system.controller.predicted_input
return

# Dictionary of prediction_mechanisms, keyed by the ORIGIN Mechanism to which they correspond
self.origin_prediction_mechanisms = {}

# self.predictionProcesses = []

# List of prediction Mechanism tuples (used by system to execute them)
self.prediction_mechs = []

Expand All @@ -780,23 +787,18 @@ def _instantiate_prediction_mechanisms(self, context=None):
except KeyError:
prediction_mechanism_params = {}

for origin_mech in system.origin_mechanisms.mechanisms:
state_names = []
variable = []
for state_name in origin_mech.input_states.names:
state_names.append(state_name)
variable.append(origin_mech.input_states[state_name].instance_defaults.variable)

for origin_mech in self.system.origin_mechanisms.mechanisms:
# FIX: 1/15/18
# # IMPLEMENT THE FOLLOWING ONCE INPUT_STATES CAN BE SPECIFIED IN CONSTRUCTION OF ALL MECHANISMS
# # (AS THEY CAN CURRENTLY FOR ObjectiveMechanisms)
# state_names = []
# variables = []
# for state_name in origin_mech.input_states.keys():
# state_names.append(state_name)
# variables.append(origin_mech_intputStates[state_name].instance_defaults.variable)

# Instantiate predictionMechanism
# Instantiate PredictionMechanism
prediction_mechanism = self.paramsCurrent[PREDICTION_MECHANISM_TYPE](
name=origin_mech.name + " " + PREDICTION_MECHANISM,
default_variable = origin_mech.input_state.instance_defaults.variable,
# default_variable=variables,
# INPUT_STATES=state_names,
default_variable=variable,
input_states=state_names,
params = prediction_mechanism_params,
context=context,
)
Expand Down Expand Up @@ -826,17 +828,16 @@ def _instantiate_prediction_mechanisms(self, context=None):
self.prediction_mechs.append(prediction_mechanism)

# Add to system execution_graph and execution_list
self.system.execution_graph[prediction_mechanism] = set()
self.system.execution_list.append(prediction_mechanism)
system.execution_graph[prediction_mechanism] = set()
system.execution_list.append(prediction_mechanism)

self.prediction_mechanisms = MechanismList(self, self.prediction_mechs)

# Assign list of destinations for predicted_inputs:
# the variable of the ORIGIN Mechanism for each process in the system
# the variable of the ORIGIN Mechanism for each Process in the system
self.predicted_input = {}
for i, origin_mech in zip(range(len(self.system.origin_mechanisms)), self.system.origin_mechanisms):
# self.predicted_input[origin_mech] = self.system.processes[i].origin_mechanisms[0].input_value
self.predicted_input[origin_mech] = self.system.processes[i].origin_mechanisms[0].instance_defaults.variable
for i, origin_mech in zip(range(len(system.origin_mechanisms)), system.origin_mechanisms):
self.predicted_input[origin_mech] = system.processes[i].origin_mechanisms[0].instance_defaults.variable

def _instantiate_attributes_after_function(self, context=None):

Expand Down Expand Up @@ -871,18 +872,8 @@ def _instantiate_attributes_after_function(self, context=None):

@tc.typecheck
def assign_as_controller(self, system:System_Base, context=COMMAND_LINE):
# # MODIFIED 1/15/18 OLD:
# super().assign_as_controller(system=system, context=context)
# self._instantiate_prediction_mechanisms(context=context)
# MODIFIED 1/15/18 NEW:
if system.controller.prediction_mechanisms:
self.prediction_mechanisms = system.controller.prediction_mechanisms
self.origin_prediction_mechanisms = system.controller.origin_prediction_mechanisms
self.predicted_input = system.controller.predicted_input
else:
self._instantiate_prediction_mechanisms(context=context)
self._instantiate_prediction_mechanisms(system=system, context=context)
super().assign_as_controller(system=system, context=context)
# MODIFIED 1/15/18 END

def _execute(self,
variable=None,
Expand Down

0 comments on commit e0f9fbd

Please sign in to comment.