A buffer is an information store. It is a place where information can be put for later retrieval. Buffers are appropriate for both short term storage (as in modelling working memory, for example) and long term storage (as in large knowledge bases). They can be configured, through subclasses and properties, so as to behave in a variety of different ways (allowing, for example, decay of elements or capacity restrictions).
A stack is a kind of buffer which provides very limited access to its ordered elements. The elements of a stack are ordered in the sequence in which those elements were added to the stack. Access to the stack is only allowed through the most recent or "top" element of the stack. There are basically three operations which may be performed on a stack: the top-most element may be matched; an element may be "pushed" onto the top of the stack (thus obscuring the previous top element); and an element may be popped off the top of the stack (thus revealing a previously hidden element).
Despite the flexibility of COGENT's standard propositional buffer type, instances of the type cannot be used to implement a stack. Firstly, only the top-most element may be accessed in a real stack, but a proposition buffer, even with LIFO access, still allows access to all buffer elements. Secondly, elements can be deleted from any position within a propositional buffer, whereas only the top-most element can be deleted from a stack (and this operation is known as a pop). Thirdly, when multiple elements are added to a propositional buffer on the same cycle, COGENT provides no guarantee about the order in which those elements will be added. The stack buffer type provides a storage mechanism that addresses each of these issues.
One common use of stacks is in maintaining sequences of goals and subgoals, as, for example, in models of problem solving.
Like all buffers, stacks can be matched by the conditions of rules. However, a stack match will only match against the top-most element of the stack.
An element may be pushed onto a stack by sending the stack a message of the form "push(Element)". The top-most element may be popped from a stack by sending the stack a message of the form "pop". Thus, the RHS of a rule which pops the top element off Goal Stack and replaces it with subgoal might look like:
|THEN:||send pop to Goal Stack|
|send push(subgoal) to Goal Stack|
If multiple push and/or pop messages are sent to the same stack from the RHS of one rule, then those push/pop messages are processed in the order in which they appear on the RHS of the rule. If multiple push and/or pop messages are sent to the same stack from multiple rules, then messages within the same rule will be processed in order, but the order of processing of messages from different rules is not deterministic.
This use of the ordering of stack related actions within rules differs from standard rule processing. In general, the ordering of rule actions does not affect the order of execution of those actions: non-stack related "send" actions are effectively processed in parallel.
The current contents of stack buffers may be viewed in text mode by opening the current contents tab, or in a graphical mode by opening the current stack tab. In the text mode, the elements in the stack are displayed with the top-most element at the top of the text view. In graphical mode, the stack is displayed pictorially, with an arrow pointing to the top-most element.
If, when printing, the View->Show current contents menu item is checked, the text and graphical views of the current stack state are included in the print out.
Like all COGENT buffers, stacks have an initialisation property which specified when they are initialised (e.g., at the beginning of each trial or block). Stacks typically have an unlimited capacity, but COGENT allows the capacity of a stack to be fixed (using the standard buffer capacity limitation properties). COGENT also allows the specification of actions to be taken when stack capacity is reached. Two further properties specify whether stack elements can decay, and, if so, the rate of that decay. Unlike Propositional buffers, there is no Duplicates property and no Access property.
Decay (possible values: None/Probabilistic/Fixed; default:
This property specifies whether the elements of a buffer decay with time, and if so what pattern of decay is observed. When the value is None, elements will remain in a buffer until they are either explicitly deleted or are forced out by the buffer overflowing. When the value is Probabilistic, elements decay in a random fashion, but with the probability of decay begin constant on each cycle. This probability is specified in terms of a half life (specified by the Decay Constant property). The half life is the number of cycles it takes on average for half of the elements to decay. When the value is Fixed, elements remain in the buffer for a fixed number of cycles (specified by the Decay Constant property).
Decay Constant (possible values: 1 -- 9999; default: 20)
This property determines the decay rate if decay is specified for the buffer. In the case of Probabilistic decay, the constant specifies the half-life (in cycles) of buffer elements. A larger number will result in a longer half-life and so a slower decay. In the case of Fixed decay, the constant specifies the number of cycles an element will remain present in the buffer after the element is added to the buffer (provided it is not ``refreshed'' via Duplicates: No, as discussed above). Again, a larger number will lead to slower decay.
Initialise (possible values: Each Trial/Each Block/Each Subject/Each Session;
default: Each Trial)
The timing of buffer initialisation is determined by this property. When the value is Each Trial, the buffer will automatically initialise itself at the beginning of each trial. When the value is Each Block, the buffer will initialise itself at the beginning of each block of trials (i.e., contents will be preserved across trials within a block). Similarly, when the value is Each Subject, contents will be preserved across simulated blocks and when the value is Each Session, the contents will be preserved across subjects.
Limited Capacity (possible values: No/Yes; default: No)
This property determines whether the buffer has limited or unlimited capacity. If the value is Yes then the buffer's capacity is limited to the value specified by the Capacity property, and its behaviour when that capacity is exceeded is governed by the On Excess property; if the value is No, these two properties are ignored.
Capacity (possible values: 1 -- 9999; default: 7)
This property specifies the capacity of a buffer in terms of the number of items it may hold. If Limited Capacity is not selected, this property has no effect.
On Excess (possible values: Random/Youngest/Oldest/Ignore; default:
The value of this property determines how the buffer behaves when its capacity is exceeded. If the value is Random, then a random element will be deleted from the buffer to make way for the new element. If the value is Youngest, then the most recently added element will be deleted to make way for the new element. If the value is Oldest, then the least recently added element will be deleted to make way for the new element. If the value is Ignore, then the new element will be discarded and the buffer contents will not be altered. If Limited Capacity is not selected, this property has no effect.
Recall that all information must be represented in COGENT via Prolog terms. Buffer elements are no exception, but they are perhaps the simplest sorts of box elements in COGENT. This is reflected in the simplicity of the buffer element editor. Apart from the comment line, it contains a single text field into which the buffer element should be typed. The contents of this field should be a valid Prolog term. If not, however, COGENT does automatic syntax checking (and attempted correction) of editor elements, and so any error will be noted and (possibly) corrected.