Skip to content

Commit

Permalink
Feat/add pathway default matrix (#2518)
Browse files Browse the repository at this point in the history
* • compositioninterfacemechanism.py:
  - _get_source_node_for_input_CIM:
        restore (modeled on _get_source_of_modulation_for_parameter_CIM) but NEEDS TESTS
  - _get_source_of_modulation_for_parameter_CIM: clean up comments, NEEDS TESTS

* -

* -

* -

* -

* -

* -

* • Nback
  - EM uses ContentAddressableMemory (instead of DictionaryMemory)
  - Implements FFN for comparison of current and retrieved stimulus and context

• Project:
  replace all instances of "RETREIVE" with "RETRIEVE"

* • objectivefunctions.py
  - add cosine_similarity (needs compiled version)

* • Project: make COSINE_SIMILARITY a synonym of COSINE
• nback_CAM_FFN:
  - refactor to implement FFN and task input
  - assign termination condition for execution that is dependent on control
  - ContentAddressableMemory: selection_function=SoftMax(output=MAX_INDICATOR,
                                                            gain=SOFT_MAX_TEMP)
• DriftOnASphereIntegrator:
  - add dimension as dependency for initializer parameter

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* -

* • test_integrator.py:
  Added identicalness test for DriftOnASphereIntegrator agains nback-paper implementation.

* -

* -

* Parameters: allow _validate_ methods to reference other parameters (#2512)

* • Scripts:
  - Updated N-back to use objective_mechanism, with commented out code for version that doesn't use it once bug is fixed
  - Deleted N-back_WITH_OBJECTIVE_MECH.py

* • Scripts:
  - Updated N-back to use objective_mechanism, with commented out code for version that doesn't use it once bug is fixed
  - Deleted N-back_WITH_OBJECTIVE_MECH.py

* • N-back.py:
  - added stimulus generation per nback-paper protocol

* - N-back.py
  tstep(s) -> trial(s)

* -

* -

* • N-back.py
  - comp -> nback_model
  - implement stim_set() method

* -

* • N-back.py:
  - added training set generation

* -

* -

* • N-back.py
  - modularized script

* -

* -

* -

* -

* • showgraph.py:
  - _assign_processing_components(): fix bug in which nested graphs not highlighted in animation.

* • showgraph.py * composition.py
  - add further description of animation, including note that animation of nested Compostions is limited.

* • showgraph.py * composition.py
  - add animation to N-back doc

* • autodiffcomposition.py
  - __init__(): move pathways arg to beginning, to capture positional assignment (i.e. w/o kw)

* -

* • N-back.py
  - ffn: implement as autodiff; still needs small random initial weight assignment

* • pathway.py
  - implement default_projection attribute

* • pathway.py
  - implement default_projection attribute

* • utilities.py:
  random_matrxi:  refactored to allow negative values and use keyword ZERO_CENTER

* • projection.py
  RandomMatrix: added class that can be used to pass a function as matrix spec

* • utilities.py
  - RandomMatrix moved here from projection.py

• function.py
  - get_matrix():  added support for RandomMatrix spec

* • port.py
  - _parse_port_spec(): added support for RandomMatrix

* • port.py
  - _parse_port_spec(): added support for RandomMatrix

* • utilities.py
  - is_matrix(): modified to support random_matrix and RandomMatrix

* • composition.py
  - add_linear_processing_pathway: add support for default_matrix argument
     (replaces default for MappingProjection for any otherwise unspecified projections)
     though still not used.

* -

* - RandomMatrix: moved from Utilities to Function

* -

* [skip ci]

* [skip ci]

* [skip ci]
• N-back.py
  - clean up script

* [skip ci]
• N-back.py
  - further script clean-up

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]
• BeukersNBackModel.rst:
  - Overview written
  - Needs other sections completed

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]

* [skip ci]
• N-back.py:
  - replace functions of TransferMechanisms with ReLU
  - replace function of Decision Mechanisms with SoftMax
  - more doc cleanup

* [skip ci]
• N-back.py:
  - replace functions of TransferMechanisms with ReLU
  - replace function of Decision Mechanisms with SoftMax
  - more doc cleanup

* [skip ci]

* -

* -

* [skip ci]

* [skip ci]
• composition.py:
  implement default_projection_matrix in add_XXX_pathway() methods

* [skip ci]
• composition.py:
  implement default_projection_matrix in add_XXX_pathway() methods

* [skip ci]
• test_composition.py:
  - add test_pathway_tuple_specs()

* -

* -

* [skip ci]

* [skip ci]

* [skip ci]

* -

Co-authored-by: jdcpni <pniintel55>
Co-authored-by: Katherine Mantel <[email protected]>
  • Loading branch information
jdcpni and kmantel authored Nov 3, 2022
1 parent 05eb311 commit f6a987a
Show file tree
Hide file tree
Showing 19 changed files with 1,051 additions and 253 deletions.
342 changes: 215 additions & 127 deletions Scripts/Models (Under Development)/N-back.py

Large diffs are not rendered by default.

81 changes: 81 additions & 0 deletions docs/source/BeukersNBackModel.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

N-Back Model (Beukers et al., 2022)
==================================================================
`"When Working Memory is Just Working, Not Memory" <https://psyarxiv.com/jtw5p>`_

Overview
--------
This implements a model of the `N-back task <https://en.wikipedia.org/wiki/N-back#Neurobiology_of_n-back_task>`_
described in `Beukers et al. (2022) <https://psyarxiv.com/jtw5p>`_. The model uses a simple implementation of episodic
memory (EM, as a form of content-retrieval memory) to store previous stimuli along with the temporal context in which
they occured, and a feedforward neural network (FFN)to evaluate whether the current stimulus is a match to the n'th
preceding stimulus (nback-level)retrieved from episodic memory. The temporal context is provided by a randomly
drifting high dimensional vector that maintains a constant norm (i.e., drifts on a sphere). The FFN is
trained, given an n-back level of *n*, to identify when the current stimulus matches one stored in EM
with a temporal context vector that differs by an amount corresponding to *n* time steps of drift. During n-back
performance, the model encodes the current stimulus and temporal context, retrieves an item from EM that matches the
current stimulus, weighted by the similarity of its temporal context vector (i.e., most recent), and then uses the
FFN to evaluate whether it is an n-back match. The model responds "match" if the FFN detects a match; otherwise, it
either responds "non-match" or, with a fixed probability (hazard rate), it uses the current stimulus and temporal
context to retrieve another sample from EM and repeat the evaluation.

This model is an example of proposed interactions between working memory (e.g., in neocortex) and episodic memory
e.g., in hippocampus and/or cerebellum) in the performance of tasks demanding of sequential processing and control,
and along the lines of models emerging machine learning that augment the use of recurrent neural networks (e.g., long
short-term memory mechanisms; LSTMs) for active memory and control with an external memory capable of rapid storage
and content-based retrieval, such as the Neural Turing Machine (NTN;
`Graves et al., 2016 <https://arxiv.org/abs/1410.5401>`_), Episodic Planning Networks (EPN;
`Ritter et al., 2020 <https://arxiv.org/abs/2006.03662>`_), and Emergent Symbols through Binding Networks (ESBN;
`Webb et al., 2021 <https://arxiv.org/abs/2012.14601>`_).

The script respectively, to construct, train and run the model:

* construct_model(args):
takes as arguments parameters used to construct the model; for convenience, defaults are defined toward the top
of the script (see "Construction parameters").
..
* train_network(args)
takes as arguments the feedforward neural network Composition (FFN_COMPOSITION) and number of epochs to train.
Note: learning_rate is set at construction (which can be specified using LEARNING_RATE under "Training parameters").
..
* run_model()
takes as arguments the drift rate in the temporal context vector to be applied on each trial,
and the number of trials to execute, as well as reporting and animation specifications
(see "Execution parameters").

The default parameters are ones that have been fit to empirical data concerning human performance
(taken from `Kane et al., 2007 <https://psycnet.apa.org/record/2007-06096-010?doi=1>`_).


The Model
---------

The models is composed of two `Compositions <Composition>`: an outer one that contains the full model (nback_model),
and an `AutodiffComposition` (ffn), nested within nback_model (see red box in Figure), that implements the
feedforward neural network (ffn).

nback_model
~~~~~~~~~~~

This contains three input Mechanisms (

Both of these are constructed in the construct_model function.
The ffn Composition is trained use

.. _nback_Fig:

.. figure:: _static/N-Back_Model_movie.gif
:align: left
:alt: N-Back Model Animation


Training
--------


Execution
---------


Script: :download:`N-back.py <../../Scripts/Models (Under Development)/Beukers_N-Back_2022.py>`
.. Script: :download:`N-back.py <../../psyneulink/library/models/Beukers -Back.py>`
2 changes: 1 addition & 1 deletion docs/source/Function.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ Function
:maxdepth: 3

.. automodule:: psyneulink.core.components.functions.function
:members: Function_Base, ArgumentTherapy
:members: Function_Base, ArgumentTherapy, RandomMatrix
:private-members:
:exclude-members: Parameters
2 changes: 1 addition & 1 deletion docs/source/Functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ Functions
UserDefinedFunction

.. automodule:: psyneulink.core.components.functions.function
:members: Function_Base, ArgumentTherapy,
:members: Function_Base, ArgumentTherapy, RandomMatrix
:private-members:
:exclude-members: Parameters
3 changes: 3 additions & 0 deletions docs/source/Models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ illustrate principles of neural and/or psychological function.
• `BotvinickConflictMonitoringModel`

• `BustamanteStroopXORLVOCModel`

• `BeukersNBackModel`

1 change: 1 addition & 0 deletions docs/source/_static/N-Back-Model_fig.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/_static/N-Back_Model_movie.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 46 additions & 6 deletions psyneulink/core/components/functions/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,13 @@
from psyneulink.core.globals.registry import register_category
from psyneulink.core.globals.utilities import (
convert_to_np_array, get_global_seed, is_instance_or_subclass, object_has_single_value, parameter_spec, parse_valid_identifier, safe_len,
SeededRandomState, contains_type, is_numeric
SeededRandomState, contains_type, is_numeric, random_matrix
)

__all__ = [
'ArgumentTherapy', 'EPSILON', 'Function_Base', 'function_keywords', 'FunctionError', 'FunctionOutputType',
'FunctionRegistry', 'get_param_value_for_function', 'get_param_value_for_keyword', 'is_Function',
'is_function_type', 'PERTINACITY', 'PROPENSITY'
'is_function_type', 'PERTINACITY', 'PROPENSITY', 'RandomMatrix'
]

EPSILON = np.finfo(float).eps
Expand Down Expand Up @@ -1201,6 +1201,48 @@ def __init__(self,
)


class RandomMatrix():
"""Function that returns matrix with random elements distributed uniformly around **center** across **range**.
The **center** and **range** arguments are passed at construction, and used for all subsequent calls.
Once constructed, the function must be called with two floats, **sender_size** and **receiver_size**,
that specify the number of rows and columns of the matrix, respectively.
Can be used to specify the `matrix <MappingProjection.matrix>` parameter of a `MappingProjection
<MappingProjection_Matrix_Specification>`, and to specify a default matrix for Projections in the
construction of a `Pathway` (see `Pathway_Specification_Projections`) or in a call to a Composition's
`add_linear_processing_pathway<Composition.add_linear_processing_pathway>` method.
.. technical_note::
A call to the class calls `random_matrix <Utilities.random_matrix>`, passing **sender_size** and
**receiver_size** to `random_matrix <Utilities.random_matrix>` as its **num_rows** and **num_cols**
arguments, respectively, and passing the `center <RandomMatrix.offset>`\-0.5 and `range <RandomMatrix.scale>`
attributes specified at construction to `random_matrix <Utilities.random_matrix>` as its **offset**
and **scale** arguments, respectively.
Arguments
----------
center : float
specifies the value around which the matrix elements are distributed in all calls to the function.
range : float
specifies range over which all matrix elements are distributed in all calls to the function.
Attributes
----------
center : float
determines the center of the distribution of the matrix elements;
range : float
determines the range of the distribution of the matrix elements;
"""

def __init__(self, center:float=0.0, range:float=1.0):
self.center=center
self.range=range

def __call__(self, sender_size:int, receiver_size:int):
return random_matrix(sender_size, receiver_size, offset=self.center - 0.5, scale=self.range)


def get_matrix(specification, rows=1, cols=1, context=None):
"""Returns matrix conforming to specification with dimensions = rows x cols or None
Expand All @@ -1215,16 +1257,14 @@ def get_matrix(specification, rows=1, cols=1, context=None):
+ INVERSE_HOLLOW_MATRIX: 0's on diagonal, -1's elsewhere (must be square matrix), otherwise generates error
+ FULL_CONNECTIVITY_MATRIX: all 1's
+ RANDOM_CONNECTIVITY_MATRIX (random floats uniformly distributed between 0 and 1)
+ RandomMatrix (random floats uniformly distributed around a specified center value with a specified range)
+ 2D list or np.ndarray of numbers
Returns 2D array with length=rows in dim 0 and length=cols in dim 1, or none if specification is not recognized
"""

# Matrix provided (and validated in _validate_params); convert to array
if isinstance(specification, (list, np.matrix)):
# # MODIFIED 4/9/22 OLD:
# return convert_to_np_array(specification)
# MODIFIED 4/9/22 NEW:
if is_numeric(specification):
return convert_to_np_array(specification)
else:
Expand Down Expand Up @@ -1272,7 +1312,7 @@ def get_matrix(specification, rows=1, cols=1, context=None):
return np.random.rand(rows, cols)

# Function is specified, so assume it uses random.rand() and call with sender_len and receiver_len
if isinstance(specification, types.FunctionType):
if isinstance(specification, (types.FunctionType, RandomMatrix)):
return specification(rows, cols)

# (7/12/17 CW) this is a PATCH (like the one in MappingProjection) to allow users to
Expand Down
9 changes: 8 additions & 1 deletion psyneulink/core/components/ports/port.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,8 @@ def test_multiple_modulatory_projections_with_mech_and_port_Name_specs(self):

from psyneulink.core import llvm as pnlvm
from psyneulink.core.components.component import ComponentError, DefaultsFlexibility, component_keywords
from psyneulink.core.components.functions.function import Function, get_param_value_for_keyword, is_function_type
from psyneulink.core.components.functions.function import \
Function, get_param_value_for_keyword, is_function_type, RandomMatrix
from psyneulink.core.components.functions.nonstateful.combinationfunctions import CombinationFunction, LinearCombination
from psyneulink.core.components.functions.nonstateful.transferfunctions import Linear
from psyneulink.core.components.shellclasses import Mechanism, Projection, Port
Expand Down Expand Up @@ -2953,6 +2954,12 @@ def _parse_port_spec(port_type=None,
if isinstance(port_specification, types.FunctionType):
port_specification = port_specification()

# RandomMatrix (used for Projection); try to resolve to a matrix
if isinstance(port_specification, RandomMatrix):
rows = len(owner.sender.value)
cols = len(owner.receiver.value)
port_specification = port_specification(rows,cols)

# ModulatorySpecification of some kind
if _is_modulatory_spec(port_specification):
# If it is a ModulatoryMechanism specification, get its ModulatorySignal class
Expand Down
Loading

0 comments on commit f6a987a

Please sign in to comment.