Skip to content

Commit

Permalink
refactor/transferfunction/scale_and_offset (#3184)
Browse files Browse the repository at this point in the history
• transferfunctions.py:
  * Add bounds to Linear (docs are wrong and code doesn't implement them)
  * Refactor bounds to use getter, and to list dependencies on scale and offset, and to use "natural bounds" or the like
  - rename bounds -> range
  - add DeterministicTransferFunction subclass of TransferFunction:
    - scale and offset Parameters used by all subclasses
    - add _range_setter() that adjusts range based on scale and/or offset
---------

Co-authored-by: jdcpni <pniintel55>
  • Loading branch information
jdcpni authored Feb 1, 2025
1 parent 1fed0ad commit 7cacab6
Show file tree
Hide file tree
Showing 15 changed files with 794 additions and 695 deletions.
2 changes: 1 addition & 1 deletion psyneulink/core/components/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@ def _get_compilation_params(self):
"control_signal", "competition",
"has_recurrent_input_port", "enable_learning",
"enable_output_type_conversion", "changes_shape",
"output_type", "bounds", "internal_only",
"output_type", "range", "internal_only",
"require_projection_in_composition", "default_input",
"shadow_inputs", "compute_reconfiguration_cost",
"reconfiguration_cost", "net_outcome", "outcome",
Expand Down
2 changes: 1 addition & 1 deletion psyneulink/core/components/functions/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ class Function_Base(Function):
prefs=None \
)
Implement abstract class for Function category of Component class
Abstract base class for Function category of Component class
COMMENT:
Description:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,6 @@ class OneHot(SelectionFunction):
function. Values specified for parameters in the dictionary override any assigned to those parameters in
arguments of the constructor.
bounds : None
owner : Component
`component <Component>` to which to assign the Function.
Expand Down
1,088 changes: 579 additions & 509 deletions psyneulink/core/components/functions/nonstateful/transferfunctions.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1735,8 +1735,6 @@ class MatrixTransform(TransformFunction): # -----------------------------------
be used if `variable <MatrixTransform.variable>` is a scalar (i.e., has only one element), and **operation**
is set to *L0* (since it is not needed, and can produce a divide by zero error).
bounds : None
params : Dict[param keyword: param value] : default None
a `parameter dictionary <ParameterPort_Specification>` that specifies the parameters for the
function. Values specified for parameters in the dictionary override any assigned to those parameters in
Expand Down Expand Up @@ -1823,7 +1821,6 @@ class Parameters(TransformFunction.Parameters):
matrix = Parameter(None, modulable=True, mdf_name='B')
operation = Parameter(DOT_PRODUCT, stateful=False)
normalize = Parameter(False)
bounds = None

@check_user_specified
@beartype
Expand Down
2 changes: 1 addition & 1 deletion psyneulink/core/components/mechanisms/mechanism.py
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ class Mechanism_Base(Mechanism):
output_ports, \
)
Base class for Mechanism.
Abstract base class for Mechanism.
The arguments below can be used in the constructor for any subclass of Mechanism.
See `Component <Component_Class_Reference>` and subclasses for additional arguments and attributes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@
after the TransferMechanism has been constructed must be done to its base value (see `ModulatorySignal_Modulation`
for additional information).
Finally, `clipping <TransferMechanism.clip>` can also be used to cap the result to within specified bounds::
Finally, `clipping <TransferMechanism.clip>` can also be used to cap the result to within specified range::
>>> my_linear_tm.clip = (.5, 1.2)
>>> my_linear_tm.execute([1.0, 1.0, 1.0])
Expand All @@ -487,7 +487,7 @@
>>> my_linear_tm.execute([1.0, 1.0, 1.0])
array([[0.5 , 1.01316799, 1.2 ]])
Note that the bounds specified in **clip** apply to all elements of the result if it is an array.
Note that the range specified in **clip** applies to all elements of the result if it is an array.
.. _TransferMechanism_Examples_Execution_With_Integration:
Expand Down Expand Up @@ -859,7 +859,6 @@
__all__ = [
'INITIAL_VALUE', 'CLIP', 'INTEGRATOR_FUNCTION', 'INTEGRATION_RATE',
'TERMINATION_THRESHOLD', 'TERMINATION_MEASURE', 'TERMINATION_MEASURE_VALUE',
'Transfer_DEFAULT_BIAS', 'Transfer_DEFAULT_GAIN', 'Transfer_DEFAULT_LENGTH', 'Transfer_DEFAULT_OFFSET',
'TransferError', 'TransferMechanism',
]

Expand All @@ -874,13 +873,6 @@
termination_keywords = [EXECUTION_COUNT, NUM_EXECUTIONS_BEFORE_FINISHED]


# TransferMechanism default parameter values:
Transfer_DEFAULT_LENGTH = 1
Transfer_DEFAULT_GAIN = 1
Transfer_DEFAULT_BIAS = 0
Transfer_DEFAULT_OFFSET = 0
# Transfer_DEFAULT_RANGE = np.array([])

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -917,19 +909,19 @@ def _clip_setter(value, owning_component=None, context=None):

if (value is not None
and owning_component.function
and hasattr(owning_component.function, 'bounds')
and owning_component.function.bounds is not None
and isinstance(owning_component.function.bounds, tuple)
and owning_component.function.bounds != (None, None)):
bounds = owning_component.function.bounds
if bounds[0] is None:
and hasattr(owning_component.function, 'range')
and owning_component.function.range is not None
and isinstance(owning_component.function.range, tuple)
and owning_component.function.range != (None, None)):
range = owning_component.function.range
if range[0] is None:
lower_bound = -np.inf
else:
lower_bound = bounds[0]
if bounds[1] is None:
lower_bound = range[0]
if range[1] is None:
upper_bound = np.inf
else:
upper_bound = bounds[1]
upper_bound = range[1]
if value[0] is None:
lower_clip = -np.inf
else:
Expand Down Expand Up @@ -962,7 +954,6 @@ def _clip_setter(value, owning_component=None, context=None):
return value


# IMPLEMENTATION NOTE: IMPLEMENTS OFFSET PARAM BUT IT IS NOT CURRENTLY BEING USED
class TransferMechanism(ProcessingMechanism_Base):
"""
TransferMechanism( \
Expand Down Expand Up @@ -1006,7 +997,7 @@ class TransferMechanism(ProcessingMechanism_Base):
specifies `IntegratorFunction` to use when `integrator_mode <TransferMechanism.integrator_mode>` is True (see
`Execution with Integration <TransferMechanism_Examples_Execution_With_Integration>` for additional details).
initial_value : value, list or np.ndarray : default Transfer_DEFAULT_BIAS
initial_value : value, list or np.ndarray : 0
specifies the starting value for integration when `integrator_mode <TransferMechanism.integrator_mode>` is
True; must be the same length `variable <Mechanism_Base.variable>` (see
`TransferMechanism_Execution_Integration_Initialization` for additional details).
Expand Down Expand Up @@ -1074,9 +1065,10 @@ class TransferMechanism(ProcessingMechanism_Base):
maximum value is set to the value of clip that it exceeds. If either item is `None`, no clipping is
performed for that item. If the `function <Mechanism_Base.function>` returns an array, the clip is applied
elementwise (i.e., the clip is applied to each element of the array independently). If either item is outside
the interval of the function's `bounds <TransferFunction.bounds>` after its `scale <TransferFunction.scale>` and
`offset <TransferFunction.offset>` have been applied (i.e., :math:`function.bounds(lower,upper) * scale +
offset`), a warning is issued and that item of the clip is ignored.
the interval of the function's `range <TransferFunction.range>` Parameter after its `scale
<TransferFunction.scale>` and `offset <TransferFunction.offset>` Parameters have been applied (i.e.,
:math:`function.range(lower,upper) * scale + offset`), a warning is issued and the corresponding items of
clip are ignored.
integrator_mode : bool
determines whether the TransferMechanism uses its `integrator_function <TransferMechanism.integrator_function>`
Expand Down Expand Up @@ -1152,11 +1144,6 @@ class TransferMechanism(ProcessingMechanism_Base):
componentType = TRANSFER_MECHANISM

classPreferenceLevel = PreferenceLevel.SUBTYPE
# These will override those specified in TYPE_DEFAULT_PREFERENCES
# classPreferences = {
# PREFERENCE_SET_NAME: 'TransferCustomClassPreferences',
# # REPORT_OUTPUT_PREF: PreferenceEntry(False, PreferenceLevel.INSTANCE),
# }

# TransferMechanism parameter and control signal assignments):

Expand Down Expand Up @@ -1554,7 +1541,7 @@ def _instantiate_attributes_after_function(self, context=None):

self.parameters.value.history_min_length = self._termination_measure_num_items_expected - 1

# Force call to _clip_setter in order to check against bounds (now that any function bounds are known)
# Force call to _clip_setter in order to check against range (now that any function range are known)
if self.clip is not None:
self.clip = self.clip

Expand Down
2 changes: 1 addition & 1 deletion psyneulink/core/components/projections/projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ class Projection_Base(Projection):
feedback=None \
)
Base class for all Projections.
Abstract base class for all Projections.
The arguments below can be used in the constructor for any subclass of Mechanism.
See `Component <Component_Class_Reference>` and subclasses for additional arguments and attributes.
Expand Down
10 changes: 6 additions & 4 deletions psyneulink/core/globals/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
'DDM_MECHANISM', 'DECAY', 'DECELERATING_TIMER_FUNCTION',
'DEFAULT', 'DEFAULT_CONTROL_MECHANISM', 'DEFAULT_INPUT', 'DEFAULT_MATRIX',
'DEFAULT_PREFERENCE_SET_OWNER', 'DEFAULT_PROCESSING_MECHANISM', 'DEFAULT_VARIABLE',
'DEFERRED_ASSIGNMENT', 'DEFERRED_DEFAULT_NAME', 'DEFERRED_INITIALIZATION', 'DETERMINISTIC',
'DICT', 'DictionaryMemory_FUNCTION', 'DIFFERENCE', 'DIFFERENCE',
'DIFFUSION', 'DIRECT', 'DISABLE', 'DISABLE_PARAM',
'DEFERRED_ASSIGNMENT', 'DEFERRED_DEFAULT_NAME', 'DEFERRED_INITIALIZATION',
'DETERMINISTIC', 'DETERMINISTIC_TRANSFER_FUNCTION_TYPE',
'DICT', 'DictionaryMemory_FUNCTION', 'DIFFERENCE', 'DIFFERENCE', 'DIFFUSION', 'DIRECT', 'DISABLE', 'DISABLE_PARAM',
'DIST_FUNCTION_TYPE', 'DIST_MEAN', 'DIST_SHAPE', 'DISTANCE_FUNCTION', 'DISTANCE_METRICS',
'DISTRIBUTION_FUNCTION_TYPE', 'DIVISION', 'DOT_PRODUCT',
'DRIFT_DIFFUSION_INTEGRATOR_FUNCTION', 'DRIFT_ON_A_SPHERE_INTEGRATOR_FUNCTION', 'DROPOUT_FUNCTION',
Expand Down Expand Up @@ -119,7 +119,7 @@
'PREDICTION_MECHANISM_OUTPUT', 'PREDICTION_MECHANISM_PARAMS', 'PREDICTION_MECHANISM_TYPE',
'PREFS_ARG', 'PREF_BASE_VALUE', 'PREF_CURRENT_VALUE',
'PREFERENCE_SET', 'PREFERENCE_SET_NAME', 'PREF_LEVEL', 'PREFS', 'PREFS_OWNER', 'PREVIOUS_VALUE', 'PRIMARY',
'PROB', 'PROB_INDICATOR', 'PROBABILISTIC',
'PROB', 'PROB_INDICATOR', 'PROBABILISTIC', 'PROBABILISTIC_TRANSFER_FUNCTION_TYPE',
'PROCESS', 'PROCESS_COMPONENT_CATEGORY', 'PROCESS_DEFAULT_MECHANISM', 'PROCESS_DEFAULT_PROJECTION_FUNCTION',
'PROCESS_EXECUTE', 'PROCESS_INIT', 'PROCESSES', 'PROCESSES_DIM', 'PROCESSING', 'PROCESSING_MECHANISM',
'PROCESSING_PATHWAY', 'PRODUCT', 'PROGRESS_BAR_CHAR', 'PROJECTION', 'PROJECTION_DIRECTION', 'PROJECTION_PARAMS',
Expand Down Expand Up @@ -721,6 +721,8 @@ class Loss(Enum):
MEMORY_FUNCTION_TYPE = "MEMORY FUNCTION TYPE"
INTEGRATOR_FUNCTION_TYPE = "INTEGRATOR FUNCTION TYPE"
TRANSFER_FUNCTION_TYPE = "TRANSFER FUNCTION TYPE"
DETERMINISTIC_TRANSFER_FUNCTION_TYPE = "DETERMINISTIC TRANSFER FUNCTION TYPE"
PROBABILISTIC_TRANSFER_FUNCTION_TYPE = "PROBABILISTIC TRANSFER FUNCTION TYPE"
TIMER_FUNCTION_TYPE = "TIMER FUNCTION TYPE"
LEABRA_FUNCTION_TYPE = "LEABRA FUNCTION TYPE"
DISTRIBUTION_FUNCTION_TYPE = "DISTRIBUTION FUNCTION TYPE"
Expand Down
16 changes: 8 additions & 8 deletions tests/composition/test_report.py

Large diffs are not rendered by default.

Loading

0 comments on commit 7cacab6

Please sign in to comment.