EpisodicMemoryMechanism¶
Contents¶
Overview¶
An EpisodicMemoryMechanism is a ProcessingMechanism that can store and retrieve items from a content addressable
memory; that is, on each execution it can store an item presented to it as input, and use that to retrieve an item
from its memory based on the content of the input. The MemoryFunction
assigned as its function
determines how items are stored and retrieved. Each memory is a list or array
composed of items referred to as memory fields, each of which is a list or
array. Memories can have an arbitrary number of fields, and each of those can be of arbitrary shape, however all
memories for a given instance of an EpisodicMemoryMechanism must have the same shape (number of fields, and shapes
of corresponding fields). Each InputPort of an EpisodicMemoryMechanism provides the input for a corresponding
field of a memory to be stored and used for retrieval. By default, each OutputPort contains the value of a field
of the last retrieved memory although, as with any Mechanism, OutputPorts can be configured in other ways. The full set of stored memories can be accessed from the Mechanism’s memory
attribute, which references its function's
memory Parameter. Other Parameters of its function (e.g., that regulate the probability of storage
and/or retrieval – see ContentAddressableMemory
) can be accessed and/or modulated
in the standard way for a Mechanism’s function
.
At present, EpisodicMemoryMechanism supports the following two MemoryFunctions:
ContentAddressableMemory
– a general form of memory, that stores and retrieves memories as described above; any (weighted) combination of its fields can be used for retrieval.DictionaryMemory
– a more specific form of memory, that has only two fields, for keys and values, that stores these as pairs; retrieval is based on similarity to the key; this implements a format commonly used by applications that use dictionaries as a form of external memory.
Creating an EpisodicMemoryMechanism¶
InputPorts, Entries and Memory Fields¶
An EpisodicMemoryMechanism is created by calling its constructor with specifications used to implement its
MemoryFunction
and the shape of entries stored in memory
. The latter
is determined by the number of fields and shape of each in an entry.
These can be specified using any of the following arguments:
default_variable or input_shapes – these are specified in the standard way that the
variable
is specified for any Component (see default_variable,input_shapes
, respectively); the specified value is passed to the constructor for the EpisodicMemoryMechanism’sfunction
), which determines the shape of an entry inmemory
; thememory
itself remains empty until the Mechanism is executed and an item is stored.Hint
Use default_variable rather than memory to specify the shape of memory but keep it empty until the first entry is stored; note, however, that since retrieval is executed before storage (see Execution), the first execution will return an entry of zeros.
memory – specifies a set of entries to be stored in
memory
; it is passed to the constructor for the EpisodicMemoryMechanism’sfunction
) as its initializer argument (seeinitializer
for an example).function – this can be used to specify a constructor for the
function
, in which the default_variable or initializer arguments are used to specify the shape of entries inmemory
. If default variable is used,memory
remains empty until the Mechanism is executed and an item is stored. If initializer is used, the items specified are stored inmemory
and are available for retrieval in the first execution of the Mechanism (seeinitializer
for an example).
The above specifications are also used to create the input_ports
for the Mechanism
in the same way that the variable
is used for any Mechanism (see Mechanism Variable for additional information), with the number of InputPorts created equal to the
number of fields in an entry of memory
. Each input_port
provides the value assigned to a corresponding field
of the entry stored in memory
, and used to retrieve one similar to it. By default,
input_port
are named FIELD_n_INPUT, where “n” is replaced by the index of
each field; however, they can be named explicitly by specifying a list of strings in the input_ports argument of
the constructor; the number of these must equal the number of fields specified in default_variable or input_shapes.
Function Parameters¶
Parameters that govern the storage and retrieval process are specified as arguments to the MemoryFunction
specified
in the function of the constructor (for example, see ContentAddressableMemory
for parameters of the default
function
).
OutputPorts¶
By default, a number of OutputPorts is created equal to the number of InputPorts, each named either RETRIEVED_FIELD_n or RETRIEVED_<user specified InputPort name>, that receive the values of the corresponding fields of a retrieved memory. OutputPort names can be specified explicitly, by assigning a list of strings to the output_ports argument of the Mechanism’s constructor; however, in that case, or if any other forms of OutputPort specification are assigned to output_ports, then only the number of OutputPorts specified are created, which may not match the number of fields of a retrieved memory.
Structure¶
Memory Fields¶
Entries in the memory
of an EpisodicMemoryMechanism are comprised of fields: lists
or 1d arrays within the outer list or array that comprise each entry. An entry can have an arbitrary number of fields,
and fields can be of arbitrary length. However, all entries must have the same form (i.e., number of fields and shape
of corresponding fields across entries). One InputPort of the EpisodicMemoryMechanism is assigned to each field. Thus,
fields can be used to store different types of information in each field, and to retrieve entries from memory based on
all fields, or a weighted combination of them (as determined by the MemoryFunction
assigned to function
; for example, this can be used to configure the default function,
ContentAddressableMemory
, as a form of key-value dictionary).
The shape of an entry in memory
is determined by the shape of the Mechanism’s
variable
. specified in the default_variable or input_shapes arguments of its constructor
(see Creating an EpisodicMemoryMechanism). Each item of variable
corresponds to a field.
Both memory
and all entries are stored in the EpisodicMemoryMechanism’s function
as np.ndarrays, the dimensionality of which is determined by the shape of an
entry and its fields. Fields are always stored as 1d arrays; if all fields have the same length (regular), then
entries are 2d arrays and memory
is a 3d array. However, if fields have
different lengths (ragged) then, although each field is 1d, an
entry is also 1d (with dtype=’object’), and memory
is 2d (with dtype=’object’).
Input¶
An EpisodicMemoryMechanism has one or more input_ports
that receive the
entry to be stored and that is used to retrieve an existing entry from its memory. If the Mechanism is assigned
ContentAddressableMemory
as its function
, then it can have an arbitrary
number of InputPorts, the input to which is assigned to the corresponding memory field of that function. By default InputPorts are named FIELD_n_INPUT (see
Creating an EpisodicMemoryMechanism). If the Mechanism is assigned DictionaryMemory
as its function
, then it is assigned at least one InputPort (named KEY_INPUT by default),
and optionally a second (named VALUE_INPUT) if default_variable or input_shapes specifies two items; any additional
fields are ignored.
Function¶
The default function is ContentAddressableMemory
that can store entries with an arbitrary number of fields and
shapes, and retrieve them based on a weighted similarity to any combination of those fields. This can be configured
as a key-value dictionary, or a more specific function dedicated
to that purpose – DictionaryMemory
– can be assigned, in which entries are restricted to be key-value pairs,
and retrieved based on similarity only to the key. A custom function can also be specified, so long as it
meets the following requirements:
it must accept a list or array as its first argument, the items of which are lists or arrays;
it must return a list or array of the same size and shape as the input;
it must implement a
memory
attribute, that can be accessed by the EpisodicMemoryMechanism’smemory
attribute.if the function is classed, and has a classmethod _enforce_memory_shape(), this is used to ensure that any specification of the memory argument in the EpisodicMemoryMechanism’s constructor conforms to the format required for the memory attribute of the function.
Output¶
By default, an EpisodicMemoryMechanism has a number of OutputPorts equal to its number of InputPorts,
each of which is assigned the value of a corresponding field of the entry retrieved from memory (see
Creating an EpisodicMemoryMechanism for naming). However, if OutputPorts were specified in the constructor, then there
may be a different number of OutputPorts than InputPorts and memory fields,
and thus some of the latter may not be reflected in any of the Mechanism’s output_ports
. If the function
is a DictionaryMemory
,
then it will have at least one OutputPort, named KEY_OUTPUT, that is assigned the key (first field) of the entry
retrieved from memory and, if two fields are specified in default_variable or sze, the Mechanism will have a
second OutputPort named VALUE_OUTPUT that is assigned the value (second field) of the entry retrieved from memory;
any additional ones are ignored and no other OutputPorts are created.
Execution¶
When an EpisodicMemoryMechanism is executed, its function
carries out
the following operations:
retrieve an item from
memory
based on thevalue
of itsinput_ports
; if no retrieval is made, then an appropriately shaped zero-valued array is returned.
store the
value
of itsinput_ports
as an entry inmemory
.
assign the value of the entry retrieved to its
output_ports
, based on how the latter are configured (see OutputPorts).Note
In general, retrieval is executed before storage, so that the current items is not also retrieved; however, the order of storage and retrieval is determined by the EpisodicMemoryMechanism’s
function
.
Examples
(See ContentAddressableMemory for additional examples of how to use that Function, including how it can be configured to implement a key-value dictionary.)
Default EpisodicMemoryMechanism¶
The following example creates a default EpisodicMemoryMechanism (with no initial memory):
>>> my_em = EpisodicMemoryMechanism()
>>> my_em.execute([[1,2]])
array([[0, 0]])
>>> my_em.execute([[2,5]])
array([[1., 2.]])
The default_variable
for an EpisodicMemoryMechanism is [[0,0]]
, so the
format of an entry in memory
is a single field with two elements. Note that, since
it was not assigned any initial memory, the first execution returns an entry comprised of zeros. However, the input to
the Mechanism in that execution ([[1,2]]
) is stored as an entry in memory
, and on
the second execution, since that is now the only entry in memory
, that is what is
returned.
Format entries using default_variable¶
In this example, the default_variable argument is used to format the entries of memory
to have two fields, one with two elements and the other with three:
>>> my_em = EpisodicMemoryMechanism(default_variable=[[0,0],[0,0,0]])
>>> my_em.execute([[1,2],[3,4,5]])
array([array([0, 0]), array([0, 0, 0])], dtype=object)
As in the previous example, the first execution returns zeros since memory
as not
been initialized; however, notice that in this case they are formated as specified in default_variable. Note
also that even though a list is specified for default_variable, the entry returned is an array; memory
and all of its entries are always formated as arrays.
Format entries using input_shapes¶
The input_shapes argument can also be used to format entries:
>>> my_em = EpisodicMemoryMechanism(input_shapes=[2,3])
>>> my_em.execute([[1,2],[3,4,5]])
array([array([0, 0]), array([0, 0, 0])], dtype=object)
Note that each element of input_shapes specifies the length of a field
(see EpisodicMemoryMechanism_Creation_Default_Variable_and_Size
for additional details).
Initialize memory¶
The memory argument of an EpisodicMemoryMechanism’s constructor can be used to initialize its memory
:
>>> my_em = EpisodicMemoryMechanism(memory=[[[1,2],[3,4,5]],
... [[10,9],[8,7,6]]])
>>> my_em.execute([[1,2],[3,4,6]])
array([array([1., 2.]), array([3., 4., 5.])], dtype=object)
>>> my_em.execute([[1,2],[3,4,6]])
array([array([1., 2.]), array([3., 4., 6.])], dtype=object)
Note that there was no need to use default_variable or input_shapes to format entries here, since that is determined
by the entries in the memory argument. If default_variable or input_shapes is specified, its shape must be the
same as the entries specified in memory. In this example, since memory
was
initialized, the first execution returns the closest value to the input, which is used as the retrieval cue. In the
second execution, the input from the first execution is returned, since it was stored after the first retrieval. The
current contents of memory can be inspected using the memory
attribute:
>>> my_em.memory
array([[array([1., 2.]), array([3., 4., 5.])],
[array([10., 9.]), array([8., 7., 6.])],
[array([1., 2.]), array([3., 4., 6.])]], dtype=object)
Notice that there is only one entry for [array([1., 2.]), array([3., 4., 6.])]
, even though it was provided
as input to execute twice. This is because the default function
is
ContentAddressableMemory
, and the default value of its duplicate_entries_allowed
attribute is False. Notice also that that the dtype of the
memory
array is object, since its entries are ragged arrays (i.e., ones with fields
of different sizes).
Initialize memory in function¶
The contents of memory
can also be initialized using the initializer argument
in the constructor for the EpisodicMemoryMechanism’s function
:
>>> my_em = EpisodicMemoryMechanism(
... function=ContentAddressableMemory(initializer=[[[1,2],[3,4,5]],
... [[10,9],[8,7,6]]]))
>>> my_em.function.memory
array([[array([1., 2.]), array([3., 4., 5.])],
[array([10., 9.]), array([8., 7., 6.])]], dtype=object)
>>> my_em.memory
array([[array([1., 2.]), array([3., 4., 5.])],
[array([10., 9.]), array([8., 7., 6.])]], dtype=object)
Notice memory
actually refers to the contents of the function
’s memory
attribute.
The input_ports
of an EpisodicMemoryMechanims correspond to fields of entries
in memory
(see Input), that by default are named
FIELD_n_INPUT
:
>>> my_em.input_ports.names
['FIELD_0_INPUT', 'FIELD_1_INPUT']
By default, an EpisodicMemoryMechanims also has the same number of output_ports
as input_ports
, named correspondingly RETRIEVED_FIELD_n
:
>>> my_em.output_ports.names
['RETRIEVED_FIELD_0', 'RETRIEVED_FIELD_1']
These are assigned the values of the fields of the entry retrieved from memory
.
The names of input_ports
can be customized by specifying a list of names in
the input_ports argument of the Mechanism’s constructor:
>>> my_em = EpisodicMemoryMechanism(input_shapes=[2,2,2],
... input_ports=['KEY', 'VALUE', 'LABEL'])
>>> my_em.input_ports.names
['KEY', 'VALUE', 'LABEL']
The number of names must be equal to the number of fields in an entry (in this case, 3). Similarly, the output_ports
can be named in the output_ports argument of the constructor. In this case,
there can be fewer items specified, in which case the number of fields assigned to OutputPorts will be limited by the
number of OutputPorts specified in the argument:
>>> my_em = EpisodicMemoryMechanism(memory=[[[1,2],[3,4,5],[6,7]],
... [[7,6],[5,4,3],[2,1]]],
... input_ports=['KEY', 'VALUE', 'LABEL'],
... output_ports=['VALUE_RETRIEVED', 'LABEL_RETRIEVED'])
>>> my_em.execute([[1,2],[3,4,5],[6,7]])
array([array([1., 2.]), array([3., 4., 5.]), array([6., 7.])],
dtype=object)
>>> my_em.output_ports
[(OutputPort VALUE_RETRIEVED), (OutputPort LABEL_RETRIEVED)]
>>> my_em.output_ports.values
[array([1., 2.]), array([3., 4., 5.])]
Notice that the first two fields of the retrieved entry are assigned to the two OutputPorts, and the third is not assigned to an OutputPort (see Custom OutputPorts for additional information about customizing OutputPorts).
Class Reference¶
- class psyneulink.library.components.mechanisms.processing.integrator.episodicmemorymechanism.EpisodicMemoryMechanism(default_variable=None, input_shapes=None, memory=None, function=None, params=None, name=None, prefs=None, **kwargs)¶
Subclass of ProcessingMechanism that implements a content addressable dictionary. See Mechanism for arguments and attributes.
- Parameters
_EpisodicMemoryMechanism_Default_Variable (.) –
default_variable (list or ndarray) – specifies the format used for entries in
memory
.memory (list or ndarray) – initial set of entries for
memory
. It should be either a 3d regular array or a 2d ragged array if the fields of an entry have different lengths; assigned as the initializer argument of the constructor for theMemoryFunction
specified in function (seeinitializer
for default assignment).
- input_ports¶
a list of the Mechanism’s InputPorts, the number of which is equal to the number of memory fields in an entry of the Mechanism’s
memory
, and that are named FIELD_n_INPUT by default (see Input and Mechanisminput_ports
for additional information).- Type
ContentAddressableList[str, InputPort]
- function¶
takes the Mechanism’s
variable
and uses it as a cue to retrieve an entry from memory based on its distance from existing entries, and then storesvariable
in memory (see Function and Mechanismfunction
for additional information).- Type
- memory¶
contains entries stored in the
function
smemory
attribute (for example,memory
).- Type
3d array
- output_ports¶
a list of the Mechanism’s OutputPorts, the number of which is, by default, equal to the number of memory fields in an entry of the Mechanism’s
memory
, and each of which is assigned the value of the corresponding field of the last entry retrieved frommemory
. However, as with any Mechanism, the number and value of OutputPorts can be customized (see Output and Mechanismoutput_ports
for additional information).- Type
ContentAddressableList[str, OutputPort]
- _handle_default_variable(default_variable=None, input_shapes=None, input_ports=None, function=None, params=None)¶
- Override to initialize or validate default_variable based on _memory_init or function.memory
if memory argument for Mechanism is specified and default_variable is not, use former to specify latter;
if both are specified, validate that they are the same shape;
if function.memory is specified and default_variable is not, use former to specify latter;
if both are specified, validate that they are the same shape;
- if default_variable is specified and neither memory arg of Mechanism nor function.memory is specified,
use default_variable to specify function.memory.
Note: handling this here insures that input_ports are specified/validated using correct default_variable
- _instantiate_input_ports(context=None)¶
Override to assign default names to input_ports
- _instantiate_function(function, function_params, context)¶
Assign memory to function if specified in Mechanism’s constructor
- _instantiate_output_ports(context=None)¶
Generate OutputPorts with names specified and values with shapes equal to corresponding InputPorts
If OutputPorts have not been specified, use InputPort names with prefix replaced and suffix removed.
- If any OutputPorts are specified as strings, those are used as names and are the only OutputPorts instantiated
(even if the number is less than the number of InputPorts)
- If any OutputPorts are specified in a form other than a string, then this method is ignored and OutputPorts
are instantiated in call to super().instantiate_output_ports; note: in that case, the shapes of the value are as specified and may not necessarily correspond to the
shapes of the corresponding Inputs (i.e., memory fields).
- _parse_function_variable(variable, context=None)¶
Parses the variable passed in to a Component into a function_variable that can be used with the Function associated with this Component
- property memory¶
Return function’s memory attribute