Combo and OpenCog Prime
Combo takes a fairly simple view of OCP's knowledge representation. The basic OCP 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 target sets.
Grounded procedure nodes have either Combo trees or C++ code associated with them (see GroundedProcedures). The Combo tree associated with a ProcedureNode may carry out basic logical or arithmetic operations, or it may access and manipulate OCP structures using the methods described herein.
OCP truth values have multiple subtypes (e.g., distributional truth values). At a minimum, they contain strength and confidence parameters (two reals in [0,1]). Count, a natural number, is sometimes used, which is related monotonically to confidence.
TODO: Incorporate Atom versions into Combo. -- Ben G, Jan 7 2007 (still not done, July 2008)
OpenCog Prime Atoms, Truth Values, Atom Types, and Atom Tables in Combo and Sasha
OCAtom, OCNode, OCLink, OCTruthValue, OCType, OCAtomTable
OCNode and OCLink are subtypes of OCAtom; every OCAtom is either an OCNode or an OCLink. All of the functions decribed below throw exceptions if their type requirements are not met.
The default OCAtomTable is accessible with:
OCTypes can be constructed from strings with:
OCType atom_type [type_name : string]
(throws an exception if the type isn't known to the core)
The naming convention for atom types is no spaces with the first letter of each word capitalized (JavaStyleClassNames).
Type inheritance can be checked with:
bool inherits_type [first_type : OCType] [second_type : OCType]
OCTruthValues currently come in only one form, SimpleTV, and are constructed with:
OCTruthValue simple_tv [strength : float] [confidence : float]
(note confidence not count!)
OCTruthValue properties can be accessed with:
float strength [tv : OCTruthValue] float confidence [tv : OCTruthValue] nat count [tv : OCTruthValue]
Atoms can be accessed with:
OCNode get_node [type : OCType] [name : string] OCLink get_link [type : OCType] [outgoing : list <OCAtom>]
If the atom doesn't exist, it will be created with a default truth value, which is guaranteed to have zero confidence (and therefore zero count). To query an atom's existance, you can use:
bool exists_node [type : OCType] [name : string] bool exists_link [type : OCType] [outgoing : list <OCAtom>]
Atoms can be created with:
OCNode create_node [type : OCType] [name : string] [tv : OCTruthValue] OCLink create_link [type : OCType] [outgoing : list<OCAtom>] [tv : OCTruthValue]
If the atom already exists, it will be replaced. The create_node command produces an error if you try to use it to create a node with a grounded procedure type or built-in type (GroundedPredicateNode, GroundedSchemaNode, BuiltInConceptNode and all subtypes therof). For creating grounded combinator tree procedures, see Grounded Procedures.
Note that all of these functions operate on the default atom table (AtomTable). There are corresponing functions X_on, which take an atom table as their first argument, e.g.:
OCNode create_node_on [table : OCAtomTable] [type : OCType] [name : string] [tv : OCTruthValue]
Atom properties can be accessed with:
OCAtomTable get_table [atom : OCAtom] OCType get_type [atom : OCAtom] OCTruthValue get_tv [atom : OCAtom] string get_name [node : OCNode] list<OCAtom> get_outgoing [link : OCLink]
Note that get_name can only be called on nodes, and get_outgoing can only be called on links.
Atom properties can be set with:
OCAtom set_tv [atom : OCAtom] [tv : OCTruthValue] OCAtom set_table [atom : OCAtom] [table : OCAtomTable]
which return the atom they are called with.
As a shorthand for accessing variable nodes, $varname can be used, which is equivalent to:
get_node (atom_type "VariableNode") "varname"
In output from the Combo interpreter, OCNodes are denoted using the syntax:
and OCLinks with the syntax
For conciseness, this syntax is used in some of the following exposition
There are some OpenCog Prime 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
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
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)
(Note that, as a syntactic simplification, in Combo we allow e.g.
EvaluationLink eats cat mouse
as a shorthand for
EvaluationLink eats (cat, mouse)
since there is no possibility for ambiguity here.)
Schemata 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:
OCNode create_predicate [name : string] [tree : combo] [tv : OCTruthValue] OCNode create_schema [name : string] [tree : combo] [tv : OCTruthValue]
(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 OCP 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 : OCNode] combo get_schema [grounded_schema : OCNode]
(thows exception if the OCNode is of the wrong type)
and set via:
OCAtom set_predicate [grounded_predicate: OCNode] [tree : combo] OCAtom set_schema [grounded_schema : OCNode] [tree : combo]
(thows exception if the OCNode is of the wrong type)
which return the atom they are called with.
Evaluation, Execution, Truth Value Computation, and Queries
What makes the OCP 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 pseudocore), inference may be invoked when truth values are computed and when queries are processed.
evaluate and execute
OCTruthValue evaluate [grounded_predicate : OCAtom] [arg : OCAtom] combo execute [grounded_schema : OCNode] [arg : OCAtom]
(throws an exception if called with an OCNode 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
OCTruthValue compute_tv [atom : OCAtom] OCTruthValue compute_tv [link_type : OCType] [outgoing : list<OCAtom>] OCTruthValue compute_tv_on [table : OCAtomTable] [link_type : OCType] [outgoing : list<OCAtom>])
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 link 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<OCAtom> query [atom : OCAtom] list<OCAtom> query_on [table : OCAtomTable] [atom : OCAtom] list<list<OCAtom> > multiquery [atom : OCAtom] list<list<OCAtom> > multiquery_on [table : OCAtomTable] [atom : OCAtom]
Queries allow the OCP 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 assigOCents 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 assigOCents 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).
Predefined Truth Values
A few truth values are predefined for convenience; TRUE, FALSE, UNKNOWN. Currently, these are simple truth values, defined as:
TRUE:=simple_tv 1 1 FALSE:=simple_tv 0 1 UNKNOWN:=simple_tv 0 0
The following are used to obtain a node of a particular type from the OCP core. Consistant with the get_node semantics, these functions create the atom if it doesn't already exist. Only the most commonly used node types are represented here; its very simple to create additional shorthands following the same syntax for different types.
//ConceptNodes OCNode CONCEPT [name : string] //WordNodes OCNode WORD [name : string] //PredicateNode (ungrounded) OCNode PRED [name : string] //SchemaNode (ungrounded) OCNode SCHEMA [name : string] //ComboTreePredicateNode OCNode COMBO_PRED [name : string] //ComboSchemaNode OCNode COMBO_SCHEMA [name : string] //BuiltInConceptNode (reflective concepts) OCNode BUILT_IN_CONCEPT [name : string]
As a general rule, the semantics of all links in OCP are binary. The main exceptions are ListLinks, which can have any arity and represent ordered lists of arguments, and ExecutionLinks, which are trinary, and have the form (SchemaNode,ArgumentList,Output). For the others cases, it is convenient to have link accessor functions that take two arguments rather than a list. Again, get_link semantics are used, so the link is created if it doesn't already exist.
Intuitively, we wish to say that Atom A inherits from Atom B, symbolized INH A B to the extent that:
- whatever is generally true of members of B, is also generally true of members of A
- whichever entities are members of A, are also members of B
In terms of subsets, this is
P(x in B || x in A)
Similarity is the symmetrical analogue of inheritance,
P(x in A and x in B || x in A or x in B)
Implication and Equivalence
Logical implication and equivalence.
Membership and Subset
Set-theoretic membership (member argument comes first, then set argument), and subset.
Evaluation and Execution Output (ExOut)
Find the truth value of a predicate on an argument, or the output of a schema on an argument.
OCLink INH [a : OCAtom] [b : OCAtom] OCLink SIM [a : OCAtom] [b : OCAtom] OCLink IMPL [a : OCAtom] [b : OCAtom] OCLink EQUIV [a : OCAtom] [b : OCAtom] OCLink MEMBER [a : OCAtom] [b : OCAtom] OCLink SUBSET [a : OCAtom] [b : OCAtom] OCLink EVAL [a : OCAtom] [b : OCAtom] OCLink EXOUT [a : OCAtom] [b : OCAtom]
OCLink LIST [l : list<OCAtom>]
creates a list link with the relevant atoms.
Predicates and Schema
Shorthands for applying predicates and schema are:
OCAtom Predicate [N : nat] [pred : OCAtom] [arg1 : OCAtom] ... [argN : OCAtom] OCAtom Schema [N : nat] [schema : OCAtom] [arg1 : OCAtom] ... [argN : OCAtom]
compute_tv (Predicate 1 (PRED "isChicken") (CONCEPT "moshe"))
to compute the extent to which moshe is a chicken.
The built-in schema AND,OR,NOT can be used with the shorthands
OCAtom AND [a : OCAtom] [b : OCAtom] OCAtom OR [a : OCAtom] [b : OCAtom] OCAtom NOT [a : OCAtom]
so for example, you can say
compute_tv (AND (CONCEPT "moshe") (CONCEPT "ben"))
to compute the truth value of "moshe and ben".
The built-in predicates FOR_ALL,THERE_EXISTS can be used with the shorthands
OCAtom FOR_ALL [variable : OCAtom] [expr : OCAtom] OCAtom THERE_EXISTS [variable : OCAtom] [expr : OCatom]
(variable must be a VariableNode, expr can be a variable-laden expression)
for example, you can say
compute_tv (FOR_ALL $x (IMPL (Predicate 1 (PRED "isChicken") $x) (Predicate 1 (PRED "isSilly") $x))
to compute the extent to which all chickens are silly.