Skip to content

Commit

Permalink
Fix/input state specs (#521)
Browse files Browse the repository at this point in the history
* -

* Merge branch 'devel' of https://github.com/PrincetonUniversity/PsyNeuLink into CURRENT

# Conflicts:
#	tests/mechanisms/test_transfer_mechanism.py

* Merge branch 'devel' of https://github.com/PrincetonUniversity/PsyNeuLink into CURRENT

# Conflicts:
#	tests/mechanisms/test_transfer_mechanism.py

* • Mechanism, PathwayProjection
  - fixed bugs related to input_state specifications

* • Mechanism, PathwayProjection
  - fixed bugs related to input_state specifications

* • Mechanism
  - _parse_arg_input_states, add_states:  modified to accomdodate deferred_init

* • State
  - _parse_state_spec re: owner name

* • State
  - _parse_state_spec re: owner name

* • State
  - _parse_state_spec re: owner name

* • States
  __init__: now calls add_states to add state to owner if called from COMMAND_LINE

* • States
  __init__: now calls add_states to add state to owner if called from COMMAND_LINE
  add_states works for deferred_init state
  assignment of State to owner now works

* • States
  __init__: now calls add_states to add state to owner if called from COMMAND_LINE
  add_states works for deferred_init state
  assignment of State to owner now works

* -

* -
  • Loading branch information
jdcpni committed Nov 10, 2017
1 parent 913f0be commit 59c418e
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 80 deletions.
42 changes: 23 additions & 19 deletions Scripts/Scratch Pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,35 +412,39 @@ def __init__(self, error_value):

# FROM KEVIN: -------------------------------------

# WORKING:
# I = pnl.InputState(reference_value=[0,0,0])
# pnl.TransferMechanism(input_states=[I])

# p = pnl.MappingProjection()
# T = pnl.TransferMechanism(input_states=[{pnl.VARIABLE: [0, 0, 0], pnl.PROJECTIONS:[p]}])
#
# p = pnl.MappingProjection()
# T = pnl.TransferMechanism(default_variable=[0, 0], input_states=[p])
I = pnl.InputState(reference_value=[0,0,0])
pnl.TransferMechanism(input_states=[I])

p = pnl.MappingProjection()
T = pnl.TransferMechanism(input_states=[{pnl.VARIABLE: [0, 0, 0], pnl.PROJECTIONS:[p]}])

p = pnl.MappingProjection()
T = pnl.TransferMechanism(default_variable=[0, 0], input_states=[p])

#-------------
# m = pnl.TransferMechanism()
# i = pnl.InputState(owner=m, variable=[0, 0, 0], reference_value=[0,0,0])

# m = pnl.TransferMechanism(default_variable=[0, 0, 0])
# i = pnl.InputState(owner=m, reference_value=[0, 0, 0])

# WORKS:
m = pnl.TransferMechanism()
i = pnl.InputState(variable=[0,0])
m.add_states([i])
m.execute()
assert True
assert len(m.input_states) == 2
assert len(m.variable)==2
assert len(m.variable[0])==1
assert len(m.variable[1])==2
assert m.input_states[0].name == 'INPUT_STATE-0'
assert m.input_states[1].name == 'INPUT_STATE-1'

# m = pnl.TransferMechanism(default_variable=[0, 0, 0])
# i = pnl.InputState(owner=m, variable=[0, 0, 0])
# m.add_states([i])
m = pnl.TransferMechanism()
i = pnl.InputState(owner=m, variable=[0, 0, 0])
m.execute()
# assert True
assert len(m.input_states) == 2
assert m.input_states[0].name == 'INPUT_STATE-0'
assert m.input_states[1].name == 'INPUT_STATE-1'
assert len(m.variable)==2
assert len(m.variable[0])==1
assert len(m.variable[1])==3


# --------------------------------------------------------------------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions psyneulink/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ def _deferred_init(self, context=None):
# the value is stored in paramClassDefaults in assign_ags_to_params_dicts,
# and will be restored in _instantiate_function
try:
del self.init_args['function']
del self.init_args[FUNCTION]
except KeyError:
pass

Expand All @@ -1064,7 +1064,7 @@ def _deferred_init(self, context=None):

# If name is None, mark as deferred so that name can be customized
# using info that has become available at time of deferred init
self.init_args['name'] = (self.init_args['name'] or
self.init_args[NAME] = (self.init_args[NAME] or
('deferred_init_' + self.className) or
DEFERRED_DEFAULT_NAME)

Expand Down
25 changes: 13 additions & 12 deletions psyneulink/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ class `UserList <https://docs.python.org/3.6/library/collections.html?highlight=
from psyneulink.components.states.inputstate import InputState
from psyneulink.components.states.parameterstate import ParameterState
from psyneulink.components.states.outputstate import OutputState
from psyneulink.components.states.state import _parse_state_spec
from psyneulink.components.states.state import _parse_state_spec, ADD_STATES
from psyneulink.globals.defaults import timeScaleSystemDefault
from psyneulink.globals.keywords import \
CHANGED, COMMAND_LINE, EVC_SIMULATION, EXECUTING, FUNCTION_PARAMS, \
Expand Down Expand Up @@ -2000,13 +2000,14 @@ def _get_variable_from_input(self, input):
# Check if inputs are of different lengths (indicated by dtype == np.dtype('O'))
num_inputs = np.size(input)
if isinstance(input, np.ndarray) and input.dtype is np.dtype('O') and num_inputs == num_input_states:
pass
# Reduce input back down to sequence of arrays (to remove extra dim added by atleast_2d above)
input = np.squeeze(input)
else:
num_inputs = np.size(input, 0) # revert num_inputs to its previous value, when printing the error
raise SystemError("Number of inputs ({0}) to {1} does not match "
"its number of input_states ({2})".
format(num_inputs, self.name, num_input_states ))
for i in range(num_input_states):
for i, input_state in enumerate(self.input_states):
# input_state = list(self.input_states.values())[i]
input_state = self.input_states[i]
# input_item = np.ndarray(input[i])
Expand All @@ -2022,7 +2023,7 @@ def _get_variable_from_input(self, input):
input[i],
len(input_state.instance_defaults.variable),
input_state.name,
append_type_to_name(self),
self.name
)
)

Expand Down Expand Up @@ -2190,7 +2191,7 @@ def plot(self, x_range=None):
plt.show()

@tc.typecheck
def add_states(self, states, context=COMMAND_LINE):
def add_states(self, states, context=ADD_STATES):
"""
add_states(states)
Expand Down Expand Up @@ -2239,6 +2240,7 @@ def add_states(self, states, context=COMMAND_LINE):
instantiated_output_states = None

for state in states:
# FIX: 11/9/17: REFACTOR USING _parse_state_spec
state_type = _parse_state_type(self, state)
if (isinstance(state_type, InputState) or
(inspect.isclass(state_type) and issubclass(state_type, InputState))):
Expand All @@ -2255,15 +2257,14 @@ def add_states(self, states, context=COMMAND_LINE):
old_variable = self.instance_defaults.variable.tolist()
old_variable.extend(added_variable)
self.instance_defaults.variable = np.array(old_variable)
# FIX: 11/8/17 - INCLUDE OR NOT:
self.function_object.instance_defaults.variable = self.instance_defaults.variable
self.function_object.variableClassDefault = self.instance_defaults.variable
self.value = self.function()
# FIX END
instantiated_input_states = _instantiate_input_states(self, input_states,
self._update_variable(self.instance_defaults.variable)
instantiated_input_states = _instantiate_input_states(self,
input_states,
added_variable,
context=context)
# instantiated_input_states = self._instantiate_input_states(input_states, context=context)
for state in instantiated_input_states:
if state.name is state.componentName or state.componentName + '-' in state.name:
state._assign_default_name(context=context)
if output_states:
instantiated_output_states = _instantiate_output_states(self, output_states, context=context)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,19 +712,14 @@ def _execute(self,
if (np.array(noise) != 0).any():
current_input = variable[0] + noise
else:

current_input = variable[0]

# self.previous_input = current_input

# Apply TransferMechanism function
output_vector = self.function(variable=current_input, params=runtime_params)

# # MODIFIED OLD:
# if list(range):
# MODIFIED NEW:
if range is not None:
# MODIFIED END
minCapIndices = np.where(output_vector < range[0])
maxCapIndices = np.where(output_vector > range[1])
output_vector[minCapIndices] = np.min(range)
Expand Down
53 changes: 38 additions & 15 deletions psyneulink/components/states/inputstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,12 @@
from psyneulink.components.component import InitStatus
from psyneulink.components.functions.function import Linear, LinearCombination
from psyneulink.components.mechanisms.mechanism import Mechanism
from psyneulink.components.states.state import StateError, State_Base, _instantiate_state_list, state_type_keywords
from psyneulink.components.states.state import \
StateError, State_Base, _instantiate_state_list, state_type_keywords, ADD_STATES
from psyneulink.components.states.outputstate import OutputState
from psyneulink.globals.keywords import EXPONENT, FUNCTION, INPUT_STATE, INPUT_STATE_PARAMS, MAPPING_PROJECTION, \
MECHANISM, OUTPUT_STATES, MATRIX, PROJECTIONS, PROJECTION_TYPE, SUM, VARIABLE, WEIGHT, REFERENCE_VALUE, \
OUTPUT_STATE, PROCESS_INPUT_STATE, SYSTEM_INPUT_STATE, LEARNING_SIGNAL, GATING_SIGNAL, SENDER
OUTPUT_STATE, PROCESS_INPUT_STATE, SYSTEM_INPUT_STATE, LEARNING_SIGNAL, GATING_SIGNAL, SENDER, COMMAND_LINE
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.globals.utilities import append_type_to_name, iscompatible
Expand Down Expand Up @@ -635,17 +636,22 @@ def __init__(self,
prefs:is_pref_set=None,
context=None):

if context is None:
context = COMMAND_LINE
else:
context = self

# Assign args to params and functionParams dicts (kwConstants must == arg names)
params = self._assign_args_to_param_dicts(function=function,
weight=weight,
exponent=exponent,
params=params)

# If owner or reference_value has not been assigned, defer init to State._instantiate_projection()
if owner is None or reference_value is None:
if owner is None or (variable is None and reference_value is None):
# Store args for deferred initialization
self.init_args = locals().copy()
self.init_args['context'] = self
self.init_args['context'] = context
self.init_args['name'] = name
self.init_args['projections'] = projections

Expand All @@ -664,10 +670,10 @@ def __init__(self,
params=params,
name=name,
prefs=prefs,
context=self)
context=context)

if self.name is self.componentName or self.componentName + '-' in self.name:
self._assign_default_name()
self._assign_default_name(context=context)


def _validate_params(self, request_set, target_set=None, context=None):
Expand Down Expand Up @@ -730,7 +736,7 @@ def _instantiate_function(self, context=None):
self.function.__self__.componentName, ))

# Insure that self.value is compatible with self.reference_value
if not iscompatible(self.value, self.reference_value):
if self.reference_value is not None and not iscompatible(self.value, self.reference_value):
raise InputStateError("Value ({}) of {} {} for {} is not compatible with specified {} ({})".
format(self.value,
self.componentName,
Expand Down Expand Up @@ -773,13 +779,29 @@ def _execute(self, function_params, context):
def _get_primary_state(self, mechanism):
return mechanism.input_state

def _assign_default_name(self):
# """Assign index of '-1' to first default InputState
# Subsequent ones are indexed sequentially by Registry starting with '-2'
def _assign_default_name(self, context=None):
# """Assign 'INPUT_STATE-n' to any InputStates with default name (i.e., name of State: 'InputState'),
# where n is the next index of InputStates with the default name
# Returns name assigned to State
# """
# if self.name == self.componentName:
# self.name = self.name+'-1'
self.name = self.name.replace(self.componentName, 'INPUT_STATE')

# Call for State being instantiated in the context of constructing its owner
if isinstance(context,State_Base):
self.name = self.name.replace(self.componentName, 'INPUT_STATE')

# Call in the context of adding a state to an existing owner
elif ADD_STATES in context:
try:
i=len([input_state for input_state in self.owner.input_states if 'INPUT_STATE-' in input_state.name])
self.name = 'INPUT_STATE-'+str(i)
except TypeError:
i=0
self.name = 'INPUT_STATE-'+str(i)

else:
raise InputStateError("PROGRAM ERROR: unrecognize context ({}) for assigning {} to {}".
format(context, NAME, InputState.__name__))
return self.name

# MODIFIED 9/30/17 NEW:
@tc.typecheck
Expand Down Expand Up @@ -944,12 +966,13 @@ def _instantiate_input_states(owner, input_states=None, reference_value=None, co
state_list=input_states,
state_type=InputState,
state_param_identifier=INPUT_STATE,
reference_value=reference_value or owner.instance_defaults.variable,
reference_value=reference_value if reference_value is not None
else owner.instance_defaults.variable,
reference_value_name=VARIABLE,
context=context)

# Call from Mechanism.add_states, so add to rather than assign input_states (i.e., don't replace)
if context and 'COMMAND_LINE' in context:
if context and 'ADD_STATES' in context:
owner.input_states.extend(state_list)
else:
owner._input_states = state_list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@
from psyneulink.globals.defaults import defaultControlAllocation
from psyneulink.globals.keywords import \
ALLOCATION_SAMPLES, AUTO, CONTROLLED_PARAMS, CONTROL_PROJECTION, CONTROL_SIGNAL, EXECUTING, \
FUNCTION, FUNCTION_PARAMS, INTERCEPT, MECHANISM, MODULATION, NAME, OFF, ON, \
FUNCTION, FUNCTION_PARAMS, INTERCEPT, COMMAND_LINE, OFF, ON, \
PARAMETER_STATE, PARAMETER_STATES, OUTPUT_STATE_PARAMS, \
PROJECTION_TYPE, RECEIVER, SEPARATOR_BAR, SLOPE, SUM, kwAssign
from psyneulink.globals.log import LogEntry, LogLevel
Expand Down Expand Up @@ -665,6 +665,11 @@ def __init__(self,
prefs:is_pref_set=None,
context=None):

if context is None:
context = COMMAND_LINE
else:
context = self

# Note index and calculate are not used by ControlSignal, but included here for consistency with OutputState
if params and ALLOCATION_SAMPLES in params and params[ALLOCATION_SAMPLES] is not None:
allocation_samples = params[ALLOCATION_SAMPLES]
Expand Down Expand Up @@ -702,7 +707,7 @@ def __init__(self,
params=params,
name=name,
prefs=prefs,
context=self)
context=context)

def _validate_params(self, request_set, target_set=None, context=None):
"""Validate allocation_samples and control_signal cost functions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@
from psyneulink.components.states.outputstate import OutputState, PRIMARY, SEQUENTIAL
from psyneulink.components.states.state import State_Base, State
from psyneulink.globals.keywords import \
MECHANISM, NAME, GATING_PROJECTION, GATING_SIGNAL, GATE, RECEIVER, SUM, PROJECTION_TYPE, \
COMMAND_LINE, GATING_PROJECTION, GATING_SIGNAL, GATE, RECEIVER, SUM, PROJECTION_TYPE, \
INPUT_STATE, INPUT_STATES, OUTPUT_STATE, OUTPUT_STATES, OUTPUT_STATE_PARAMS
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
Expand Down Expand Up @@ -430,6 +430,11 @@ def __init__(self,
prefs:is_pref_set=None,
context=None):

if context is None:
context = COMMAND_LINE
else:
context = self

# Note: calculate is not currently used by GatingSignal;
# it is included here for consistency with OutputState and possible use by subclasses.
if index is None and owner is not None:
Expand Down Expand Up @@ -459,7 +464,7 @@ def __init__(self,
params=params,
name=name,
prefs=prefs,
context=self)
context=context)

def _execute(self, function_params, context):
return float(super()._execute(function_params=function_params, context=context))
Expand Down
13 changes: 9 additions & 4 deletions psyneulink/components/states/modulatorysignals/learningsignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
from psyneulink.components.states.outputstate import PRIMARY
from psyneulink.components.states.state import State_Base
from psyneulink.globals.keywords import \
LEARNED_PARAM, LEARNING_SIGNAL, LEARNING_PROJECTION, \
COMMAND_LINE, LEARNED_PARAM, LEARNING_SIGNAL, LEARNING_PROJECTION, \
PROJECTION_TYPE, RECEIVER, OUTPUT_STATE_PARAMS, PARAMETER_STATE, PARAMETER_STATES, SUM
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
Expand Down Expand Up @@ -375,6 +375,11 @@ def __init__(self,
prefs:is_pref_set=None,
context=None):

if context is None:
context = COMMAND_LINE
else:
context = self

# Assign args to params and functionParams dicts (kwConstants must == arg names)
params = self._assign_args_to_param_dicts(function=function,
learning_rate=learning_rate,
Expand All @@ -397,7 +402,7 @@ def __init__(self,
params=params,
name=name,
prefs=prefs,
context=self)
context=context)

def _get_primary_state(self, projection):
return projection.parameter_state
Expand All @@ -406,12 +411,12 @@ def _get_primary_state(self, projection):
def learning_signal(self):
return self.value

def _assign_default_name(self):
def _assign_default_name(self, context=None):
# Preserve LEARNING_SIGNAL as name of the first LearningSignal
# as documented, and as it is used by System._instantiate_learning_graph
if self.name is self.componentName:
return self.name
# Otherwise, allow ModulatorySignal to construct default name as usual
else:
super()._assign_default_name()
super()._assign_default_name(context=context)

Loading

0 comments on commit 59c418e

Please sign in to comment.