ControlSignal¶
Contents:¶
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 are specified in the control argument or CONTROL 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 control argument of its constructor, or in an entry of a dictionary assigned to the params argument
with the key CONTROL. These are assigned to its efferents
attribute. See
Port Projections for additional details concerning the specification of Projections when
creating a Port, including examples of ControlProjection specification.
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 using its constructor in the function argument of the
ControlSignal’s constructor, with a different TransferFunction
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
's cost functions can also be assigned
using its constructor. A function other than TransferWithCosts
can also be assigned to the ControlSignal’s
function argument, 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:
intensity_cost
- calculated by theintensity_cost_fct
based on the currentintensity
of the ControlSignal, and can be referenced by the ControlSignal’sintensity_cost_function
attribute.
adjustment_cost
- calculated by theadjustment_cost_fct
based on a change in the ControlSignal’sintensity
from its last value, and can be referenced by the ControlSignal’sadjustment_cost_function
attribute.
duration_cost
- calculated by theduration_cost_fct
based on an integral of the ControlSignal’scost
, and can be referenced by the ControlSignal’sduration_cost_function
attribute.
cost
- calculated by thecombine_costs_fct
that combines the results of any cost functions that are enabled, and can be referenced by the ControlSignal’sduration_cost_function
attribute.
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,
... MODULATION: ADDITIVE
... }])
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(control=(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()
>>> pway = comp.add_linear_processing_pathway(pathway=[mech,
... 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_A
s 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(owner=None, reference_value=None, default_allocation=None, input_shapes=None, transfer_function=None, cost_options=None, intensity_cost_function=None, adjustment_cost_function=None, duration_cost_function=None, combine_costs_function=None, allocation_samples=None, modulation=None, control=None, params=None, name=None, prefs=None, **kwargs)¶
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 inallocation_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 itsallocation
; must be TransferWithCosts or a subclass of that, or one that meets the requirements described seeabove
); seefunction
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 itscost
.adjustment_cost_function (Optional[TransferFunction] : default Linear) – specifies the function used to calculate the contribution of the change in the ControlSignal’s
intensity
(from itslast_intensity
value) to itscost
.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’scost
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 itscontrol_allocation
(see Execution).control (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 thevariable
of itsfunction
to determine the ControlSignal’sControlSignal.intensity
.- Type
float : default: defaultControlAllocation
- last_allocation¶
value of
allocation
in the previous execution of ControlSignal’sowner
.- Type
float
- allocation_samples¶
SampleIterator
created from allocation_samples specification and used to generate a set of values to sample by the ControlSignal’sowner
when determining itscontrol_allocation
.- Type
- function¶
converts
allocation
into the ControlSignal’sintensity
. The default is the identity function, which assigns the ControlSignal’sallocation
as itsintensity
, and does not calculate any costs. SeeControlSignals_Function
for additional details.- Type
- value¶
result of the ControlSignal’s
function
; same asintensity
andcontrol_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 ascontrol_signal
.- Type
float
- last_intensity¶
the
intensity
of the ControlSignal on the previous execution of itsowner
.- Type
float
- cost_options¶
boolean combination of currently assigned
CostFunctions
. Specified initially in costs argument of ControlSignal’s constructor; can be modified using theassign_cost_options
method.- Type
CostFunctions or None
- intensity_cost_function¶
calculates
intensity_cost
from the current value ofintensity
. It can be anyTransferFunction
, or any other function that takes and returns a scalar value. The default isExponential
. It can be disabled permanently for the ControlSignal by assigningNone
.- Type
TransferFunction : default default Exponential
- adjustment_cost_function¶
calculates
adjustment_cost
based on the change inintensity
fromlast_intensity
. It can be anyTransferFunction
, or any other function that takes and returns a scalar value. It can be disabled permanently for the ControlSignal by assigningNone
.- Type
TransferFunction : default Linear
- adjustment_cost¶
cost associated with last change to
intensity
.A ControlSignal’s
adjustment_cost
, and itsadjustment_cost_function
are distinct from thereconfiguration_cost
andcompute_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 anyIntegratorFunction
, 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 assigningNone
.- Type
IntegratorFunction : default Linear
- 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]]
- errorType¶
alias of
psyneulink.core.components.ports.modulatorysignals.controlsignal.ControlSignalError
- projection_type¶
alias of
psyneulink.core.components.projections.modulatory.controlprojection.ControlProjection
- _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 aSampleIterator
.
- _parse_port_specific_specs(owner, port_dict, port_specific_spec, context=None)¶
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.
- exception psyneulink.core.components.ports.modulatorysignals.controlsignal.ControlSignalError(message, component=None)¶