For a full overview of Combo as a general programming language, see Combo Programming Language. In the following, the type combo is used to indicate any combinator tree.
The basic OpenCog units are atoms. Every atom has a truth value, and an atom type from a fixed hierarchy of types. The top level of the type hierarchy divides all atoms into nodes or links, with many node and link subtypes (e.g. concept node, inheritance link, etc...). All nodes have a name (string), all links have n-ary outgoing sets of other atoms. Every atom lives in an atom table, a data structure which can be queried to access its atoms. Note that links in one atom table can have atoms from another table in their outgoing sets. Grounded procedure nodes have either combinator trees or C++ code associated with them (see Grounded Procedures).
There are some OpenCog nodes which are created automatically before combo has started.
The following are built-in predicates and schema (see Grounded Procedures for an explanation):
#AND:BuiltInSchemaNode #OR:BuiltInSchemaNode #NOT:BuiltInSchemaNode #ForAll:BuiltInPredicateNode #ThereExists:BuiltInPredicateNode
There are also built-in concept nodes corresponding to all atom types, representing the set of all of the atoms of that type. This allows reflective expressions to be written. These are named like:
#[[InheritanceLink]]:BuiltInConceptNode #SchemaNode:BuiltInConceptNode #ConceptNode:BuiltInConceptNode etc...
Note that "set of all the atoms of that type" includes atoms of subtypes as well. So for example, the BuiltInConceptNode named "ConceptNode" is a member of the set that it represents.
The ProcedureNode hierarchy is as follows:
ProcedureNode +PredicateNode ++GroundedPredicateNode +++BuiltInPredicateNode +++ComboPredicateNode +SchemaNode ++GroundedSchemaNode +++BuiltInSchemaNode +++ComboSchemaNode
That is, BuiltInSchemaNode is a subtype of GroundedSchemaNode, which is a subtype of SchemaNode, etc...
Predicates are functions that may take in arguments, and produce a truth value as output. The semantics are that a predicate node is linked to the its list of argument(s) with an evaluation link, the truth value of which is output of the predicate on those arguments. So for example, we can say, using the shorthands given above:
create_link (atom_type "EvaluationLink") (cons (pred "eats") (cons (list (cons (concept "cat") (cons (concept "fish") nil))) nil)) (simple_tv .99 .99)
Schema are more general than predicates, and can produce any kind of output. Schema nodes are linked to their arguments and outputs via execution links. So for example:
create_link (atom_type "ExecutionLink") (cons (schema "meaning_of") (cons (list (cons (concept "life") nil)) nil)) (simple_tv 1 1)
i.e., meaning_of(life)=42. Note that even though the meaning_of schema is only taking a single argument (life), the argument is still given inside a list link rather than directly, for consistancy.
Grounded procedures are those that are associated with procedural knowledge that allows their outputs to be computed with certainty. Built in procedures have associated C++ code and cannot be modified by Combo. Combo procedures (ComboPredicateNodes and ComboSchemaNodes) have associated combo trees.
Combinator Tree Procedures
Combinator tree procedures can be created with:
NMNode create_predicate [name : string] [tree : combo] [tv : NMTruthValue] NMNode create_schema [name : string] [tree : combo] [tv : NMTruthValue] (use create_predicate_on and create_schema_on to specify an atom table).
Note that the tree argument can be any valid Combo expression. For example:
fuzzy_or(2):=simple_tv (max (strength (get_tv $1)) (strength (get_tv $2))) (max (confidence (get_tv $1)) (confidence (get_tv $2))) create_predicate "fuzzyOr" fuzzy_or (simple_tv .66 1) create_schema "combinatorMashUp" (S B (S S K (I I B S S))) (simple_tv .5 .5)
Creation of a grounded combinator tree procedure is similar to the definition of a function. The differences are that procedure creation can occur within a larger tree, procedures are stored in the OpenCog core, and procedures don't have arities stored with them. Note that, like function definition, the combinator tree argument of a procedure is only evaluated when the procedure is invoked (see below for commands dealing with procedure invocation). This means that, for example:
create_predicate "randomTV" (simple_tv random random) (simple_tv 0.5 1)
stores a predicate that generates a random tv each time. To compute a single random TV and store it, you can say:
randomTV:=(simple_tv random random) create_predicate "randomTV" randomTV (simple_tv 0.5 1)
since evaluation of constants (:=) is eager.
The combinator tree associated with a combinator tree procedure can be accessed via:
combo get_predicate [grounded_predicate : NMNode] combo get_schema [grounded_schema : NMNode] (thows exception if the NMNode is of the wrong type)
and set via:
NMAtom set_predicate [grounded_predicate: NMNode] [tree : combo] NMAtom set_schema [grounded_schema : NMNode] [tree : combo] (thows exception if the NMNode is of the wrong type)
which return the atom they are called with.
Evaluation, Execution, Truth Value Computation, and Queries
What makes the OpenCog core come alive is the evaluation/execution of grounded procedures, the computation of truth values, and the processing of queries. These processed are invoked in Combo with the commands evaluate, execute, compute_tv, and query, described below. When combo is linked to the real core (rather than the pseudo-core), inference may be invoked when truth values are computed and when queries are processed.
evaluate and execute
NMTruthValue evaluate [grounded_predicate : NMAtom] [arg : NMAtom] combo execute [grounded_schema : NMNode] [arg : NMAtom] (throws an exception if called with an NMNode that is not a grounded predicate/schema).
These commands are used to explicitly call for evaluation/execution of a grounded procedure (built-in or combinator tree), on the argument given, and return the result. For example, let's say that foo is a combinator tree schema. Then
execute foo (list (cons arg1 (cons arg2 nil)))
is equivalent to saying
(get_schema foo) arg1 arg2
NMTruthValue compute_tv [atom : NMAtom] NMTruthValue compute_tv [link_type : NMType] [outgoing : list<NMAtom>] NMTruthValue compute_tv_on [table : NMAtomTable] [link_type : NMType] [outgoing : list<NMAtom>])
Computing the truth value of an atom is different from calling get_tv because inference may be invoked, and built-in predicates and schema may be evaluated. In the first version, the atom's actual truth value may be altered. The second and third version allows the truth value of link to be computed without actually creating the link in the core. Note that to compute the truth value of a links which links to other links, the sublinks must be added to the core. Other atoms in the system may have their truth values updated as well as a result of a compute_tv call.
list<NMAtom> query [atom : NMAtom] list<NMAtom> query_on [table : NMAtomTable] [atom : NMAtom] list<list<NMAtom> > multiquery [atom : NMAtom] list<list<NMAtom> > multiquery_on [table : NMAtomTable] [atom : NMAtom]
Queries allow the OpenCog core to be accessed like a database. The query and query_on command recursively searches their argument for occurances of a variable node. The return value is a list giving all valid assignments of these variables that satisfy the query (i.e., strength above a threshold). For queries with multiple variables, the multiquery or multiquery_on command must be used; in this case, the return value is a list of lists of atoms. The first list in the return value contains these varaible nodes. It is followed by lists giving all valid assignments of these variables that satisfy the query.
create_link (atom_type "[[InheritanceLink]]") (cons (concept "moshe") (cons (concept "chicken") nil)) (simple_tv 1 1) create_link (atom_type "[[InheritanceLink]]") (cons (concept "chicken") (cons (concept "animal") nil)) (simple_tv 1 1) create_link (atom_type "[[InheritanceLink]]") (cons (concept "eel") (cons (concept "animal") nil)) (simple_tv 1 1) multiquery (inherits $x $y) -> (($x,$y), (#moshe:ConceptNode, #chicken:ConceptNode), (#chicken:ConceptNode, #animal:ConceptNode), (#eel:ConceptNode, #animal:ConceptNode)) query (inherits $x $x) -> nil query (inherits $x (concept "animal")) -> (#chicken:ConceptNode, #eel:ConceptNode)
Note that in the last case, inference might conclude that since moshe inherits from chicken and chicken inherits from animal, that moshe is also an animal, and return him as well (this won't happen when using the pseudocore).
Syntax and Semantics of built-in Predicates
See ComboSashaShorthands for the correct way to access built-in predicates and schema within Combo and Sasha.
-- Main.MosheLooks - 13 Dec 2004