def test_initial_values_softmax(self): T = TransferMechanism(default_variable=[[0.0, 0.0], [0.0, 0.0]], function=SoftMax(), integrator_mode=True, integration_rate=0.5, initial_value=[[1.0, 2.0], [3.0, 4.0]]) T2 = TransferMechanism() P = Process(pathway=[T, T2]) S = System(processes=[P]) S.run(inputs={T: [[1.5, 2.5], [3.5, 4.5]]}) result = T.value # Expected results # integrator function: # input = [[1.5, 2.5], [3.5, 4.5]] | output = [[1.25, 2.25]], [3.25, 4.25]] integrator_fn = AdaptiveIntegrator(rate=0.5, default_variable=[[0.0, 0.0], [0.0, 0.0]], initializer=[[1.0, 2.0], [3.0, 4.0]]) expected_result_integrator = integrator_fn.function([[1.5, 2.5], [3.5, 4.5]]) S1 = SoftMax() expected_result_s1 = S1.function([[1.25, 2.25]]) S2 = SoftMax() expected_result_s2 = S2.function([[3.25, 4.25]]) assert np.allclose(expected_result_integrator, T.integrator_function_value) assert np.allclose(expected_result_s1, result[0]) assert np.allclose(expected_result_s2, result[1])
def test_integrator_type_adaptive_rate_float_input_list(self): I = IntegratorMechanism(default_variable=[0, 0, 0], name='IntegratorMechanism', function=AdaptiveIntegrator(rate=0.5)) # P = Process(pathway=[I]) val = list(I.execute([10.0, 10.0, 10.0])[0]) assert val == [5.0, 5.0, 5.0]
def __init__(self, default_variable=None, size=None, function=AdaptiveIntegrator(rate=0.5), time_scale=TimeScale.TRIAL, params=None, name=None, prefs: is_pref_set = None, context=None): """Assign type-level preferences, default input value (SigmoidLayer_DEFAULT_BIAS) and call super.__init__ """ if default_variable is None and size is None: default_variable = self.ClassDefaults.variable # Assign args to params and functionParams dicts (kwConstants must == arg names) # self.ClassDefaults.variable = default_variable or [[0]] params = self._assign_args_to_param_dicts(function=function, params=params) # if default_variable is NotImplemented: # default_variable = SigmoidLayer_DEFAULT_NET_INPUT # self.size = size super(IntegratorMechanism, self).__init__(variable=default_variable, size=size, params=params, name=name, prefs=prefs, context=self)
def test_integrator_adaptive_noise_fn(self): I = IntegratorMechanism( name='IntegratorMechanism', function=AdaptiveIntegrator(noise=NormalDist()), ) val = float(I.execute(10)) np.testing.assert_allclose(val, 12.240893199201459)
def test_integrator_adaptive_noise_fn(self): I = IntegratorMechanism( name='IntegratorMechanism', function=AdaptiveIntegrator(noise=NormalDist().function), time_scale=TimeScale.TIME_STEP) val = float(I.execute(10)) np.testing.assert_allclose(val, 11.86755799)
def test_adaptive_integrator(self): I = IntegratorMechanism(function=AdaptiveIntegrator( initializer=10.0, rate=0.5, offset=10, )) # P = Process(pathway=[I]) # 10*0.5 + 1*0.5 + 10 val = I.execute(1) assert val == 15.5
def test_integrator_type_adaptive_rate_float(self): I = IntegratorMechanism( name='IntegratorMechanism', function=AdaptiveIntegrator( rate=0.5 ) ) # P = Process(pathway=[I]) val = list(I.execute(10.0)) assert val == [5.0]
def test_integrator_adaptive_noise_fn_var_list(self): I = IntegratorMechanism( name='IntegratorMechanism', default_variable=[0, 0, 0, 0], function=AdaptiveIntegrator(noise=NormalDist(), ), ) val = I.execute([10, 10, 10, 10])[0] np.testing.assert_allclose( val, [10.95008842, 9.84864279, 9.89678115, 10.4105985])
def test_integrator_adaptive_noise_fn_var_list(self): I = IntegratorMechanism(name='IntegratorMechanism', default_variable=[0, 0, 0, 0], function=AdaptiveIntegrator( noise=NormalDist().function, default_variable=[0, 0, 0, 0]), time_scale=TimeScale.TIME_STEP) val = I.execute([10, 10, 10, 10])[0] np.testing.assert_allclose( val, [10.12167502, 10.44386323, 10.33367433, 11.49407907])
def test_integrator_adaptive_noise_fn_var_list(self): I = IntegratorMechanism( name='IntegratorMechanism', default_variable=[0, 0, 0, 0], function=AdaptiveIntegrator( noise=NormalDist(), ), ) val = I.execute([10, 10, 10, 10])[0] np.testing.assert_allclose(val, [10.14404357, 11.45427351, 10.76103773, 10.12167502])
def test_integrator_adaptive_with_reset_intializer(self): I = IntegratorMechanism(name='IntegratorMechanism', function=AdaptiveIntegrator(rate=0.5), time_scale=TimeScale.TIME_STEP) # val = float(I.execute(10)[0]) # P = Process(pathway=[I]) val = float(I.execute(10)) # returns (rate)*variable + (1-rate*previous_value) + noise # rate = 1, noise = 0, so in this case, returns 10.0 # testing initializer I.function_object.reset_initializer = 1.0 val2 = float(I.execute(1)) assert [val, val2] == [5.0, 1.0]
def test_Adaptive_valid(self): I = IntegratorMechanism( name='IntegratorMechanism', function=AdaptiveIntegrator( rate=0.5 ), ) # returns (1-rate)*previous_value + rate*variable + noise # so in this case, returns 0.5*0 + 0.5*10 + 0 = 5.0 I.execute(10) assert np.allclose(I.value, 5.0) assert np.allclose(I.output_state.value, 5.0) # reinitialize function I.function_object.reinitialize(1.0) assert np.allclose(I.function_object.value, 1.0) assert np.allclose(I.value, 5.0) assert np.allclose(I.output_states[0].value, 5.0) # reinitialize function without value spec I.function_object.reinitialize() assert np.allclose(I.function_object.value, 0.0) assert np.allclose(I.value, 5.0) assert np.allclose(I.output_states[0].value, 5.0) # reinitialize mechanism I.reinitialize(2.0) assert np.allclose(I.function_object.value, 2.0) assert np.allclose(I.value, 2.0) assert np.allclose(I.output_states[0].value, 2.0) I.execute(1.0) # (1-0.5)*2.0 + 0.5*1.0 + 0 = 1.5 assert np.allclose(I.value, 1.5) assert np.allclose(I.output_states[0].value, 1.5) # reinitialize mechanism without value spec I.reinitialize() assert np.allclose(I.function_object.value, 0.0) assert np.allclose(I.value, 0.0) assert np.allclose(I.output_states[0].value, 0.0)
class ClassDefaults(ProcessingMechanism_Base.ClassDefaults): function = AdaptiveIntegrator(rate=0.5)
def _execute(self, variable=None, runtime_params=None, clock=CentralClock, time_scale=TimeScale.TRIAL, context=None): """Execute TransferMechanism function and return transform of input Execute TransferMechanism function on input, and assign to output_values: - Activation value for all units - Mean of the activation values across units - Variance of the activation values across units Return: value of input transformed by TransferMechanism function in outputState[TransferOuput.RESULT].value mean of items in RESULT outputState[TransferOuput.MEAN].value variance of items in RESULT outputState[TransferOuput.VARIANCE].value Arguments: # CONFIRM: variable (float): set to self.value (= self.input_value) - params (dict): runtime_params passed from Mechanism, used as one-time value for current execution: + NOISE (float) + TIME_CONSTANT (float) + RANGE ([float, float]) - context (str) Returns the following values in self.value (2D np.array) and in the value of the corresponding outputState in the self.output_states list: - activation value (float) - mean activation value (float) - standard deviation of activation values (float) :param self: :param variable (float) :param params: (dict) :param context: (str) :rtype self.outputState.value: (number) """ # FIX: ??CALL check_args()?? # FIX: IS THIS CORRECT? SHOULD THIS BE SET TO INITIAL_VALUE # FIX: WHICH SHOULD BE DEFAULTED TO 0.0?? # Use self.instance_defaults.variable to initialize state of input # FIX: NEED TO GET THIS TO WORK WITH CALL TO METHOD: integrator_mode = self.integrator_mode #region ASSIGN PARAMETER VALUES time_constant = self.time_constant clip = self.clip noise = self.noise #endregion #region EXECUTE TransferMechanism FUNCTION --------------------------------------------------------------------- # FIX: NOT UPDATING self.previous_input CORRECTLY # FIX: SHOULD UPDATE PARAMS PASSED TO integrator_function WITH ANY RUNTIME PARAMS THAT ARE RELEVANT TO IT # Update according to time-scale of integration if integrator_mode: # if time_scale is TimeScale.TIME_STEP: if not self.integrator_function: self.integrator_function = AdaptiveIntegrator( variable, initializer=self.initial_value, noise=self.noise, rate=self.time_constant, owner=self) current_input = self.integrator_function.execute( variable, # Should we handle runtime params? params={ INITIALIZER: self.initial_value, NOISE: self.noise, RATE: self.time_constant }, context=context) else: # elif time_scale is TimeScale.TRIAL: noise = self._try_execute_param(self.noise, variable) # formerly: current_input = self.input_state.value + noise # (MODIFIED 7/13/17 CW) this if/else below is hacky: just allows a nicer error message # when the input is given as a string. if (np.array(noise) != 0).any(): current_input = variable + noise else: current_input = variable if isinstance(self.function_object, TransferFunction): outputs = self.function(variable=current_input, params=runtime_params) # if clip is not None: # print(clip) # minCapIndices = np.where(outputs < clip[0]) # print(minCapIndices) # maxCapIndices = np.where(outputs > clip[1]) # print(maxCapIndices) # outputs[minCapIndices] = np.min(clip) # outputs[maxCapIndices] = np.max(clip) else: # Apply TransferMechanism's function to each input state separately outputs = [] for elem in current_input: output_item = self.function(variable=elem, params=runtime_params) # if clip is not None: # minCapIndices = np.where(output_item < clip[0]) # maxCapIndices = np.where(output_item > clip[1]) # output_item[minCapIndices] = np.min(clip) # output_item[maxCapIndices] = np.max(clip) outputs.append(output_item) # outputs = [] # for elem in current_input: # output_item = self.function(variable=elem, params=runtime_params) # if clip is not None: # minCapIndices = np.where(output_item < clip[0]) # maxCapIndices = np.where(output_item > clip[1]) # output_item[minCapIndices] = np.min(clip) # output_item[maxCapIndices] = np.max(clip) # outputs.append(output_item) return outputs
class TransferMechanism(ProcessingMechanism_Base): """ TransferMechanism( \ default_variable=None, \ size=None, \ input_states=None, \ function=Linear, \ initial_value=None, \ noise=0.0, \ time_constant=1.0, \ integrator_mode=False, \ clip=(float:min, float:max), \ output_states=RESULTS \ params=None, \ name=None, \ prefs=None) Subclass of `ProcessingMechanism <ProcessingMechanism>` that performs a simple transform of its input. COMMENT: Description ----------- TransferMechanism is a Subtype of the ProcessingMechanism Type of the Mechanism Category of the Component class It implements a Mechanism that transforms its input variable based on FUNCTION (default: Linear) Class attributes ---------------- + componentType (str): TransferMechanism + classPreference (PreferenceSet): Transfer_PreferenceSet, instantiated in __init__() + classPreferenceLevel (PreferenceLevel): PreferenceLevel.SUBTYPE + ClassDefaults.variable (value): Transfer_DEFAULT_BIAS Class methods ------------- None MechanismRegistry ----------------- All instances of TransferMechanism are registered in MechanismRegistry, which maintains an entry for the subclass, a count for all instances of it, and a dictionary of those instances COMMENT Arguments --------- default_variable : number, list or np.ndarray : default Transfer_DEFAULT_BIAS specifies the input to the Mechanism to use if none is provided in a call to its `execute <Mechanism_Base.execute>` or `run <Mechanism_Base.run>` method; also serves as a template to specify the length of `variable <TransferMechanism.variable>` for `function <TransferMechanism.function>`, and the `primary outputState <OutputState_Primary>` of the Mechanism. size : int, list or np.ndarray of ints specifies default_variable as array(s) of zeros if **default_variable** is not passed as an argument; if **default_variable** is specified, it takes precedence over the specification of **size**. As an example, the following mechanisms are equivalent:: T1 = TransferMechanism(size = [3, 2]) T2 = TransferMechanism(default_variable = [[0, 0, 0], [0, 0]]) input_states : str, list or np.ndarray specifies the InputStates for the TransferMechanism; by default, a single InputState is created using the value of default_variable as its `variable <InputState.variable>`; if more than one is specified, the number and, if specified, their values must be compatible with any specifications in **default_variable** or **size** (see `Mechanism_InputStates`); see `input_states <TransferMechanism.output_states>` for additional details. function : TransferFunction : default Linear specifies the function used to transform the input; can be `Linear`, `Logistic`, `Exponential`, or a custom function. initial_value : value, list or np.ndarray : default Transfer_DEFAULT_BIAS specifies the starting value for time-averaged input (only relevant if `integrator_mode <TransferMechanism.integrator_mode>` is True). COMMENT: Transfer_DEFAULT_BIAS SHOULD RESOLVE TO A VALUE COMMENT noise : float or function : default 0.0 a stochastically-sampled value added to the result of the `function <TransferMechanism.function>`: if it is a float, it must be in the interval [0,1] and is used to scale the variance of a zero-mean Gaussian; if it is a function, it must return a scalar value. time_constant : float : default 1.0 the time constant for exponential time averaging of input when the Mechanism is executed with `integrator_mode` set to True:: result = (time_constant * current input) + ((1-time_constant) * result on previous time_step) clip : Optional[Tuple[float, float]] specifies the allowable range for the result of `function <TransferMechanism.function>`: the first item specifies the minimum allowable value of the result, and the second its maximum allowable value; any element of the result that exceeds the specified minimum or maximum value is set to the value of `clip <TransferMechanism.clip>` that it exceeds. output_states : str, list or np.ndarray : default RESULTS specifies the OutputStates for the TransferMechanism; by default, one is created for each InputState specified in **input_states**; see `note <TransferMechanism_OutputStates_Note>`, and `output_states <TransferMechanism.output_states>` for additional details). params : Dict[param keyword, param value] : default None a `parameter dictionary <ParameterState_Specification>` that can be used to specify the parameters for the Mechanism, its `function <Mechanism_Base.function>`, and/or a custom function and its parameters. Values specified for parameters in the dictionary override any assigned to those parameters in arguments of the constructor. name : str : default see `name <TransferMechanism.name>` specifies the name of the TransferMechanism. prefs : PreferenceSet or specification dict : default Mechanism.classPreferences specifies the `PreferenceSet` for the TransferMechanism; see `prefs <TransferMechanism.prefs>` for details. context : str : default componentType+INITIALIZING string used for contextualization of instantiation, hierarchical calls, executions, etc. Returns ------- instance of TransferMechanism : TransferMechanism Attributes ---------- variable : value the input to Mechanism's `function <TransferMechanism.function>`. COMMENT: :py:data:`Transfer_DEFAULT_BIAS <LINK->SHOULD RESOLVE TO VALUE>` COMMENT input_states : *ContentAddressableList[InputState]* list of Mechanism's `InputStates <InputStates>` (see `TransferMechanism_InputStates` for additional details). function : Function the Function used to transform the input. COMMENT: THE FOLLOWING IS THE CURRENT ASSIGNMENT COMMENT initial_value : value, list or np.ndarray : Transfer_DEFAULT_BIAS specifies the starting value for time-averaged input (only relevant if `integrator_mode <TransferMechanism.integrator_mode>` is True and `time_constant <TransferMechanism.time_constant>` is not 1.0). COMMENT: Transfer_DEFAULT_BIAS SHOULD RESOLVE TO A VALUE COMMENT noise : float or function a stochastically-sampled value added to the output of the `function <TransferMechanism.function>`: if it is a float, it must be in the interval [0,1] and is used to scale the variance of a zero-mean Gaussian; if it is a function, it must return a scalar value. time_constant : float the time constant for exponential time averaging of input when the Mechanism is executed with `integrator_mode` set to True:: result = (time_constant * current input) + ( (1-time_constant) * result on previous time_step) integrator_mode : booleane when set to True, the Mechanism time averages its input according to an exponentially weighted moving average (see `time_constant <TransferMechanisms.time_constant>`). clip : Optional[Tuple[float, float]] determines the allowable range of the result: the first value specifies the minimum allowable value and the second the maximum allowable value; any element of the result that exceeds minimum or maximum is set to the value of `clip <TransferMechanism.clip>` it exceeds. If `function <TransferMechanism.function>` is `Logistic`, `clip <TransferMechanism.clip>` is set by default to (0,1). value : 2d np.array [array(float64)] result of executing `function <TransferMechanism.function>`. previous_value : float the `value <TransferMechanism.value>` on the previous execution of the Mechanism. delta : float the change in `value <TransferMechanism.value>` from the previous execution of the Mechanism (i.e., `value <TransferMechanism.value>` - `previous_value <TransferMechanism.previous_value>`). output_states : *ContentAddressableList[OutputState]* list of Mechanism's `OutputStates <OutputStates>`; by default there is one OutputState for each InputState, with the base name `RESULT` (see `TransferMechanism_OutputStates` for additional details). output_values : List[array(float64)] each item is the `value <OutputState.value>` of the corresponding OutputState in `output_states <TransferMechanism.output_states>`. The default is a single item containing the result of the TransferMechanism's `function <TransferMechanism.function>`; additional ones may be included, based on the specifications made in the **output_states** argument of the Mechanism's constructor (see `TransferMechanism Standard OutputStates <TransferMechanism_Standard_OutputStates>`). name : str the name of the TransferMechanism; if it is not specified in the **name** argument of the constructor, a default is assigned by MechanismRegistry (see `Naming` for conventions used for default and duplicate names). prefs : PreferenceSet or specification dict the `PreferenceSet` for the TransferMechanism; if it is not specified in the **prefs** argument of the constructor, a default is assigned using `classPreferences` defined in __init__.py (see :doc:`PreferenceSet <LINK>` for details). """ componentType = TRANSFER_MECHANISM classPreferenceLevel = PreferenceLevel.SUBTYPE # These will override those specified in TypeDefaultPreferences classPreferences = { kwPreferenceSetName: 'TransferCustomClassPreferences', kpReportOutputPref: PreferenceEntry(False, PreferenceLevel.INSTANCE), kpRuntimeParamStickyAssignmentPref: PreferenceEntry(False, PreferenceLevel.INSTANCE) } # TransferMechanism parameter and control signal assignments): paramClassDefaults = ProcessingMechanism_Base.paramClassDefaults.copy() paramClassDefaults.update({NOISE: None}) standard_output_states = standard_output_states.copy() class ClassDefaults(ProcessingMechanism_Base.ClassDefaults): variable = [[0]] @tc.typecheck def __init__(self, default_variable=None, size=None, input_states: tc.optional( tc.any(Iterable, Mechanism, OutputState, InputState)) = None, function=Linear, initial_value=None, noise=0.0, time_constant=1.0, integrator_mode=False, clip=None, output_states: tc.optional(tc.any(str, Iterable)) = RESULTS, time_scale=TimeScale.TRIAL, params=None, name=None, prefs: is_pref_set = None, context=componentType + INITIALIZING): """Assign type-level preferences and call super.__init__ """ # Default output_states is specified in constructor as a string rather than a list # to avoid "gotcha" associated with mutable default arguments # (see: bit.ly/2uID3s3 and http://docs.python-guide.org/en/latest/writing/gotchas/) if output_states is None or output_states is RESULTS: output_states = [RESULTS] params = self._assign_args_to_param_dicts( function=function, initial_value=initial_value, input_states=input_states, output_states=output_states, noise=noise, time_constant=time_constant, integrator_mode=integrator_mode, time_scale=time_scale, clip=clip, params=params) self.integrator_function = None if not isinstance(self.standard_output_states, StandardOutputStates): self.standard_output_states = StandardOutputStates( self, self.standard_output_states, indices=PRIMARY) super(TransferMechanism, self).__init__( variable=default_variable, size=size, params=params, name=name, prefs=prefs, context=self, input_states=input_states, ) def _validate_params(self, request_set, target_set=None, context=None): """Validate FUNCTION and Mechanism params """ super()._validate_params(request_set=request_set, target_set=target_set, context=context) # Validate FUNCTION if FUNCTION in target_set: transfer_function = target_set[FUNCTION] # FUNCTION is a Function if isinstance(transfer_function, Component): transfer_function_class = transfer_function.__class__ transfer_function_name = transfer_function.__class__.__name__ # FUNCTION is a function or method elif isinstance(transfer_function, (function_type, method_type)): transfer_function_class = transfer_function.__self__.__class__ transfer_function_name = transfer_function.__self__.__class__.__name__ # FUNCTION is a class elif inspect.isclass(transfer_function): transfer_function_class = transfer_function transfer_function_name = transfer_function.__name__ if not transfer_function_class.componentType is TRANSFER_FUNCTION_TYPE and not transfer_function_class.componentType is NORMALIZING_FUNCTION_TYPE: raise TransferError( "Function {} specified as FUNCTION param of {} must be a {}" .format(transfer_function_name, self.name, TRANSFER_FUNCTION_TYPE)) # Validate INITIAL_VALUE if INITIAL_VALUE in target_set: initial_value = target_set[INITIAL_VALUE] if initial_value is not None: if not iscompatible(initial_value, self.instance_defaults.variable): raise Exception( "initial_value is {}, type {}\nself.instance_defaults.variable is {}, type {}" .format( initial_value, type(initial_value).__name__, self.instance_defaults.variable, type(self.instance_defaults.variable).__name__, )) raise TransferError( "The format of the initial_value parameter for {} ({}) must match its input ({})" .format( append_type_to_name(self), initial_value, self.instance_defaults.variable[0], )) # FIX: SHOULD THIS (AND TIME_CONSTANT) JUST BE VALIDATED BY INTEGRATOR FUNCTION NOW THAT THEY ARE PROPERTIES?? # Validate NOISE: if NOISE in target_set: self._validate_noise(target_set[NOISE], self.instance_defaults.variable) # Validate TIME_CONSTANT: if TIME_CONSTANT in target_set: time_constant = target_set[TIME_CONSTANT] if (not (isinstance(time_constant, float) and 0 <= time_constant <= 1)) and (time_constant != None): raise TransferError( "time_constant parameter ({}) for {} must be a float between 0 and 1" .format(time_constant, self.name)) # Validate RANGE: if CLIP in target_set: clip = target_set[CLIP] if clip: if not (isinstance(clip, tuple) and len(clip) == 2 and all(isinstance(i, numbers.Number) for i in clip)): raise TransferError( "clip parameter ({}) for {} must be a tuple with two numbers" .format(clip, self.name)) if not clip[0] < clip[1]: raise TransferError( "The first item of the clip parameter ({}) must be less than the second" .format(clip, self.name)) # self.integrator_function = Integrator( # # default_variable=self.default_variable, # initializer = self.instance_defaults.variable, # noise = self.noise, # rate = self.time_constant, # integration_type= ADAPTIVE) def _validate_noise(self, noise, var): # Noise is a list or array if isinstance(noise, (np.ndarray, list)): if len(noise) == 1: pass # Variable is a list/array elif not iscompatible(np.atleast_2d(noise), var) and len(noise) > 1: raise MechanismError( "Noise parameter ({}) does not match default variable ({}). Noise parameter of {} must be specified" " as a float, a function, or an array of the appropriate shape ({})." .format(noise, self.instance_defaults.variable, self.name, np.shape(np.array(var)))) else: for noise_item in noise: if not isinstance( noise_item, (float, int)) and not callable(noise_item): raise MechanismError( "The elements of a noise list or array must be floats or functions. {} is not a valid noise" " element for {}".format(noise_item, self.name)) elif _is_control_spec(noise): pass # Otherwise, must be a float, int or function elif not isinstance(noise, (float, int)) and not callable(noise): raise MechanismError( "Noise parameter ({}) for {} must be a float, " "function, or array/list of these.".format(noise, self.name)) def _try_execute_param(self, param, var): # param is a list; if any element is callable, execute it if isinstance(param, (np.ndarray, list)): # NOTE: np.atleast_2d will cause problems if the param has "rows" of different lengths param = np.atleast_2d(param) for i in range(len(param)): for j in range(len(param[i])): if callable(param[i][j]): param[i][j] = param[i][j]() # param is one function elif callable(param): # NOTE: np.atleast_2d will cause problems if the param has "rows" of different lengths new_param = [] for row in np.atleast_2d(var): new_row = [] for item in row: new_row.append(param()) new_param.append(new_row) param = new_param return param def _instantiate_parameter_states(self, context=None): from psyneulink.components.functions.function import Logistic # If function is a logistic, and clip has not been specified, bound it between 0 and 1 if ((isinstance(self.function, Logistic) or (inspect.isclass(self.function) and issubclass(self.function, Logistic))) and self.clip is None): self.clip = (0, 1) super()._instantiate_parameter_states(context=context) def _instantiate_attributes_before_function(self, context=None): super()._instantiate_attributes_before_function(context=context) if self.initial_value is None: self.initial_value = self.instance_defaults.variable def _instantiate_output_states(self, context=None): # If user specified more than one item for variable, but did not specify any custom OutputStates # then assign one OutputState (with the default name, indexed by the number of them) per item of variable if len(self.variable) > 1 and len( self.output_states) == 1 and self.output_states[0] == RESULTS: self.output_states = [] for i, item in enumerate(self.variable): self.output_states.append({NAME: RESULT, INDEX: i}) super()._instantiate_output_states(context=context) def _execute(self, variable=None, runtime_params=None, clock=CentralClock, time_scale=TimeScale.TRIAL, context=None): """Execute TransferMechanism function and return transform of input Execute TransferMechanism function on input, and assign to output_values: - Activation value for all units - Mean of the activation values across units - Variance of the activation values across units Return: value of input transformed by TransferMechanism function in outputState[TransferOuput.RESULT].value mean of items in RESULT outputState[TransferOuput.MEAN].value variance of items in RESULT outputState[TransferOuput.VARIANCE].value Arguments: # CONFIRM: variable (float): set to self.value (= self.input_value) - params (dict): runtime_params passed from Mechanism, used as one-time value for current execution: + NOISE (float) + TIME_CONSTANT (float) + RANGE ([float, float]) - context (str) Returns the following values in self.value (2D np.array) and in the value of the corresponding outputState in the self.output_states list: - activation value (float) - mean activation value (float) - standard deviation of activation values (float) :param self: :param variable (float) :param params: (dict) :param context: (str) :rtype self.outputState.value: (number) """ # FIX: ??CALL check_args()?? # FIX: IS THIS CORRECT? SHOULD THIS BE SET TO INITIAL_VALUE # FIX: WHICH SHOULD BE DEFAULTED TO 0.0?? # Use self.instance_defaults.variable to initialize state of input # FIX: NEED TO GET THIS TO WORK WITH CALL TO METHOD: integrator_mode = self.integrator_mode #region ASSIGN PARAMETER VALUES time_constant = self.time_constant clip = self.clip noise = self.noise #endregion #region EXECUTE TransferMechanism FUNCTION --------------------------------------------------------------------- # FIX: NOT UPDATING self.previous_input CORRECTLY # FIX: SHOULD UPDATE PARAMS PASSED TO integrator_function WITH ANY RUNTIME PARAMS THAT ARE RELEVANT TO IT # Update according to time-scale of integration if integrator_mode: # if time_scale is TimeScale.TIME_STEP: if not self.integrator_function: self.integrator_function = AdaptiveIntegrator( variable, initializer=self.initial_value, noise=self.noise, rate=self.time_constant, owner=self) current_input = self.integrator_function.execute( variable, # Should we handle runtime params? params={ INITIALIZER: self.initial_value, NOISE: self.noise, RATE: self.time_constant }, context=context) else: # elif time_scale is TimeScale.TRIAL: noise = self._try_execute_param(self.noise, variable) # formerly: current_input = self.input_state.value + noise # (MODIFIED 7/13/17 CW) this if/else below is hacky: just allows a nicer error message # when the input is given as a string. if (np.array(noise) != 0).any(): current_input = variable + noise else: current_input = variable if isinstance(self.function_object, TransferFunction): outputs = self.function(variable=current_input, params=runtime_params) # if clip is not None: # print(clip) # minCapIndices = np.where(outputs < clip[0]) # print(minCapIndices) # maxCapIndices = np.where(outputs > clip[1]) # print(maxCapIndices) # outputs[minCapIndices] = np.min(clip) # outputs[maxCapIndices] = np.max(clip) else: # Apply TransferMechanism's function to each input state separately outputs = [] for elem in current_input: output_item = self.function(variable=elem, params=runtime_params) # if clip is not None: # minCapIndices = np.where(output_item < clip[0]) # maxCapIndices = np.where(output_item > clip[1]) # output_item[minCapIndices] = np.min(clip) # output_item[maxCapIndices] = np.max(clip) outputs.append(output_item) # outputs = [] # for elem in current_input: # output_item = self.function(variable=elem, params=runtime_params) # if clip is not None: # minCapIndices = np.where(output_item < clip[0]) # maxCapIndices = np.where(output_item > clip[1]) # output_item[minCapIndices] = np.min(clip) # output_item[maxCapIndices] = np.max(clip) # outputs.append(output_item) return outputs #endregion def _report_mechanism_execution(self, input, params, output): """Override super to report previous_input rather than input, and selected params """ # KAM Changed 8/29/17 print_input = self.previous_input --> print_input = input # because self.previous_input is not a valid attrib of TransferMechanism print_input = input print_params = params.copy() # Only report time_constant if in TIME_STEP mode if params['time_scale'] is TimeScale.TRIAL: del print_params[TIME_CONSTANT] # Suppress reporting of range (not currently used) del print_params[CLIP] super()._report_mechanism_execution(input_val=print_input, params=print_params) # def terminate_function(self, context=None): # """Terminate the process # # called by process.terminate() - MUST BE OVERRIDDEN BY SUBCLASS IMPLEMENTATION # returns output # # :rtype CurrentStateTuple(state, confidence, duration, controlModulatedParamValues) # """ # # IMPLEMENTATION NOTE: TBI when time_step is implemented for TransferMechanism # @property def clip(self): return self._clip @clip.setter def clip(self, value): self._clip = value # MODIFIED 4/17/17 NEW: @property def noise(self): return self._noise @noise.setter def noise(self, value): self._noise = value @property def time_constant(self): return self._time_constant @time_constant.setter def time_constant(self, value): self._time_constant = value # # MODIFIED 4/17/17 END @property def previous_value(self): if self.integrator_function: return self.integrator_function.previous_value return None @property def delta(self): if self.integrator_function: return self.value - self.integrator_function.previous_value return None
class ClassDefaults(ProcessingMechanism_Base.ClassDefaults): # setting owner to a string to avoid using this exact instance # in the future, best to make this a sort of spec that can be used to # construct a default instance function = AdaptiveIntegrator(rate=0.5, owner=CLASS_DEFAULTS)