# ControlSignal¶

## Overview¶

A ControlSignal is a type of ModulatorySignal that is specialized for use with a ControlMechanism and one or more ControlProjections, to modify the parameter(s) of one or more Components. A ControlSignal receives an allocation value from the ControlMechanism to which it belongs, and uses that to compute an intensity (also referred to as a control_signal) that is assigned as the value of its ControlProjections. Each ControlProjection conveys its value to the ParameterPort for the parameter it controls, which uses that value to modulate the value of the parameter. A ControlSignal also calculates a cost, based on its intensity and/or its time course, that may be used by the ControlMechanism to adapt the ControlSignal’s allocation in subsequent executions.

## Creating a ControlSignal¶

A ControlSignal is created automatically whenever the parameter of a Mechanism or of its function is specified for control. ControlSignals can also be specified in the control_signals argument of the constructor for a ControlMechanism, as well as in the specification of the parameter that the ControlSignal is intended to modulate (also see Modualory Specification. Although a ControlSignal can also be created on its own using its constructor (or any of the other ways for creating an OutputPort), this is usually not necessary nor is it advisable, as a ControlSignal has dedicated components and requirements for configuration that must be met for it to function properly.

### Specifying ControlSignals¶

When a ControlSignal is specified in the control_signals argument of the constructor for a ControlMechanism, the parameter(s) to be controlled must be specified. If other attributes of the ControlSignal need to be specified (e.g., one or more of its cost functions), then the Constructor for the ControlSignal can be used or a port specification dictionary, in which the parameter(s) to be controlled in the projections argument or PROJECTIONS entry, respectively, using any of the forms below. For convenience, the parameters can also be specified on their own in the control_signals argument of the ControlMechanism’s constructor, in which case a default ControlSignal will be created for each. In all cases, any of the following can be use to specify the parameter(s) to be controlled:

• ParameterPort (or list of them) – for the Mechanism(s) to which the parameter(s) belong;

• 2-item tuple: (parameter name or list of them>, <Mechanism>) – the 1st item must be the name of the parameter (or list of parameter names), and the 2nd item the Mechanism to which it (they) belong(s); this is a convenience format, that is simpler to use than a specification dictionary (see above), but precludes specification of any parameters for the ControlSignal.

• specification dictionary – this is an abbreviated form of port specification dictionary, in which the parameter(s) to be controlled can be specified in either of the two following ways:

• for controlling a single parameter, the dictionary can have the following two entries:

• NAME: str

the string must be the name of the parameter to be controlled;

• MECHANISM: Mechanism

the Mechanism must be the one to the which the parameter to be controlled belongs.

• for controlling multiple parameters, the dictionary can have the following entry:

• <str>:list

the string used as the key specifies the name to be used for the ControlSignal, and each item of the list must be a specification of a parameter to be controlled by the ControlSignal (and that will receive a ControlProjection from it).

## Structure¶

A ControlSignal is owned by an ControlMechanism, and controls the parameters of one or more Components by modulating the function of the ParameterPort that determines the value of each of the parameters that it control. Its operation is governed by several attributes of the ControlSignal, that are described below.

### Projections¶

When a ControlSignal is created, it can be assigned one or more ControlProjections, using either the projections argument of its constructor, or in an entry of a dictionary assigned to the params argument with the key PROJECTIONS. These will be assigned to its efferents attribute. See Port Projections for additional details concerning the specification of Projections when creating a Port.

Note

Although a ControlSignal can be assigned more than one ControlProjection, all of those Projections will receive the same value (based on the intensity of that ControlSignal), and use the same form of modulation. Thus, for them to be meaningful, they should project to ParameterPorts for parameters that are meaningfully related to one another (for example, the threshold parameter of multiple DDM Mechanisms).

### Modulation¶

A ControlSignal has a modulation attribute that determines how its ControlSignal’s value is used by the Ports to which it projects to modify their value s (see Modulation for an explanation of how the modulation attribute is specified and used to modulate the value of a Port). The modulation attribute can be specified in the modulation argument of the constructor for a ControlSignal, or in a specification dictionary as described above (see Types of Modulation for forms of specification). If it is not specified, its default is the value of the modulation attribute of the ControlMechanism to which the ControlSignal belongs (which is the same for all of the ControlSignals belonging to that ControlMechanism). The value of the modulation attribute of a ControlSignal is used by all of the ControlProjections that project from that ControlSignal.

### Allocation, Function and Intensity¶

Allocation (variable). A ControlSignal is assigned an allocation by the ControlMechanism to which it belongs. Some ControlMechanisms sample different allocation values for their ControlSignals to determine which to use (e.g., OptimizationControlMechanism); in those cases, they use each ControlSignal’s allocation_samples attribute (specified in the allocation_samples argument of the ControlSignal’s constructor) to determine the allocation values to sample for that ControlSignal. A ControlSignal’s allocation attribute contains the value assigned to it by the ControlMechanism at the end of the previous TRIAL (i.e., when the ControlMechanism last executed – see ControlMechanism Execution); its value from the previous TRIAL is assigned to the last_intensity attribute.

Function. A ControlSignal’s allocation serves as its variable, and is used by its function to generate an intensity. The default function for a ControlSignal is TransferWithCosts. This is a Function that supplements its core TransferFunction (specified by its transfer_fct with a set of cost functions that can be used to compute the ControlSignal’s cost attributes. The default transfer_fct> for TransferWithCosts is an identity function (Linear with slope =1 and intercept=0), that simply assigns the ControlSignal’s allocation as its intensity. However, the TransferWithCosts function can be specified as function argument in the ControlSignal’s constructor, with a different function specified for the transfer_fct argument of the TransferWithCosts’s constructor (e.g., Exponential, or any other function that takes and returns a scalar value or 1d array). The TransferWithCosts’ cost functions can also be assigned using its own constructor. A function other than TransferWithCosts can also be assigned as the ControlSignal’s function, however in that case, the ControlSignal’s costs can’t be computed and will all be assigned None.

Intensity (value). The result of the function is assigned as the value of the ControlSignal’s intensity attribute, which serves as the ControlSignal’s value (also referred to as control_signal). The intensity is used by its ControlProjection(s) to modulate the parameter(s) for which the ControlSignal is responsible. The ControlSignal’s intensity attribute reflects its value for the current TRIAL; its value from the previous TRIAL is assigned to the last_intensity attribute.

### Costs and Cost Functions¶

A ControlSignal has a cost attribute that may be used by the ControlMechanism to which it belongs to determine its allocation. The value of the cost is computed from the ControlSignal’s intensity using one or more of four cost functions. These are only available if the ControlSignal’s function is TransferWithCosts (which it is by default), and are actually functions that belong to the TransferWithCosts Function (see TransferWithCosts_Cost_Functions). Three of these compute different types of cost, and a fourth combines them, the result of which is assigned as the ControlSignal’s cost attribute. The ControlSignal has attributes that reference each of the TransferWithCosts’ cost functions and their attributesas listed below:

Which of the cost functions are used can be specified in the costs_options argument ControlSignal’s constructor, which is passed to the corresponding argument of the TransferWithCosts constructor (where it can also be specified). The costs functions can also be enabled and disabled using the TransferWithCosts function’s enable_costs, disable_costs, toggle_cost and assign_costs methods. All of these take one or more values of CostFunctions. The combine_costs_function is used to combine the values generated by the enabled cost functions, and the result is assigned as the ControlSignal’s cost attribute. By default, the combine_costs_function sums the results of the enabled cost functions. However, this can be modified by specifying the combine_costs_function by specifying the ControlSignal’s function using the constructor for the TransferWithCosts function. The parameters of the ControlSignal’s cost functions can also be modulated by another ControlMechanism (see example below).

## Execution¶

A ControlSignal cannot be executed directly. It is executed whenever the ControlMechanism to which it belongs is executed. When this occurs, the ControlMechanism provides the ControlSignal with an allocation, that is used by its function to compute its intensity for that TRIAL. The intensity is used by the ControlSignal’s ControlProjections to set the value(s) of the ParameterPort(s) to which the ControlSignalprojects.

Recall that the ParameterPort value is referenced anywhere that the controlled parameter is used in computation, and that it does not update until the component to which the ParameterPort belongs executes. If the distinction between the base value stored in the parameter attribute (i.e. MyTransferMech.function.gain) and the value of the ParameterPort is unfamiliar, see Parameter Port documentation for more details, or see Modulation for a detailed description of how modulation operates.

The ControlSignal’s intensity is also used by its cost functions to compute its cost attribute. That is used by some ControlMechanisms, along with the ControlSignal’s allocation_samples attribute, to evaluate a control_allocation, and adjust the ControlSignal’s allocation for the next TRIAL.

Note

The changes in a parameter in response to the execution of a ControlMechanism are not applied until the Mechanism with the parameter being controlled is next executed; see Lazy Evaluation for an explanation of “lazy” updating).

Examples

As explained above (and in Specifying Parameters to Control), a ControlSignal can be specified either where the parameter it controls is specified, or in the constructor of the ControlMechanism to which the ControlSignal belongs (i.e., that is responsible for the control). Which of these to use is largely an aesthetic matter – that is, where you wish the specification of control to appear. Examples of each are provided below.

Note

If a ControlSignal is specified where the parameter it controls is specified, the ControlSignal is not implemented until the Component to which the parameter belongs is assigned to a Composition (see Specifying Parameters to Control).

Specify a ControlSignal where a parameter is specified

The simplest way to do this is to specify the ControlSignal by class, or using the keyword CONTROL, in a tuple specification for the parameter, as in the following examples:

>>> from psyneulink import *
>>> my_mech = ProcessingMechanism(function=Logistic(bias=(1.0, ControlMechanism)))
or
>>> my_mech = ProcessingMechanism(function=Logistic(bias=(1.0, CONTROL)))


Both of these assign a ControlSignal to the bias parameter of the Logistic Function used by a ProcessingMechanism. This creates a default ControlSignal, with a ControlProjection that projects to the ProcessingMechanism’s ParameterPort for the bias parameter of its Logistic Function. The default value of a ControlSignal’s modulation attribute is MULTIPLICATIVE, so that it will multiply the value of the bias parameter. When the ProcessingMechanism executes, the Logistic Function will use the value of the ControlSignal as its bias parameter.

If attributes of the ControlSignal must be specified, then its constructor can be used. For example, ordinarily a ControlSignal modulates the MULTIPLICATIVE_PARAM of a Port's function. However, this can be changed by using the ControlSignal’s constructor to specify its modulation argument, as follows:

>>> my_mech = ProcessingMechanism(function=Logistic(gain=(1.0, ControlSignal(modulation=ADDITIVE))))


This specifies that the value of the ControlSignal should be added to, rather than multiply the value of the gain parameter of the Logistic function. Note that the modulation argument determines how to modify a parameter of the Logistic Function (in this case, its gain parameter), and its input directly; that is, in this example, the value of the ControlSignal is added to the gain parameter of the Logistic function, not to its variable. If the value of the ControlSignal’s modulation argument had been OVERRIDE, then the ControlSignal’s value would have been used as (i.e., it would have replaced) the ˚value of the Logistic Function’s gain parameter, rather than being added to it.

Specify ControlSignals in a ControlMechanism’s constructor

Parameters can also be specified for control in constructor of the ControlMechanism that controls them. This is done in the control_signals argument of the ControlMechanism’s constructor, by specifying a ControlSignal for each parameter to be controlled. The following example shows several ways in which the ControlSignal can be specified (see Specifying ControlSignals for additional details):

>>> mech_A = ProcessingMechanism()
>>> mech_B = ProcessingMechanism(function=Logistic)
>>> ctl_mech = ControlMechanism(control_signals=[(INTERCEPT, mech_A),
...                                              ControlSignal(modulates=(GAIN, mech_B),
...                                                            cost_options=CostFunctions.INTENSITY),
...                                              {
...                                                  NAME: BIAS,
...                                                  MECHANISM: mech_B,
...                                              }])


Here, ctl_mech modulates the intercept parameter of mech_A’s Linear Function (the default function for a ProcessingMechanism), and the bias and gain parameters of mech_B’s Logistic Function. The first ControlSignal is specified using a 2-item tuple (the simplest way to do so); the second uses the ControlSignal’s constructor (allowing another parameter to be specified – here, its cost_options); and the third uses a specification dictionary (which supports additional options; see above).

Modulate the parameters of a ControlSignal’s cost function.

ControlSignals can be used to modulate the parameters of other ControlSignals. This is done using the modulation argument, but instead of using the keyword for a generic form of modulation (as in the example above), a parameter of the ControlSignal’s function is used. For example, the following shows how the parameters of a ControlSignal’s cost function can be modulated by another ControlSignal:

>>> from psyneulink import *
>>> mech = ProcessingMechanism(name='my_mech')
>>> ctl_mech_A = ControlMechanism(monitor_for_control=mech,
...                               control_signals=ControlSignal(modulates=(INTERCEPT,mech),
...                                                             cost_options=CostFunctions.INTENSITY))
>>> ctl_mech_B = ControlMechanism(monitor_for_control=mech,
...                               control_signals=ControlSignal(modulates=ctl_mech_A.control_signals[0],
...                                                             modulation=INTENSITY_COST_FCT_MULTIPLICATIVE_PARAM))
>>> comp = Composition()
...                                              ctl_mech_A,
...                                              ctl_mech_B
...                                              ])
>>> pway.pathway
[(ProcessingMechanism my_mech)]


Here, the ControlSignal of ctl_mech_A is configured to monitor the output of mech, modulate the the intercept parameter of its function (which is a Linear by default), and to implement its intensity_cost_fct (using the cost_options argument of the ControlSignal’s constructor). ctl_mech_B is configured to also monitor mech, but to modulate the multiplicative_param of the intensity_cost_fct of ctl_mech_As ControlSignal. The default for the intensity_cost_fct is Exponential, the multiplicative_param of which is rate. (Note that the pathway returned from the call to add_linear_processing_pathway contains only my_mech, since that is the only ProcessingMechanism in the Pathway. When the comp is run with an input of 3, since the default function for mech is Linear with a slope of 1 and an intercept of 0, its output is also 3, which is used by both ctl_mech_A and ctl_mech_B as their allocation. Since the ControlSignals of both use their default function —— TransferWithCosts with an identity function as its transfer_fct – the intensity of both is the same as their allocation (i.e., 3). This is used as the input to the Exponential function used as intensity_cost_function of ctl_mech_A. However, since the rate of that function is modulated by ctl_mech_B, the intensity of which is also 3, the value of the intensity_cost for ctl_mech_A is e ^ (allocation (3) * value of ctl_mech_B (also 3)) = e^9, as shown below:

>>> comp.run(inputs={mech:[3]}, num_trials=2)
[array([3.])]
>>> ctl_mech_A.control_signals[0].intensity_cost
array([8103.08392758])


The Composition must be executed for 2 trials to see this, since the value computed by ctl_mech_B must be computed on the first trial before it can have its effect on ctl_mech_A on the next (i.e., second) trial (see noted under Execution).

## Class Reference¶

class psyneulink.core.components.ports.modulatorysignals.controlsignal.ControlSignal(default_allocation=defaultControlAllocation, function=TransferWithCosts, costs_options=None, intensity_cost_function=Exponential, adjustment_cost_function=Linear, duration_cost_function=IntegratorFunction, combine_costs_function=Reduce(operation=SUM), allocation_samples=self.class_defaults.allocation_samples, modulates=None, projections=None)

A subclass of ModulatorySignal used by a ControlMechanism to modulate the parameter(s) of one or more other Mechanisms. See ModulatorySignal for additional arguments and attributes.

Parameters
• default_allocation (scalar, list or np.ndarray : defaultControlAllocation) – specifies the template and default value used for allocation; must match the shape of each item specified in allocation_samples.

• function (Function or method : default TransferWithCosts(transfer_fct=Linear(slope=1, intercept=0))) – specifies the function used to determine the intensity of the ControlSignal from its allocation; must be TransferWithCosts or a subclass of that, or one that meets the requirements described see above); see function for default behavior.

• cost_options (CostFunctions or List[CostsFunctions] : None) – specifies the cost components to include in the computation of the ControlSignal’s cost.

• intensity_cost_function (Optional[TransferFunction] : default Exponential) – specifies the function used to calculate the contribution of the ControlSignal’s intensity to its cost.

• adjustment_cost_function (Optional[TransferFunction] : default Linear) – specifies the function used to calculate the contribution of the change in the ControlSignal’s intensity (from its last_intensity value) to its cost.

• duration_cost_function (IntegratorFunction : default IntegratorFunction) – specifies the function used to calculate the contribution of the ControlSignal’s duration to its cost.

• combine_costs_function (function : default Reduce(operation=SUM)) – specifies the function used to combine the results of any cost functions that are enabled, the result of which is assigned as the ControlSignal’s cost attribute.

• allocation_samples (list, 1d array, or SampleSpec : default SampleSpec(0.1, 1, 0.1)) – specifies the values used by the ControlSignal’s owner to determine its control_allocation (see Execution).

• modulates (list of Projection specifications) – specifies the ControlProjection(s) to be assigned to the ControlSignal, and that will be listed in its efferents attribute (see Projections for additional details).

allocation

value assigned by the ControlSignal’s owner, and used as the variable of its function to determine the ControlSignal’s ControlSignal.intensity.

Type

float : default: defaultControlAllocation

last_allocation

value of allocation in the previous execution of ControlSignal’s owner.

Type

float

allocation_samples

SampleIterator created from allocation_samples specification and used to generate a set of values to sample by the ControlSignal’s owner when determining its control_allocation.

Type

SampleIterator

function

converts allocation into the ControlSignal’s intensity. The default is the identity function, which assigns the ControlSignal’s allocation as its intensity, and does not calculate any costs. See ControlSignals_Function for additional details.

Type

TransferWithCosts

value

result of the ControlSignal’s function; same as intensity and control_signal.

Type

float

intensity

result of the ControlSignal’s function; assigned as the value of the ControlSignal’s ControlProjection, and used to modify the value of the parameter to which the ControlSignal is assigned; same as control_signal.

Type

float

last_intensity

the intensity of the ControlSignal on the previous execution of its owner.

Type

float

control_signal

result of the ControlSignal’s function; same as intensity.

Type

float

cost_options

boolean combination of currently assigned CostFunctions. Specified initially in costs argument of ControlSignal’s constructor; can be modified using the assign_cost_options method.

Type

CostFunctions or None

intensity_cost_function

calculates intensity_cost from the current value of intensity. It can be any TransferFunction, or any other function that takes and returns a scalar value. The default is Exponential. It can be disabled permanently for the ControlSignal by assigning None.

Type

TransferFunction : default default Exponential

intensity_cost

cost associated with the current intensity.

Type

float

adjustment_cost_function

calculates adjustment_cost based on the change in intensity from last_intensity. It can be any TransferFunction, or any other function that takes and returns a scalar value. It can be disabled permanently for the ControlSignal by assigning None.

Type

TransferFunction : default Linear

adjustment_cost

cost associated with last change to intensity.

A ControlSignal’s adjustment_cost, and its adjustment_cost_function are distinct from the reconfiguration_cost and compute_reconfiguration_cost <ControlMechanism.compute_reconfiguration_cost function of the ControlMechanism to which the ControlSignal belongs (see ControlMechanism Reconfiguration Cost for additional details).

Type

float

duration_cost_function

calculates an integral of the ControlSignal’s cost. It can be any IntegratorFunction, or any other function that takes a list or array of two values and returns a scalar value. It can be disabled permanently for the ControlSignal by assigning None.

Type

IntegratorFunction : default Linear

duration_cost

intregral of cost.

Type

float

combine_costs_function

combines the results of all cost functions that are enabled, and assigns the result to cost. It can be any function that takes an array and returns a scalar value.

Type

function : default Reduce(operation=SUM)

cost

combined result of all cost functions that are enabled.

Type

float

efferents

a list of the ControlProjections assigned to (i.e., that project from) the ControlSignal.

Type

[List[ControlProjection]]

projection_type
_validate_params(request_set, target_set=None, context=None)

Validate cost functions and allocation_samples

Checks if: - cost functions are all appropriate

(i.e., are references to valid ControlProjection costFunctions (listed in self.costFunctions)

• allocation_samples is a list, array, range or SampleSpec

_instantiate_attributes_before_function(function=None, context=None)

Instantiate default variable if it is None or numeric :param function:

_instantiate_function(function, function_params=None, context=None)

Instantiate function defined in <subclass>.function or <subclass>.function

Instantiate params[FUNCTION] if present, and assign it to self.function

If params[FUNCTION] is present and valid,

it is assigned as the function’s execute method, overriding any direct implementation of self.function

If FUNCTION IS in params:
• if it is a Function object, it is simply assigned to self.function;

• if it is a Function class reference:

it is instantiated using self.defaults.variable and, if present, params[FUNCTION_PARAMS]

If FUNCTION IS NOT in params:
• if self.function IS implemented, it is assigned to params[FUNCTION]

• if self.function IS NOT implemented: program error (should have been caught in _validate_function)

Upon successful completion:
• self._function === self.function

• self.execute should always return the output of self.function in the first item of its output array;

this is done by Function.execute; any subclass override should do the same, so that…

• value is value[0] returned by self.execute

_instantiate_allocation_samples(context=None)

Assign specified allocation_samples to a SampleIterator.

_parse_port_specific_specs(owner, port_dict, port_specific_spec)

Get ControlSignal specified for a parameter or in a ‘control_signals’ argument

Tuple specification can be:

(parameter name, Mechanism) [TBI:] (Mechanism, parameter name, weight, exponent, projection_specs)

Returns params dict with CONNECTIONS entries if any of these was specified.

_update(params=None, context=None)

Update value (intensity) and costs

compute_costs(intensity, context=None)

Compute costs based on self.value (intensity).

exception psyneulink.core.components.ports.modulatorysignals.controlsignal.ControlSignalError(error_value)