Combo
From OpenCog
Contents |
Combo Concepts
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.
OpenCog Concepts
The basic OpenCog units are atoms. Every atom has a truth value, and an atom type from a fixed heirarchy of types. The top level of the type heirarchy 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-arry 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).
OpenCog 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 to confidence via the formula:
confidence=count/(count+KKK) (currently KKK=800)
OpenCog Atoms, Truth Values, Atom Types, and Atom Tables in Combo and Sasha
The section describes the interface between Combo/Sasha and the OpenCog core. First of all, we have the following types (within Combo/Sasha):
NMAtom, NMNode, NMLink, NMTruthValue, NMType, NMAtomTable
NMNode and NMLink are subtypes of NMAtom; every NMAtom is either an NMNode or an NMLink. All of the functions decribed below throw exceptions if their type requirements are not met.
The default (currently only) NMAtomTable is accessible with:
AtomTable
NMTypes can be constructed from strings with:
NMType 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 : NMType] [second_type : NMType]
NMTruthValues currently come in only one form, SimpleTV, and are constructed with:
NMTruthValue simple_tv [strength : float] [confidence : float] (note confidence not count!)
NMTruthValue properties can be accessed with:
float strength [tv : NMTruthValue] float confidence [tv : NMTruthValue] nat count [tv : NMTruthValue]
Atoms can be accessed with:
NMNode get_node [type : NMType] [name : string] NMLink get_link [type : NMType] [outgoing : list <NMAtom>]
If the atom doesn't exist, it will be created with a default truth value, which is guarunteed to have zero confidence (and therefore zero count). To query an atom's existance, you can use:
bool exists_node [type : NMType] [name : string] bool exists_link [type : NMType] [outgoing : list <NMAtom>]
Atoms can be created with:
NMNode create_node [type : NMType] [name : string] [tv : NMTruthValue] NMLink create_link [type : NMType] [outgoing : list<NMAtom>] [tv : NMTruthValue]
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.:
NMNode create_node_on [table : NMAtomTable] [type : NMType] [name : string] [tv : NMTruthValue]
Atom properties can be accessed with:
NMAtomTable get_table [atom : NMAtom] NMType get_type [atom : NMAtom] NMTruthValue get_tv [atom : NMAtom] string get_name [node : NMNode] list<NMAtom> get_outgoing [link : NMLink]
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:
NMAtom set_tv [atom : NMAtom] [tv : NMTruthValue] NMAtom set_table [atom : NMAtom] [table : NMAtomTable]
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"
The Combo language (see Combo Programming Language) makes it easy to define shorthands, which are used to illustrate the more complex commands described below.
In output from the Combo interpreter, NMNodes are denoted using the syntax:
#name:type
and NMLinks with the syntax
#type(arg1,arg2,...,argN)
For conciseness, this syntax is used in some of the following exposition
Built-in Nodes
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.
Procedures
The ProcedureNode heirarchy 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)
i.e., eats(cat,fish).
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.
- GroundedProcedures
Grounded Procedures
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.
Combiantor 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 psuedo-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
compute_tv
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.
query
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.
For example:
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

