From OpenCog

Denoting Functions and Predicates

SchemaNodes and PredicateNodes contain functions internally; and Links may also usefully be considered as functions. We now briefly discuss the notations we will use to denote functions in various contexts.

Firstly, we will make some use of the currying notation drawn from combinatory logic, in which adjacency indicates function application. So, for instance, using currying,

f x

means the function f evaluated at the argument x; and (f x y) means (f(x))(y). If we want to specify explicitly that a block of terminology is being specified using currying we will use the notation @[<expression>]; for instance

@[f x y z]



We will also frequently use conventional notation to refer to functions, such as f(x,y). Of course, this is consistent with the currying convention if (x,y) is interpreted as a list and f is then a function that acts on 2-element lists. We will have many other occasions than this to use list notation.

Also, we will sometimes use a non-curried notation, most commonly with Links, so that e.g.

InheritanceLink x y

does not mean a curried evaluation but rather means InheritanceLink(x,y). If we want to explicitly specify that an expression is non-curried, we will use the notation ![ expression ].

Execution Output Links

In the case where f refers to a schema, the occurrence of the combination f x in the system is represented by

ExOutLink f x

or graphically

    /   \ 
   f     x

Note that, just as when we write

f (g x)

we mean to apply f to the result of applying g to x, similarly when we write

ExOutLink f (ExOutLink g x)

we mean the same thing.

So for instance

EvaluationLink (ExOutLink g x) y <.8>

means that the result of applying g to x is a predicate r, so that r(y) evaluates to True with strength .8.

This approach, in its purest incarnation, does not allow multi-argument schemata. Now, multi-argument schemata are never actually necessary, because one can use argument currying to simulate multiple arguments. However, this is often awkward, and things become simpler if one introduces an explicit tupling operator, which we call ListLink. Simply enough,

ListLink A1 … An

denotes an ordered list (A1 ,… ,An)

Execution Links

ExecutionLinks give the system an easy way to record acts of schema execution. These are ternary links of the form:

SchemaNode: S

Atom: A, B

ExecutionLink S A B

In words, this says the procedure represented by SchemaNode S has taken input A and produced output B.

There may also be schemata that do not take output, or do not take input. But these are treated as PredicateNodes, to be discussed below; their activity is recorded by EvaluationLinks, not ExecutionLinks.

The TruthValue of an ExecutionLink records how frequently the result encoded in the ExecutionLink occurs. Specifically,

  • the TruthValue of (ExecutionLink S A B) tells you the probability of getting B as output, given that you have run schema S on input A
  • the TruthValue of (ExecutionLink S A) tells you the probability that if S is run, it is run on input A

Often it is useful to record the time at which a given act of schema execution was carried out; in that case one uses the atTime schema, writing e.g.

    ExecutionLink S A B

where T is a TimeNode, or else one uses an implicit method such as storing the time-stamp of the ExecutionLink in a core-level data-structure called the TimeServer. The implicit method is logically equivalent to explicitly using atTime, and is treated the same way by PLN inference, but provides significant advantages in terms of memory usage and lookup speed.

For purposes of logically reasoning about schema, it is useful to create binary links representing ExecutionLinks with some of their arguments fixed. We name these as follows:

ExecutionLink1 A B means: X so that ExecutionLink X A B 

ExecutionLink2 A B means: X so that ExecutionLink A X B 

ExecutionLink3 A B means: X so that ExecutionLink A B X 

Finally, a SchemaNode may be associated with a structure called a Graph.

Where S is a SchemaNode,

Graph(S) = { (x,y): ExecutionLink S x y }

Sometimes, the graph of a SchemaNode may be explicitly embodied as a ConceptNode; other times, it may be constructed implicitly by a MindAgent in analyzing the SchemaNode (e.g. the inference MindAgent).

Note that the set of ExecutionLinks describing a SchemaNode may not define that SchemaNode exactly, because some of them may be derived by inference. This means that the model of a SchemaNode contained in its ExecutionLinks may not actually be a mathematical function, in the sense of assigning only one output to each input. One may have

ExecutionLink S X A <.5>

ExecutionLink S X B <.5>

meaning that the system does not know whether S(X) evaluates to A or to B. So the set of ExecutionLinks modeling a SchemaNode may constitute a non-function relation, even if the schema inside the SchemaNode is a relation.

Finally, what of the case where f x represents the action of a built-in system function f on an argument x? This is an awkward case in that it will not be necessary once the OCP system is revised so that all cognitive functions are carried out using SchemaNodes. However, in OCP 0.5 where most cognitive functions are carried out using C++ MindAgent objects, if we want OCP to study its own cognitive behavior in a statistical way, we need BuiltInSchemaNodes that refer to MindAgents rather than to ComboTrees (or else, we need to represent MindAgents using ComboTrees, which will become practicable once we have a sufficiently efficient Combo interpreter). The semantics here is thus basically the same as where f refers to a schema. For instance we might have

ExecutionLink FirstOrderInferenceMindAgent (L1, L2) L3

where L1, L2 and L3 are links related by


according to the first-order PLN deduction rule.


Predicates are related but not identical to schema, both conceptually and notationally. PredicateNodes involve predicate schema which output TruthValue objects. But there is a difference between a SchemaNode embodying a predicate schema and a PredicateNode, which is that a PredicateNode doesn't output a TruthValue, it adjusts its own TruthValue as a result of the output of its own internal predicate schema.

The record of the activity of a PredicateNode is given not by an ExecutionLink but rather by an:

EvaluationLink P A <t>

where P is a PredicateNode, A is its input, and <tv> is the truth value assumed by the EvaluationLink corresponding to the PredicateNode being fed the input A. There is also the variant

EvaluationLink P <tv>

for the case where the PredicateNode P embodies a schema that takes no inputs.

A simple example of a PredicateNode is the predicate GreaterThan. In this case we have, for instance

EvaluationLink GreaterThan 5 6 <0>

EvaluationLink GreaterThan 5 3 <1>

and we also have:


We will also encounter many commonsense-semantics predicates such as isMale, with e.g.

EvaluationLink isMale Ben_Goertzel <1>

Schemata that return no outputs are treated as predicates, and handled using EvaluationLinks. The truth value of such a predicate, as a default, is considered as True if execution is successful, and False otherwise.

And, analogously to the Graph operator for SchemaNodes, we have for PredicateNodes the SatisfyingSet operator, defined so that the SatisfyingSet of a predicate is the set whose members are the elements that satisfy the predicate. Formally, that is:

S = SatisfyingSet P


TruthValue ( MemberLink X S )


TruthValue ( EvaluationLink P X)

This operator allows the system to carry out advanced logical operations like higher-order inference and unification.

Denoting Schema and Predicate Variables

OCP sometimes uses variables to represent the expressions inside schemata and predicates, and sometimes uses variable-free, combinatory-logic-based representations. There are two sorts of variables in the system, either of which may exist either inside compound schema or predicates, or else in the AtomSpace as VariableNodes:

It is important to distinguish between two sorts of variables that may exist in OCP:

  • Variable Atoms, which may be quantified (bound to existential or universal quantifiers) or unquantified
  • Variables that are used solely as function-arguments or local variables inside the "Combo tree" structures used inside some ProcedureNodes (PredicateNodes or SchemaNodes) (to be described below), but are not related to Variable Atoms

Examples of quantified variables represented by Variable Atoms are $X and $Y in:

ForAll $X <.0001>


        ExtensionalInheritanceLink $X human

        ThereExists $Y


                ExtensionalInheritanceLink $Y human

                EvaluationLink parent_of ($X, $Y)

An example of an unquantified Variable Atom is $X in

ExtensionalImplicationLink <.3>

    ExtensionalInheritanceLink $X human

    ThereExists $Y


            ExtensionalInheritanceLink $Y human

            EvaluationLink parent_of ($X, $Y)

This ImplicationLink says that 30% of humans are parents: a more useful statement than the ForAll Link given above, which says that it is very very unlikely to be true that all humans are parents.

We may also say, for instance,

SatisfyingSet( EvaluationLink eats (cat, $X)

to refer to the set of X so that eats(cat, X).

On the other hand, suppose we have the implication


    Evaluation f $X



        ExOut reverse $X

where f is a PredicateNode embodying a mathematical operator acting on pairs of NumberNodes, and reverse is an operator that reverses a list. So, this implication says that the f predicate is commutative. Now, suppose that f is grounded by the formula

f(a,b) = (a > b — 1)

embodied in a Combo Tree object, stored in the ProcedureRepository and linked to the PredicateNode for f. These f-internal variables, which I have written here using the letters a and b, are not VariableNodes in the OCP AtomTable. The notation we use for these within the textual Combo language, that goes with the Combo Tree formalism, is to replace a and b in this example with #1 and #2, so the above grounding would be denoted

f —> (#1 > #2 — 1)

Input and Output Type Restrictions

Each ProcedureNode (SchemaNode or PredicateNode) has certain input type restrictions, and certain output type restrictions. In other words, a schema doesn't usually act on a general Atom, but will often only meaningfully act on a particular type of Atom. And a schema doesn't usually output a general Atom, but will often only output a particular type of Atom it must come with.

This notion of type restriction may be formalized by considering each Procedure/InstanceNode to be associated with:

  • An input type restriction ITR
  • An output type restriction OTR

These type restrictions ITR and OTR are themselves predicates. It is unnecessary for these to be Atoms (in fact this would lead to an infinite regress), but there should be special schemata



that produce schema embodying the ITR and OTR (respectively) of their argument SchemaNodes. The ITR and OTR can be expanded into PredicateNodes in cases where this is appropriate.

When necessary, type restrictions may be represented as relationships of the form

Type T

where T is a ConceptNode representing an Atom type. Here, Type is a predicate so that Type T is itself a PredicateNode, and the semantics is

EvaluationLink ((Type T) x) iff x is of type T

There may also be more subtle type restrictions. For instance, what if the input type of X is the set of SchemaNodes with input type Y and output type Z? Then

    EquivalenceLink (ITR(W)) Y
    EquivalenceLink (OTR(W)) Z

A ProcedureNode X can only be involved in a relationship

ExecutionOutputLink X Y

with an Procedure/InstanceNode Y if

OTR(X) W = True implies ITR(Y) W = True

or, put more simply, if

OTR(X) implies ITR(Y)

A non-ProcedureNode Z can only have an ExOutLink pointing to a ProcedureNode X if

ITR(X) Z equals True

These conditions must be checked whenever ExOutLinks are created.

In the current OCP version, it is assumed that type restrictions are always crisp, not probabilistically truth-valued. This assumption may be revisited in a later version of the system.

Links as Predicates

It is conceptually important to recognize that OCP link types may be interpreted as predicates. For instance, when one says

InheritanceLink cat animal <.8>

indicating an Inheritance relation between cat and animal with a strength .8, effectively one is declaring that one has a predicate giving an output of .8. Depending on the interpretation of InheritanceLink as a predicate, one has either the predicate

InheritanceLink cat $X

acting on the input


or the predicate

InheritanceLink #1 animal

acting on the input


or the predicate

InheritanceLink #1 #2

acting on the list input

(cat, animal)

This means that, if we wanted to, we could do away with all Link types except OrderedLink and UnorderedLink, and represent all other Link types as PredicateNodes embodying appropriate predicate schema.

This is not the approach taken in the current codebase. However, the situation is somewhat similar to that with CIM-Dynamics:

  • In future we will create a revision of OCP that regularly revises its own vocabulary of Link types, in which case an explicit representation of link types as predicate schema will be appropriate.
  • In the shorter term, it can be useful to treat link types as virtual predicates, meaning that one lets the system create SchemaNodes corresponding to them, and hence do some meta level reasoning about its own link types.

Representation of Cognitive Processes as Schemata

OCP's CIM-Dynamics provide it with an initial set of cognitive tools, with which it can learn how to interact in the world. One of the jobs of this initial set of cognitive tools, however, is to create better cognitive tools. Initially, this takes the form of cognitive schema learning — the learning of schemata carrying out cognitive processes in more specialized, context-dependent ways than the general CIM-Dynamics do. Eventually, these cognitive schema will rival the CIM-Dynamics in complexity, and will replace the CIM-Dynamics altogether, leaving the system to operate almost entirely based on cognitive schemata. This long-term vision of OCP as a self-modifying, self-programming system will be revisited later on.

In order to make the process of cognitive schema learning easier, we provide a number of elementary schemata embodying the basic cognitive processes contained in the CIM-Dynamics. Of course, cognitive schemata need not use these — they may embody entirely different cognitive processes than the CIM-Dynamics. Eventually, we want the system to discover better ways of doing things than anything even hinted at by its initial CIM-Dynamics. But for the initial phases or the system's schema learning, it will have a much easier time learning to use the basic cognitive operations as the initial CIM-Dynamics, rather than inventing new ways of thinking from scratch!

For instance, we provide a number of elementary schemata corresponding to inference operations, such as

Schema: Deduction
    Input InheritanceLink: X, Y
    Output InheritanceLink

The inference CIM-Dynamics apply this rule in certain ways, designed to be reasonably effective in a variety of situations. But there are certainly other ways of using the deduction rule, outside of the basic control strategies embodied in the inference CIM-Dynamics. By learning schemata involving the Deduction schema, the system can learn special, context-specific rules for combining deduction with concept-formation, association-formation and other cognitive processes. And as it gets smarter, it can then take these schemata involving the Deduction schema, and replace it with a new schema that eg. contains a context-appropriate deduction formula.

Eventually, to support cognitive schema learning, we will want to cast the hard-wired CIM-Dynamics as cognitive schemata, so the system can see what is going on inside them. Pragmatically, what this requires is coding versions of the CIM-Dynamics in Sasha rather than C++, to facilitate the studying of the system, even if the C++ versions are the ones actually executed until the system's self-modificatory learning has proceeded so far that it has rendered its original CIM-Dynamics useless.

But even prior to this kind of fully cognitively transparent implementation, the system can still reason about its use of different mind dynamics by considering each CIM-Dynamic as a virtual Procedure with a real SchemaNode attached to it. This can lead to some valuable learning, with the obvious limitation that in this approach the system is thinking about its CIM-Dynamics as black boxes rather than being equipped with full knowledge of their internals.