Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/composition/add controller #1304

Merged
merged 33 commits into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .idea/runConfigurations/Make_HTML.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/Scratch_Pad.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions .idea/runConfigurations/pytest_in_tests2.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ def print_after():
stimuli = {color_input:[red]*num_trials,
word_input:[green]*num_trials,
task_input:[color]*num_trials}
Stroop_model.run(inputs=stimuli,
animate={'show_controller':True,
# 'show_cim':True
},
call_after_trial=print_after)
# Stroop_model.run(inputs=stimuli,
# animate={'show_controller':True,
# # 'show_cim':True
# },
# call_after_trial=print_after)

Stroop_model.log.print_entries(display=[TIME, VALUE])
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,8 @@ def assign_as_controller(self, system:System_Base, context=None):
# Add any ControlSignals specified for System
for control_signal_spec in system_control_signals:
control_signal = self._instantiate_control_signal(control_signal=control_signal_spec, context=context)
if not control_signal:
continue
# FIX: 1/18/18 - CHECK FOR SAME NAME IN _instantiate_control_signal
# # Don't add any that are already on the ControlMechanism
if control_signal.name in self.control_signals.names and (self.verbosePref or system.verbosePref):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,10 @@
from psyneulink.core.components.states.parameterstate import ParameterState
from psyneulink.core.globals.context import ContextFlags, handle_external_context
from psyneulink.core.globals.defaults import defaultControlAllocation, defaultGatingAllocation
from psyneulink.core.globals.keywords import AUTO_ASSIGN_MATRIX, CONTEXT, \
CONTROL, CONTROL_PROJECTIONS, CONTROL_SIGNALS, EID_SIMULATION, GATING_SIGNALS, INIT_EXECUTE_METHOD_ONLY, \
MODULATORY_SIGNAL, MODULATORY_SIGNALS, MONITOR_FOR_MODULATION, \
from psyneulink.core.globals.keywords import \
AUTO_ASSIGN_MATRIX, CONTEXT, CONTROL, CONTROL_PROJECTIONS, CONTROL_SIGNAL, CONTROL_SIGNALS, \
EID_SIMULATION, GATING_SIGNAL, GATING_SIGNALS, INIT_EXECUTE_METHOD_ONLY, \
MODULATORY_PROJECTION, MODULATORY_SIGNAL, MODULATORY_SIGNALS, MONITOR_FOR_MODULATION, \
OBJECTIVE_MECHANISM, OUTCOME, OWNER_VALUE, PRODUCT, PROJECTIONS, SYSTEM
from psyneulink.core.globals.parameters import Parameter
from psyneulink.core.globals.preferences.componentpreferenceset import is_pref_set
Expand Down Expand Up @@ -1346,10 +1347,8 @@ def _instantiate_modulatory_signal(self, modulatory_signal, context=None):
try:
modulatory_signal = _instantiate_state(state_type=ControlSignal,
owner=self,
# variable=self.parameters.control_allocation.default_value,
variable=self.default_allocation or
self.parameters.control_allocation.default_value,
# reference_value=ControlSignal.defaults.allocation,
reference_value=self.parameters.control_allocation.default_value,
modulation=self.modulation,
state_spec=mod_spec,
Expand All @@ -1374,6 +1373,43 @@ def _instantiate_modulatory_signal(self, modulatory_signal, context=None):

modulatory_signal.owner = self

# Check that modulatory_signal is not a duplicate of one already instantiated for the ModulatoryMechanism
# (viz., if control of parameter was specified both in constructor for Mechanism and in ModulatoryMechanism)
for existing_mod_sig in [ms for ms in self._modulatory_signals if isinstance(ms, ModulatorySignal)]:

# OK if modulatory_signal is one already assigned to ModulatoryMechanism (i.e., let it get processed below);
# this can happen if it was in deferred_init status and initalized in call to _instantiate_state above.
if modulatory_signal == existing_mod_sig:
continue

# # MODIFIED 9/14/19 NEW:
# # Return if *all* projections from modulatory_signal are identical to ones in an existing modulatory_signal
# if all(
# any(new_p.receiver == existing_p.receiver
# for existing_p in existing_mod_sig.efferents) for new_p in modulatory_signal.efferents):
# if self.verbosePref:
# warnings.warn(f"Specification of {modulatory_signal.name} for {self.name} "
# f"is redundant with existing one ({existing_mod_sig.name}) so it has been ignored.")
# return
# MODIFIED 9/14/19 NEWER: [JDC]
# Return if *all* projections from modulatory_signal are identical to ones in an existing modulatory_signal
for proj in modulatory_signal.efferents:
if proj not in existing_mod_sig.efferents:
# A Projection in modulatory_signal is not in this existing one: it is different,
# so break and move on to next existing_mod_sig
break
return
# MODIFIED 9/14/19 END

# Warn if *any* projections from modulatory_signal are identical to ones in an existing modulatory_signal
if any(
any(new_p.receiver == existing_p.receiver
for existing_p in existing_mod_sig.efferents) for new_p in modulatory_signal.efferents):
# warnings.warn(f"{modulatory_signal.__class__.__name__} ({modulatory_signal.name}) has ")
warnings.warn(f"Specification of {modulatory_signal.name} for {self.name} "
f"has one or more {MODULATORY_PROJECTION}s redundant with ones already on "
f"an existing {ModulatorySignal.__name__} ({existing_mod_sig.name}).")

if isinstance(modulatory_signal, ControlSignal):
# Update control_signal_costs to accommodate instantiated Projection
control_signal_costs = self.parameters.control_signal_costs._get(context)
Expand All @@ -1385,6 +1421,7 @@ def _instantiate_modulatory_signal(self, modulatory_signal, context=None):

# UPDATE output_states AND modulatory_projections -------------------------------------------------------------

# FIX: 9/14/19 - THIS SHOULD BE IMPLEMENTED
# TBI: For modulatory mechanisms that accumulate, starting output must be equal to the initial "previous value"
# so that modulation that occurs BEFORE the control mechanism executes is computed appropriately
# if (isinstance(self.function, IntegratorFunction)):
Expand Down Expand Up @@ -1583,30 +1620,73 @@ def assign_as_controller(self, system:System_Base, context=None):

self._activate_projections_for_compositions(system)

def _activate_projections_for_compositions(self, compositions=None):
def _remove_default_modulatory_signal(self, type:tc.enum(MODULATORY_SIGNAL, CONTROL_SIGNAL, GATING_SIGNAL)):
if type == MODULATORY_SIGNAL:
mod_sig_attribute = self.modulatory_signals
elif type == CONTROL_SIGNAL:
mod_sig_attribute = self.control_signals
elif type == GATING_SIGNAL:
mod_sig_attribute = self.gating_signals
else:
assert False, \
f"PROGRAM ERROR: bad 'type' arg ({type})passed to " \
f"{ModulatoryMechanism.__name__}._remove_default_modulatory_signal" \
f"(should have been caught by typecheck"

if (len(mod_sig_attribute)==1
and mod_sig_attribute[0].name==type+'-0'
and not mod_sig_attribute[0].efferents):
self.remove_states(mod_sig_attribute[0])

def _activate_projections_for_compositions(self, composition=None):
'''Activate eligible Projections to or from nodes in composition.
If Projection is to or from a node NOT (yet) in the Composition,
assign it the node's aux_components attribute but do not activate it.
'''
dependent_projections = set()

if self.objective_mechanism:
# Safe to add this, as it is already in the ModulatoryMechanism's aux_components
# and will therefore be added to the Composition along with the ModulatoryMechanism
assert self.objective_mechanism in self.aux_components, \
f"PROGRAM ERROR: {OBJECTIVE_MECHANISM} for {self.name} not listed in its 'aux_components' attribute."
dependent_projections.add(self._objective_projection)

for aff in self._objective_mechanism.afferents:
# MODIFIED 9/15/19 OLD:
dependent_projections.add(aff)
# # MODIFIED 9/15/19 NEW: [JDC]
# # NOTE: THIS CAUSES AN ERROR WHEN CONTRROLLER IS ADDED TO COMP SINCE PROJECTION HAS NOT BEEN ACTIVATED
# if aff.sender.owner in composition.nodes:
# dependent_projections.add(aff)
# else:
# aff.sender.owner.aux_components.append(aff)
# MODIFIED 9/15/19 END

for ms in self.modulatory_signals:
for eff in ms.efferents:
# MODIFIED 9/15/19 OLD:
dependent_projections.add(eff)

# # MODIFIED 9/15/19 NEW: [JDC] - SAME PROBLEM AS ABOVE
# if eff.receiver.owner in composition.nodes:
# dependent_projections.add(eff)
# else:
# eff.receiver.owner.aux_components.append(eff)
# MODIFIED 9/15/19 END

# FIX: 9/15/19 - HOW IS THIS DIFFERENT THAN objective_mechanism's AFFERENTS ABOVE?
# assign any deferred init objective mech monitored output state projections to this system
if self.objective_mechanism:
for output_state in self.objective_mechanism.monitored_output_states:
for eff in output_state.efferents:
dependent_projections.add(eff)

# FIX: 9/15/19 - HOW IS THIS DIFFERENT THAN modulatory_signal's EFFERENTS ABOVE?
for eff in self.efferents:
dependent_projections.add(eff)

for proj in dependent_projections:
proj._activate_for_compositions(compositions)
proj._activate_for_compositions(composition)

def _apply_modulatory_allocation(self, modulatory_allocation, runtime_params, context):
"""Update values to `modulatory_signals <ModulatoryMechanism.modulatory_signals>`
Expand Down
46 changes: 40 additions & 6 deletions psyneulink/core/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,19 +949,19 @@ class `UserList <https://docs.python.org/3.6/library/collections.html?highlight=
from psyneulink.core.components.functions.transferfunctions import Linear
from psyneulink.core.components.shellclasses import Function, Mechanism, Projection, State
from psyneulink.core.components.states.inputstate import DEFER_VARIABLE_SPEC_TO_MECH_MSG, InputState
from psyneulink.core.components.states.modulatorysignals.controlsignal import ControlSignal
from psyneulink.core.components.states.modulatorysignals.modulatorysignal import _is_modulatory_spec
from psyneulink.core.components.states.outputstate import OutputState
from psyneulink.core.components.states.parameterstate import ParameterState
from psyneulink.core.components.states.state import REMOVE_STATES, _parse_state_spec
from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context
from psyneulink.core.globals.keywords import \
CONTROL_SIGNAL, CURRENT_EXECUTION_COUNT, CURRENT_EXECUTION_TIME, EXECUTION_PHASE, FUNCTION, FUNCTION_PARAMS, \
CURRENT_EXECUTION_COUNT, CURRENT_EXECUTION_TIME, EXECUTION_PHASE, FUNCTION, FUNCTION_PARAMS, \
INITIALIZING, INIT_EXECUTE_METHOD_ONLY, INIT_FUNCTION_METHOD_ONLY, \
INPUT_LABELS_DICT, INPUT_STATE, INPUT_STATES, INPUT_STATE_VARIABLES, MONITOR_FOR_CONTROL, MONITOR_FOR_LEARNING, \
OUTPUT_LABELS_DICT, OUTPUT_STATE, OUTPUT_STATES, OWNER_VALUE, PARAMETER_STATE, PARAMETER_STATES, PREVIOUS_VALUE, \
OUTPUT_LABELS_DICT, OUTPUT_STATE, OUTPUT_STATES, OWNER_VALUE, \
PARAMETER_STATE, PARAMETER_STATES, PREVIOUS_VALUE, PROJECTIONS, \
REFERENCE_VALUE, TARGET_LABELS_DICT, VALUE, VARIABLE, kwMechanismComponentCategory
from psyneulink.core.globals.parameters import Parameter, parse_context
from psyneulink.core.globals.parameters import Parameter
from psyneulink.core.scheduling.condition import Condition
from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.core.globals.registry import register_category, remove_instance_from_registry
Expand Down Expand Up @@ -2458,6 +2458,28 @@ def _update_parameter_states(self, context=None, runtime_params=None):
state._update(context=context, params=runtime_params)
self._update_attribs_dicts(context=context)

def _get_parameter_state_deferred_init_control_specs(self):
# FIX: 9/14/19 - THIS ASSUMES THAT ONLY CONTROLPROJECTIONS RELEVANT TO COMPOSITION ARE in DEFERRED INIT;
# BUT WHAT IF NODE SPECIFIED CONTROL BY AN EXISTING CONTROLMECHANISM NOT IN A COMPOSITION
# THAT WAS THEN ADDED; COMPOSITION WOULD STILL NEED TO KNOW ABOUT IT TO ACTIVATE THE CTLPROJ
# # MODIFIED 9/15/19 NEW:
# for parameter_state in self._parameter_states:
# for proj in parameter_state.mod_afferents:
# if proj.initialization_status == ContextFlags.DEFERRED_INIT:
# proj_control_signal_specs = proj.control_signal_params or {}
# proj_control_signal_specs.update({PROJECTIONS: [proj]})
# return proj_control_signal_specs
# MODIFIED 9/15/19 NEWER:
ctl_specs = []
for parameter_state in self._parameter_states:
for proj in parameter_state.mod_afferents:
if proj.initialization_status == ContextFlags.DEFERRED_INIT:
proj_control_signal_specs = proj.control_signal_params or {}
proj_control_signal_specs.update({PROJECTIONS: [proj]})
ctl_specs.append(proj_control_signal_specs)
return ctl_specs
# MODIFIED 9/15/19 END

def _update_attribs_dicts(self, context):
from psyneulink.core.globals.keywords import NOISE
for state in self._parameter_states:
Expand Down Expand Up @@ -3334,8 +3356,14 @@ def delete_state_projections(proj_list):
index = self.input_states.index(state)
delete_state_projections(state.path_afferents)
del self.input_states[index]
# If state is subclass of OutputState:
# check if regsistry has category for that class, and if so, use that
category = INPUT_STATE
class_name = state.__class__.__name__
if class_name != INPUT_STATE and class_name in self._stateRegistry:
category = class_name
remove_instance_from_registry(registry=self._stateRegistry,
category=INPUT_STATE,
category=category,
component=state)
old_variable = self.defaults.variable
old_variable = np.delete(old_variable,index,0)
Expand All @@ -3359,8 +3387,14 @@ def delete_state_projections(proj_list):
delete_state_projections(state.efferents)
del self.output_values[index]
del self.output_states[state]
# If state is subclass of OutputState:
# check if regsistry has category for that class, and if so, use that
category = OUTPUT_STATE
class_name = state.__class__.__name__
if class_name != OUTPUT_STATE and class_name in self._stateRegistry:
category = class_name
remove_instance_from_registry(registry=self._stateRegistry,
category=OUTPUT_STATE,
category=category,
component=state)

self.defaults.variable = self.input_values
Expand Down
Loading