diff --git a/Scripts/Scratch Pad.py b/Scripts/Scratch Pad.py index dedbc3b37f7..315d2d0fc22 100644 --- a/Scripts/Scratch Pad.py +++ b/Scripts/Scratch Pad.py @@ -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 # -------------------------------------------------------------------------------------------------- diff --git a/psyneulink/components/functions/function.py b/psyneulink/components/functions/function.py index c2178169584..48f6e016a36 100644 --- a/psyneulink/components/functions/function.py +++ b/psyneulink/components/functions/function.py @@ -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 @@ -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: diff --git a/psyneulink/components/mechanisms/mechanism.py b/psyneulink/components/mechanisms/mechanism.py index 4153d7100dd..4d27c63163c 100644 --- a/psyneulink/components/mechanisms/mechanism.py +++ b/psyneulink/components/mechanisms/mechanism.py @@ -710,14 +710,16 @@ class `UserList `, making a copy of the State and assigning that to the `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 `). + appended to its name, and incremented for each State subsequently added with the same name (see :ref:`naming + conventions `). 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 ` attribute, + Adding InputStates to a Mechanism changes the size of its `variable ` attribute, which may produce an incompatibility with its `function ` (see `Mechanism InputStates` for a more detailed explanation). @@ -2212,6 +2219,11 @@ def add_states(self, states, context=COMMAND_LINE): `State specification dictionary ` (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 @@ -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) diff --git a/psyneulink/components/states/inputstate.py b/psyneulink/components/states/inputstate.py index 4ab04c263f7..c9461749b5b 100644 --- a/psyneulink/components/states/inputstate.py +++ b/psyneulink/components/states/inputstate.py @@ -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)): @@ -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)) @@ -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] @@ -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) diff --git a/psyneulink/components/states/state.py b/psyneulink/components/states/state.py index f299d325b9c..ffa46c678eb 100644 --- a/psyneulink/components/states/state.py +++ b/psyneulink/components/states/state.py @@ -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, \ @@ -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""" @@ -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: @@ -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: