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/input state specs #519

Merged
merged 9 commits into from
Nov 9, 2017
14 changes: 13 additions & 1 deletion Scripts/Scratch Pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,23 @@ def __init__(self, error_value):

#-------------
# m = pnl.TransferMechanism()
# i = pnl.InputState(owner=m, reference_value=[0, 0, 0])
# 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

m = pnl.TransferMechanism(default_variable=[0, 0, 0])
i = pnl.InputState(owner=m, variable=[0, 0, 0])
# m.add_states([i])
# m.execute()
assert True

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

Expand Down
17 changes: 15 additions & 2 deletions psyneulink/components/functions/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@

from psyneulink.components.component import ComponentError, function_type, method_type, parameter_keywords
from psyneulink.components.shellclasses import Function
from psyneulink.globals.keywords import ACCUMULATOR_INTEGRATOR_FUNCTION, ADAPTIVE_INTEGRATOR_FUNCTION, ALL, ANGLE, ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, AUTO_DEPENDENT, BACKPROPAGATION_FUNCTION, BETA, BIAS, COMBINATION_FUNCTION_TYPE, COMBINE_MEANS_FUNCTION, CONSTANT_INTEGRATOR_FUNCTION, CORRELATION, CROSS_ENTROPY, DECAY, DIFFERENCE, DISTANCE_FUNCTION, DISTANCE_METRICS, DIST_FUNCTION_TYPE, DIST_MEAN, DIST_SHAPE, DRIFT_DIFFUSION_INTEGRATOR_FUNCTION, ENERGY, ENTROPY, EUCLIDEAN, EXAMPLE_FUNCTION_TYPE, EXECUTING, EXPONENTIAL_DIST_FUNCTION, EXPONENTIAL_FUNCTION, EXPONENTS, FHN_INTEGRATOR_FUNCTION, FULL_CONNECTIVITY_MATRIX, FUNCTION, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, FUNCTION_PARAMS, GAIN, GAMMA_DIST_FUNCTION, GILZENRAT_INTEGRATOR_FUNCTION, HEBBIAN_FUNCTION, HIGH, HOLLOW_MATRIX, IDENTITY_MATRIX, INCREMENT, INITIALIZER, INITIALIZING, INPUT_STATES, INTEGRATOR_FUNCTION, INTEGRATOR_FUNCTION_TYPE, INTERCEPT, LEARNING_FUNCTION_TYPE, LEARNING_RATE, LINEAR_COMBINATION_FUNCTION, LINEAR_FUNCTION, LINEAR_MATRIX_FUNCTION, LOGISTIC_FUNCTION, LOW, MATRIX, MATRIX_KEYWORD_NAMES, MATRIX_KEYWORD_VALUES, MAX_INDICATOR, MAX_VAL, NOISE, NORMAL_DIST_FUNCTION, OBJECTIVE_FUNCTION_TYPE, OFFSET, OPERATION, ORNSTEIN_UHLENBECK_INTEGRATOR_FUNCTION, OUTPUT_STATES, OUTPUT_TYPE, PARAMETER_STATE_PARAMS, PEARSON, PROB, PRODUCT, RANDOM_CONNECTIVITY_MATRIX, RATE, RECEIVER, REDUCE_FUNCTION, RL_FUNCTION, SCALE, SIMPLE_INTEGRATOR_FUNCTION, SLOPE, SOFTMAX_FUNCTION, STABILITY_FUNCTION, STANDARD_DEVIATION, SUM, TIME_STEP_SIZE, TRANSFER_FUNCTION_TYPE, UNIFORM_DIST_FUNCTION, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE, UTILITY_INTEGRATOR_FUNCTION, WALD_DIST_FUNCTION, WEIGHTS, kwComponentCategory, kwPreferenceSetName
from psyneulink.globals.keywords import VARIABLE, ACCUMULATOR_INTEGRATOR_FUNCTION, ADAPTIVE_INTEGRATOR_FUNCTION, ALL, \
ANGLE, ARGUMENT_THERAPY_FUNCTION, AUTO_ASSIGN_MATRIX, AUTO_DEPENDENT, BACKPROPAGATION_FUNCTION, BETA, BIAS, COMBINATION_FUNCTION_TYPE, COMBINE_MEANS_FUNCTION, CONSTANT_INTEGRATOR_FUNCTION, CORRELATION, CROSS_ENTROPY, DECAY, DIFFERENCE, DISTANCE_FUNCTION, DISTANCE_METRICS, DIST_FUNCTION_TYPE, DIST_MEAN, DIST_SHAPE, DRIFT_DIFFUSION_INTEGRATOR_FUNCTION, ENERGY, ENTROPY, EUCLIDEAN, EXAMPLE_FUNCTION_TYPE, EXECUTING, EXPONENTIAL_DIST_FUNCTION, EXPONENTIAL_FUNCTION, EXPONENTS, FHN_INTEGRATOR_FUNCTION, FULL_CONNECTIVITY_MATRIX, FUNCTION, FUNCTION_OUTPUT_TYPE, FUNCTION_OUTPUT_TYPE_CONVERSION, FUNCTION_PARAMS, GAIN, GAMMA_DIST_FUNCTION, GILZENRAT_INTEGRATOR_FUNCTION, HEBBIAN_FUNCTION, HIGH, HOLLOW_MATRIX, IDENTITY_MATRIX, INCREMENT, INITIALIZER, INITIALIZING, INPUT_STATES, INTEGRATOR_FUNCTION, INTEGRATOR_FUNCTION_TYPE, INTERCEPT, LEARNING_FUNCTION_TYPE, LEARNING_RATE, LINEAR_COMBINATION_FUNCTION, LINEAR_FUNCTION, LINEAR_MATRIX_FUNCTION, LOGISTIC_FUNCTION, LOW, MATRIX, MATRIX_KEYWORD_NAMES, MATRIX_KEYWORD_VALUES, MAX_INDICATOR, MAX_VAL, NOISE, NORMAL_DIST_FUNCTION, OBJECTIVE_FUNCTION_TYPE, OFFSET, OPERATION, ORNSTEIN_UHLENBECK_INTEGRATOR_FUNCTION, OUTPUT_STATES, OUTPUT_TYPE, PARAMETER_STATE_PARAMS, PEARSON, PROB, PRODUCT, RANDOM_CONNECTIVITY_MATRIX, RATE, RECEIVER, REDUCE_FUNCTION, RL_FUNCTION, SCALE, SIMPLE_INTEGRATOR_FUNCTION, SLOPE, SOFTMAX_FUNCTION, STABILITY_FUNCTION, STANDARD_DEVIATION, SUM, TIME_STEP_SIZE, TRANSFER_FUNCTION_TYPE, UNIFORM_DIST_FUNCTION, USER_DEFINED_FUNCTION, USER_DEFINED_FUNCTION_TYPE, UTILITY_INTEGRATOR_FUNCTION, WALD_DIST_FUNCTION, WEIGHTS, kwComponentCategory, kwPreferenceSetName
from psyneulink.globals.preferences.componentpreferenceset import is_pref_set, kpReportOutputPref, kpRuntimeParamStickyAssignmentPref
from psyneulink.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel
from psyneulink.globals.registry import register_category
Expand Down Expand Up @@ -2346,8 +2347,20 @@ def function(self,
intercept = self.paramsCurrent[INTERCEPT]
outputType = self.functionOutputType

# MODIFIED 11/9/17 NEW:
try:
# By default, result should be returned as np.ndarray with same dimensionality as input
result = variable * slope + intercept
result = variable * slope + intercept
except TypeError:
# If variable is an array with mixed sizes or types, try item-by-item operation
if variable.dtype == object:
result = np.zeros_like(variable)
for i, item in enumerate(variable):
result[i] = variable[i] * slope + intercept
else:
raise FunctionError("Unrecognized type for {} of {} ({})".format(VARIABLE, self.name, variable))
# MODIFIED 11/9/17 END


# region Type conversion (specified by outputType):
# Convert to 2D array, irrespective of variable type:
Expand Down
46 changes: 36 additions & 10 deletions psyneulink/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,14 +710,16 @@ class `UserList <https://docs.python.org/3.6/library/collections.html?highlight=
from psyneulink.components.component import Component, InitStatus, ExecutionStatus, function_type, method_type
from psyneulink.components.shellclasses import Function, Mechanism, Projection, State
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.globals.defaults import timeScaleSystemDefault
from psyneulink.globals.keywords import \
CHANGED, COMMAND_LINE, EVC_SIMULATION, EXECUTING, FUNCTION_PARAMS, \
INITIALIZING, INIT_FUNCTION_METHOD_ONLY, INIT__EXECUTE__METHOD_ONLY, \
INPUT_STATES, INPUT_STATE_PARAMS, MECHANISM_TIME_SCALE, MONITOR_FOR_CONTROL, MONITOR_FOR_LEARNING, \
NO_CONTEXT, OUTPUT_STATES, OUTPUT_STATE_PARAMS, PARAMETER_STATE_PARAMS, PROCESS_INIT, SEPARATOR_BAR, \
SET_ATTRIBUTE, SYSTEM_INIT, TIME_SCALE, UNCHANGED, VALIDATE, VARIABLE, VALUE, REFERENCE_VALUE, \
NO_CONTEXT, OUTPUT_STATES, OUTPUT_STATE_PARAMS, PARAMETER_STATES, PARAMETER_STATE_PARAMS, PROCESS_INIT, \
SEPARATOR_BAR, SET_ATTRIBUTE, SYSTEM_INIT, TIME_SCALE, UNCHANGED, VALIDATE, VARIABLE, VALUE, REFERENCE_VALUE, \
kwMechanismComponentCategory, kwMechanismExecuteFunction
from psyneulink.globals.preferences.preferenceset import PreferenceLevel
from psyneulink.globals.registry import register_category
Expand Down Expand Up @@ -1012,6 +1014,10 @@ class ClassDefaults(Mechanism.ClassDefaults):
variableEncodingDim = 2
valueEncodingDim = 2

state_list_attr = {InputState:INPUT_STATES,
ParameterState:PARAMETER_STATES,
OutputState:OUTPUT_STATES}

# Category specific defaults:
paramClassDefaults = Component.paramClassDefaults.copy()
paramClassDefaults.update({
Expand Down Expand Up @@ -1071,7 +1077,8 @@ def __init__(self,

def spec_incompatible_with_default_error(spec_variable, default_variable):
return MechanismError(
'default variable determined from the specified input_states spec ({0}) is not compatible with the specified default variable ({1})'.format(
'default variable determined from the specified input_states spec ({0}) '
'is not compatible with the specified default variable ({1})'.format(
spec_variable, default_variable
)
)
Expand Down Expand Up @@ -1288,11 +1295,11 @@ def _parse_arg_input_states(self, input_states):
elif isinstance(parsed_spec, (Projection, Mechanism, State)):
if parsed_spec.init_status is InitStatus.DEFERRED_INITIALIZATION:
args = parsed_spec.init_args
if REFERENCE_VALUE in args:
if REFERENCE_VALUE in args and args[REFERENCE_VALUE] is not None:
variable = args[REFERENCE_VALUE]
elif VALUE in args:
elif VALUE in args and args[VALUE] is not None:
variable = args[VALUE]
elif VARIABLE in args:
elif VARIABLE in args and args[VARIABLE] is not None:
variable = args[VARIABLE]
else:
try:
Expand Down Expand Up @@ -2195,11 +2202,11 @@ def add_states(self, states, context=COMMAND_LINE):
Mechanism to which it is being added, the user is given the option of reassigning the State to the `owner
<State_Base.owner>`, making a copy of the State and assigning that to the `owner <State_Base.owner>`, or
aborting. If the name of a specified State is the same as an existing one with the same name, an index is
appended to its name, and incremented for each State subsequently added with the same name
(see :ref:`naming conventions <LINK>`).
appended to its name, and incremented for each State subsequently added with the same name (see :ref:`naming
conventions <LINK>`). If a specified State already belongs to the Mechanism, the request is ignored.

.. note::
Adding States to a Mechanism changes the size of its `variable <Mechanism_Base.variable>` attribute,
Adding InputStates to a Mechanism changes the size of its `variable <Mechanism_Base.variable>` attribute,
which may produce an incompatibility with its `function <Mechanism_Base.function>` (see
`Mechanism InputStates` for a more detailed explanation).

Expand All @@ -2212,6 +2219,11 @@ def add_states(self, states, context=COMMAND_LINE):
`State specification dictionary <State_Specification>` (the latter must have a *STATE_TYPE* entry
specifying the class or keyword for InputState or OutputState).

Returns
-------

Dictionary with entries containing InputStates and/or OutputStates added

"""
from psyneulink.components.states.state import _parse_state_type
from psyneulink.components.states.inputstate import InputState, _instantiate_input_states
Expand All @@ -2237,7 +2249,21 @@ def add_states(self, states, context=COMMAND_LINE):

# _instantiate_state_list(self, input_states, InputState)
if input_states:
instantiated_input_states = _instantiate_input_states(self, input_states, context=context)
# FIX: 11/9/17
added_variable, added_input_state = self._parse_arg_input_states(input_states)
if added_input_state:
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,
added_variable,
context=context)
# instantiated_input_states = self._instantiate_input_states(input_states, context=context)
if output_states:
instantiated_output_states = _instantiate_output_states(self, output_states, context=context)

Expand Down
12 changes: 6 additions & 6 deletions psyneulink/components/states/inputstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ def _instantiate_function(self, context=None):
:return:
"""

super(InputState, self)._instantiate_function(context=context)
super()._instantiate_function(context=context)

# Insure that function is Function.LinearCombination
if not isinstance(self.function.__self__, (LinearCombination, Linear)):
Expand All @@ -729,14 +729,14 @@ def _instantiate_function(self, context=None):
self.owner.name,
self.function.__self__.componentName, ))

# Insure that self.value is compatible with (relevant item of) self.reference_value
# Insure that self.value is compatible with self.reference_value
if not iscompatible(self.value, self.reference_value):
raise InputStateError("Value ({}) of {} {} for {} is not compatible with "
"the variable ({}) of its function".
raise InputStateError("Value ({}) of {} {} for {} is not compatible with specified {} ({})".
format(self.value,
self.componentName,
self.name,
self.owner.name,
REFERENCE_VALUE,
self.reference_value))
# self.owner.variable))

Expand Down Expand Up @@ -909,7 +909,7 @@ def pathway_projections(self, assignment):


# def _instantiate_input_states(owner, input_states=None, context=None):
def _instantiate_input_states(owner, input_states=None, context=None):
def _instantiate_input_states(owner, input_states=None, reference_value=None, context=None):
"""Call State._instantiate_state_list() to instantiate ContentAddressableList of InputState(s)

Create ContentAddressableList of InputState(s) specified in paramsCurrent[INPUT_STATES]
Expand Down Expand Up @@ -944,7 +944,7 @@ def _instantiate_input_states(owner, input_states=None, context=None):
state_list=input_states,
state_type=InputState,
state_param_identifier=INPUT_STATE,
reference_value=owner.instance_defaults.variable,
reference_value=reference_value or owner.instance_defaults.variable,
reference_value_name=VARIABLE,
context=context)

Expand Down
33 changes: 24 additions & 9 deletions psyneulink/components/states/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def test_multiple_modulatory_projections_with_mech_and_state_name_specs(self):
from psyneulink.components.functions.function import LinearCombination, ModulationParam, _get_modulated_param, get_param_value_for_function, get_param_value_for_keyword
from psyneulink.components.shellclasses import Mechanism, Process_Base, Projection, State
from psyneulink.globals.keywords import \
CONTEXT, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, EXECUTING, FUNCTION, FUNCTION_PARAMS, \
CONTEXT, COMMAND_LINE, CONTROL_PROJECTION_PARAMS, CONTROL_SIGNAL_SPECS, EXECUTING, FUNCTION, FUNCTION_PARAMS, \
GATING_PROJECTION_PARAMS, GATING_SIGNAL_SPECS, INITIALIZING, \
LEARNING, LEARNING_PROJECTION_PARAMS, LEARNING_SIGNAL_SPECS, \
MAPPING_PROJECTION_PARAMS, MECHANISM, \
Expand Down Expand Up @@ -965,6 +965,13 @@ def __init__(self,
# if params = NotImplemented or there is no param[PROJECTIONS]
pass

# if owner:
# assert True
# state_list = getattr(owner, owner.state_list_attr[self.__class__])
# if state_list and not self in state_list:
# owner.add_states(self)


def _handle_size(self, size, variable):
"""Overwrites the parent method in Component.py, because the variable of a State
is generally 1D, rather than 2D as in the case of Mechanisms"""
Expand Down Expand Up @@ -2155,9 +2162,13 @@ def _instantiate_state(state_type:_is_state_class, # State's type

# FIX: THIS SHOULD ONLY APPLY TO InputState AND ParameterState; WHAT ABOUT OutputState?
# State's assigned value is incompatible with its reference_value (presumably its owner Mechanism's variable)
if not iscompatible(state.value, reference_value):
raise StateError("{}'s value attribute ({}) is incompatible with the variable ({}) of its owner ({})".
format(state.name, state.value, state.reference_value, owner.name))
# MODIFIED 11/9/17 OLD:
# if not iscompatible(state.value, reference_value):
# MODIFIED 11/9/17 NEW:
if not iscompatible(state.value, state.reference_value):
# MODIFIED 11/9/17 END
raise StateError("{}'s value attribute ({}) is incompatible with the {} ({}) of its owner ({})".
format(state.name, state.value, REFERENCE_VALUE, state.reference_value, owner.name))

# State has already been assigned to an owner
if state.owner is not None and not state.owner is owner:
Expand Down Expand Up @@ -2402,13 +2413,17 @@ def _parse_state_spec(state_type=None,
if isinstance(state_specification, state_type):
# Make sure that the specified State belongs to the Mechanism passed in the owner arg
if state_specification.init_status is InitStatus.DEFERRED_INITIALIZATION:
owner = state_specification.init_args[OWNER]
state_owner = state_specification.init_args[OWNER]
else:
owner = state_specification.owner
if owner is not None and not state_specification.owner is owner:
state_owner = state_specification.owner
if owner is not None and not state_owner is owner:
raise StateError("The State specified in a call to _instantiate_state ({}) "
"does belong to the {} specified in the \'{}\' argument ({})".
format(state_specification.name, owner.name, Mechanism.__name__, OWNER, owner.name))
"does not belong to the {} specified in the \'{}\' argument ({})".
format(state_specification.name,
owner.name,
Mechanism.__name__,
OWNER,
state_owner.name))
return state_specification

else:
Expand Down