OpenCog

Scheme

From OpenCog

This page describes the Scheme extensions for OpenCog. Scheme is a dialect of Lisp; the scheme shell allows scheme code to manipulate the contents of an OpenCog AtomSpace. See the cookbook for a quick overview; this page provides a detailed command reference.

This binding is intended to be used for day-to-day maintenance activities, experimentation and MindAgent development. The shell is implemented in guile; the guile reference manual is here. The best book on the Scheme programming language (never-mind the language, it's one of the best books written on programming, no matter what language you like to use) is "Structure and Interpretation of Computer Programs" by Abelson and Sussman. It is now available online for free from MIT Press.

Documented below are the core interface functions implemented by the guile binding -- these provide for the creation and deletion of OpenCog Nodes, Links, TruthValues and VersionHandles, as well as ways to get properties (such as the names of Nodes, or the outgoing sets of Links). Although the core interface is documented below, it is important to notice that scheme makes "syntactic sugar" very easy to use; thus the interface below can be simplified, and has been simplified (in the file src/scm/type_constructors.scm, which is automatically loaded by lib/opencog.conf on server startup). Thus, atom creation can be as simple as the following example:

  (ReferenceLink (stv 0.987 0.234)
      (ConceptNode "dog-instance-1") (WordNode "dog"))

which creates a link connecting a concept node and a word node, having a simple truth value. Any valid node or link name can be used in this way.

A number of utilities and handy functions can be found in the directory opencog/scm; additional NLP-related utilities and MindAgents are located in opencog/nlp/scm and other nlp subdirectories.

Contents

Running

The shell may be invoked by typing "scm" at the opencog prompt; however, the shell is available only if 1) opencog has been compiled with guile support, and 2) the scheme shell module is loaded! To load the module, start the opencog server as follows:

 bin/opencog/server/cogserver -c lib/opencog.conf

The "opencog.conf" file specifies a bunch of defaults for the opencog server, including modules that need to be loaded. If the module has been loaded, then typing "help" at the opencog prompt should list "scm" as one of the possible commands.

To get to the opencog shell, say

  telnet localhost 17001

To leave the shell, place a single . on the line, and hit enter. Alternately, hit ^D.

Core Functions

The following are the "core" functions, implemented in the guile-to-C++ interface.

cog-new-node node-type node-name

Create a new node of the given type and name

Optionally, a truth value can follow the node name. Throws errors if node-type is not a valid atom type for a node, and if node-name is not a string.

Example:

         ; Create a new node, and prints its value:
         guile> (cog-new-node 'ConceptNode "some node name")
         #<node[3:some node name] sti:(0,0) tv:(0.000000,0.000000)>

         ; Creates a new node, with a truth value:
         guile> (cog-new-node 'ConceptNode "another node"
                       (cog-new-stv 0.8 0.9))
         #<node[3:another node] sti:(0,0) tv:(0.800000,0.900000)>

cog-node node-type node-name

Returns the node of the given type and name, if it exists, else returns null.

Optionally, a truth value can follow the node name. If the node exists, then the truth value is modified. Throws errors if node-type is not a valid atom type for a node, and if node-name is not a string.

Example:

         ; Check to see if a node exists: 
         guile> (cog-node 'ConceptNode "asdf")
         ()

         ; Verify that the return value is actually a true null:
         guile> (null? (cog-node 'ConceptNode "asdf"))
         #t 

         ; Now, create the node, and see if it exists:
         guile> (cog-new-node 'ConceptNode "asdf")
         #<node[3:asdf] sti:(0,0) tv:(0.000000,0.000000)>
         guile> (null? (cog-node 'ConceptNode "asdf"))
         #f

         ; Change the truth value of an existing node:
         guile> (cog-node 'ConceptNode "asdf" (cog-new-stv 0.8 0.9))
         #<node[3:some node name] sti:(0,0) tv:(0.800000,0.900000)>


cog-new-link link-type atom ... atom

Create a new link, with the given atoms in the link.

Optionally, a truth value can follow the list of atoms. Throws errors if the link type is not a valid opencog link type, or if any of the arguments after the link type are not atoms.

Example:

   ; Creates two nodes, and a new link: 
   guile> (define x (cog-new-node 'ConceptNode "abc")) 
   guile> (define y (cog-new-node 'ConceptNode "def")) 
   guile> (cog-new-link 'Link x y) 
   #<link[2 sti:(0,0) tv:(0.000000,0.000000) <[3 abc],[3 def]>]> 

   ; Create a new link with a truth value: 
   guile> (cog-new-link 'Link x y (cog-new-stv 0.7 0.8)) 
   #<link[2 sti:(0,0) tv:(0.700000,0.800000) <[3 abc],[3 def]>]> 

cog-link link-type atom ... atom

Returns the link of the given type and list of atoms, if it exists, else returns null.

Optionally, a truth value can follow the list of atoms. If the link exists, then the truth value is modified.

Throws errors if the link type is not a valid opencog link type, or if any of the arguments after the link type are not atoms.

Example:

   ; Create two nodes: 
   guile> (define x (cog-new-node 'ConceptNode "abc")) 
   guile> (define y (cog-new-node 'ConceptNode "def")) 

   ; Does a link with these two nodes exist? 
   guile> (cog-link 'Link x y) 
   () 

   ; Now, create such a link 
   guile> (cog-new-link 'Link x y) 
   #<link[2 sti:(0,0) tv:(0.000000,0.000000) <[3 abc],[3 def]>]> 

   ; Check again for existence: 
   guile> (cog-link 'Link x y) 
   #<link[2 sti:(0,0) tv:(0.000000,0.000000) <[3 abc],[3 def]>]> 

   ; Change the truth value of an existing node: 
   guile> (cog-link 'Link x y (cog-new-stv 0.7 0.8)) 
   #<link[2 sti:(0,0) tv:(0.700000,0.800000) <[3 abc],[3 def]>]>

cog-delete atom

Delete the indicated atom, but only if it has no incoming links.

cog-delete-recursive atom

Delete the incoming atom, and all atoms that point at it.

Both functions return #t on success, else they return #f. If #f is returned, then the delete failed.

Example:

  ; Define two nodes and a link between them: 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (define y (cog-new-node 'ConceptNode "def")) 
  guile> (define l (cog-new-link 'Link x y)) 

  ; Verify that there's an atom called x: 
  guile> x 
  #<node[3:abc] sti:(0,0) tv:(0.000000,0.000000)> 

  ; Try to delete x. This should fail, since there's a link 
  ; containing x.  
  guile> (cog-delete x) 
  #f 

  ; Delete x, and everything pointing to it. This should delete 
  ; both x, and the link l. 
  guile> (cog-delete-recursive x) 
  #t 

  ; Verify that the link l is gone: 
  guile> l 
  Invalid handle 

  ; Verify that the node x is gone: 
  guile> x 
  Invalid handle 

  ; Verify that the node y still exists: 
  guile> y 
  #<node[3:def] sti:(0,0) tv:(0.000000,0.000000)> 


cog-atom? exp

Return #t if exp is an atom, else return #f

Example:

  ; Define a node 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (define y (+ 2 2)) 
  guile> (cog-atom? x) 
  #t 
  guile> (cog-atom? y) 
  #f 


cog-name atom

Return the name of the node. If the atom is not a node, returns NIL.

Example:

  ; Define a node 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (cog-name x) 
  "abc" 


cog-type atom

Return the type of the atom.

Example:

  ; Define a node 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (cog-type x) 
  ConceptNode 
  guile> (eq? 'ConceptNode (cog-type x)) 
  #t 

cog-arity atom

Return the arity of the atom.

Example:

  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (cog-arity x) 
  0 
  guile> (define l (cog-new-link 'Link x x x)) 
  guile> (cog-arity l) 
  3 


cog-incoming-set atom

cog-outgoing-set atom

Return the incoming and outgoing sets, respectively, of the atom. These sets are returned as ordinary scheme lists.

Example:

  ; Define two nodes and a link between them: 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (define y (cog-new-node 'ConceptNode "def")) 
  guile> (define l (cog-new-link 'Link x y)) 

  ; Get the incoming sets of nodes x and y (which is the link l): 
  guile> (cog-incoming-set x) 
  (#<link[2 sti:(0,0) tv:(0.000000,0.000000) <[3 abc],[3 def]>]>) 
  guile> (cog-incoming-set y) 
  (#<link[2 sti:(0,0) tv:(0.000000,0.000000) <[3 abc],[3 def]>]>) 

  ; Verify that the both incoming sets are really one and the 
  ; same link: 
  guile> (equal? (cog-incoming-set x) (cog-incoming-set y)) 
  #t 

  ; The returned values are lists, and not singleton atoms. 
  ; Thus, the incoming set of x is a list containing l: 
  guile> (equal? (cog-incoming-set x) (list l)) 
  #t 

  ; Verify that the returned value is a true list: 
  guile> (list? (cog-incoming-set x)) 
  #t 


cog-atom handle

Reference the atom identified by the integer-valued handle

cog-handle atom

Return the handle (which is an integer) of the atom

It may be useful to remember that scheme indicates hexadecimal numbers by preceding them with #x, and so, for example, (cog-atom #x2c949b) gets the handle associated with hex 2c949b.

Example:

  ; Create two atoms, and get their handles: 
  guile> (define x (cog-new-node 'ConceptNode "abc")) 
  guile> (define y (cog-new-node 'ConceptNode "def")) 
  guile> (cog-handle x) 
  113 
  guile> (cog-handle y) 
  114 

  ; Get the atom corresponding to handle number 114 
  guile> (cog-atom 114) 
  #<node[3:def] sti:(0,0) tv:(0.000000,0.000000)> 

  ; Verify that handles are truly integers 
  guile> (integer? x) 
  #f 
  guile> (integer? (cog-handle x)) 
  #t 


cog-new-stv mean confidence

Create a SimpleTruthValue with the given mean and confidence. Unlike atoms, truth values are ephemeral: they are automatically garbage-collected when no longer needed.

Throws errors if mean and confidence are not floating-point values. Example:

   ; Create a new simple truth value: 
   guile> (cog-new-stv 0.7 0.9) 

cog-new-ctv mean confidence count

Create a CountTruthValue with the given mean, confidence and count. Unlike atoms, truth values are ephemeral: they are automatically garbage-collected when no longer needed.

Throws errors if mean, confidence and count are not floating-point values. Example:

   ; Create a new count truth value: 
   guile> (cog-new-ctv 0.7 0.9 44.0) 

cog-new-itv lower upper confidence

Create an IndefiniteTruthValue with the given lower, upper and confidence. Unlike atoms, truth values are ephemeral: they are automatically garbage-collected when no longer needed.

Throws errors if lower, upper and confidence are not floating-point values. Example:

   ; Create a new indefinite truth value: 
   guile> (cog-new-itv 0.7 0.9 0.6) 

cog-tv? exp

Return #t if exp is a truth value, else return #f

Example:

  ; Define a node 
  guile> (define x (cog-new-stv 0.7 0.9)) 
  guile> (define y (+ 2 2)) 
  guile> (cog-tv? x) 
  #t 
  guile> (cog-tv? y) 
  #f

cog-stv? exp

Return #t if exp is a SimpleTruthValue, else return #f

cog-ctv? exp

Return #t if exp is a CountTruthValue, else return #f

cog-itv? exp

Return #t if exp is a IndefiniteTruthValue, else return #f

cog-tv atom

Return the truth-value of the atom.

Example:

  ; Define a node 
  guile> (define x  
            (cog-new-node 'ConceptNode "abc"  
               (cog-new-stv 0.2 0.5))) 
  guile> (cog-tv x) 
  (stv 0.2 0.5) 
  guile> (cog-tv? (cog-tv x)) 
  #t

cog-set-tv! atom tv

Set the truth-value of the atom.

Example:

  ; Define a node
  guile> (define x (cog-new-node 'ConceptNode "def"))
  guile> (cog-tv x)
  (stv 0 0)
  guile> (cog-set-tv! x (cog-new-stv 0.9 0.8))
  (ConceptNode "def" (stv 0.9 0.8))
  guile> (cog-tv x)
  (stv 0.9 0.8)

cog-tv->alist

Convert a truth value to an association list (alist).

Example:

  guile> (define x (cog-new-stv 0.7 0.9)) 
  guile> (cog-tv->alist x) 
  ((mean . 0.699999988079071) (confidence . 0.899999976158142))

cog-new-av sti lti vlti

Create an AttentionValue with the given STI, LTI and VLTI (#t or #f). Unlike atoms, attention values are ephemeral: they are automatically garbage-collected when no longer needed.

Example:

   ; Create a new attention value:
   guile> (cog-new-av 10 20 #t)

cog-av? exp

Return #t if exp is an attention value, else return #f

Example:

  ; Define a simple attention value
  guile> (define x (cog-new-av 15 25 #f))
  guile> (define y (+ 2 2))
  guile> (cog-av? x)
  #t
  guile> (cog-av? y)
  #f

cog-av atom

Return the attention value of the atom.

Example:

  ; Define a node
  guile> (define x 
            (cog-new-node 'ConceptNode "abc" 
               (cog-new-av 11 21 #f)))
  guile> (cog-av x)
  (av 11 21 #f)
  guile> (cog-av? (cog-av x))
  #t

cog-set-av! atom av

Set the attention value of the atom.

Example:

  ; Define a node
  guile> (define x (cog-new-node 'ConceptNode "def"))
  guile> (cog-av x)
  (av 0 0 #f)
  guile> (cog-set-av! x (cog-new-av 44 55 #f))
  (ConceptNode "def" (av 44 55 #f))
  guile> (cog-av x)
  (av 44 55 #f)

cog-av->alist

Convert an attention value to an association list (alist).

Example:

  guile> (define x (cog-new-av 99 88 #f))
  guile> (cog-av->alist x)
  ((sti . 99) (lti . 88) (vlti . #f))

cog-new-vh indicator atom

Create a VersionHandle of the indicated type, referring to the indicated atom. Possible indicator types are: "HYPOTHETICAL","CONTEXTUAL" and "UNKNOWN".

Throws errors if the type or handle is invalid.

Example:

        ; Define a node
        guile> (define n (cog-new-node 'ConceptNode "abc"))
        ; Define a version handle holding that node
        guile> (cog-new-vh "HYPOTHETICAL" n)

cog-vh? exp

Return #t if exp is a version handle, else return #f

Example:

        ; Define a node
        guile> (define n (cog-new-node 'ConceptNode "abc"))
        ; Define a version handle holding that node
        guile> (define x (cog-new-vh "HYPOTHETICAL" n))
        guile> (define y (+ 2 2))
        guile> (cog-vh? x)
        #t
        guile> (cog-tv? y)
        #f

cog-vh->alist

Convert a version handle to an association list (alist). This allows scheme code to access the two parts of the version handle by name, the two parts being the handle and the version indicator.

Example:

        ; Define a node
        guile> (define n (cog-new-node 'ConceptNode "abc"))
        ; Define a version handle holding that node
        guile> (define x (cog-new-vh "HYPOTHETICAL" n))
        guile> (cog-vh->alist x)
        ((indicator . HYPOTHETICAL) (atom . (ConceptNode "abc")))

cog-get-types

Return a list of all of the atom types in the system.

Example:

       guile> (cog-get-types)

cog-type?

Return #t if the symbol names an atom type, else return #f

Example:

   guile> (cog-type? 'ConceptNode)
   #t
   guile> (cog-type? 'FlorgleBarf)
   #f

cog-get-subtypes type

Return a list of the subtypes of the given type. Only the immediate subtypes are returned; to obtain all subtypes, this function should be called recursively.

Example:

   guile> (cog-get-subtypes 'Atom)
   (Link Node)

cog-subtype? type subtype

Return #t if 'subtype' is a subtype of 'type', else return #f. The check is performed recursively.

Example:

   guile> (cog-subtype? 'Node 'Link)
   #f
   guile> (cog-subtype? 'Atom 'Link)
   #t
   guile> (cog-subtype? 'Atom 'ConceptNode)
   #t

cog-map-type proc type

Call procedure proc for each atom in the atomspace that is of type type. If proc returns any value other than #f, then the iteration is terminated.

Example:

  ; define a function that prints the atoms: 
  guile> (define (prt-atom h) (display h) #f) 
  guile> (cog-map-type prt-atom 'ConceptNode) 

cog-ad-hoc command parameters

The cog-ad-hoc command is actually a command dispatcher for a grab-bag of partial, incomplete, poorly-thought-out or development interfaces. Its an easy way to attach new C++ code while pondering a more appropriate long-term solution.

Currently-implemented commands are:

  • do-wsd -- run word-sense disambiguation
  • do-implication handle -- run forward-chainer on handle, which must be an ImplicationLink.
  • store-atom handle -- store indicated atom to SQL/persistent storage.
  • fetch-atom handle -- fetch indicated atom from SQL/persistent storage.

Notes, examples, utilities

Various related notes.

Load scheme data from a file

There are two ways to load scheme from a file: automatically, at server startup, or manually.

To load automatically, edit opencog.conf, and append the filename to the line SCM_PRELOAD, for example:

  SCM_PRELOAD = scm/type_constructors.scm, scm/utilities.scm

The cogserver is then started with the -c flag:

  cogserver -c ../lib/opencog.conf

To manually load scheme code, simply make sure the first 3 characters of the file are "scm" followed by a carriage return. Then, at the shell prompt, simply execute the command:

  cat filename | telnet localhost 17001 

This assumes the scheme code is in "filename", that the cogserver is running on "localhost", and is listening on port 17001 (which is the default port for the cogserver).

Utilities

A number of handy utilities can be found in opencog/scm/utilities.scm as well as NLP-specific utilities in opencog/nlp/scm/nlp-utils.scm. These include small, simple routines for following a link from one atom type to another, finding all EvaluationLinks having some given form, filtering lists based on atom types, and so on.

Delete a list of atoms

Below is a simple example of scheme code for deleting all of the atoms in a list.

 (define (killall lst) 
    (if (null? lst) 
        '() 
        (cons (cog-delete (car lst)) 
              (killall (cdr lst)))))

Count the total number of atoms in the atomspace

Here is a simple example showing how to count the total number of atoms in the atomspace.

  (define (cnt-all)
     (define cnt 0)
     (define (ink a) (set! cnt (+ cnt 1)) #f)
     (define (cnt-type x) (cog-map-type ink x)) 
     (map cnt-type (cog-get-types))
     cnt
  )

Calling scheme from C++

Scheme code can be called from C++. The most common case is handled below: calling a scheme function which expects 0,1,2 or more Handle arguments, and returns a Handle. So, for example, assume you have a scheme function called do-stuff as below:

 (define (do-stuff handle1 handle2) (... return some-hand ...))

Then you can call it from C++ as follows:

 // Create a LIST_LINK on stack, holding the input arguments.
 Handle arg1 = ...;
 Handle arg2 = ...;
 Link lnk(LIST_LINK, arg1, arg2;...);    // constructor runs on stack
 Handle hlnk = TLB::getHandle(&lnk);     // handle of the list link
 
 // Call the "do-stuff" function
 SchemeEval evaluator;
 Handle answer = evaluator.apply("do-stuff", hlnk);

If do-stuff doesn't take any arguments, then set hlink to Handle::UNDEFINED, or pass an empty ListLink.

The above form is used by the ExecutionLink to embed scheme callouts into hypergraphs.

Calling C++ from scheme

There is no generic way of calling some arbitrary C++ method from scheme; to enable this, new, explicit C++ code must be written. There are two ways to do this: create a formal API, or provide an ad-hoc extension. Creating a formal API without some general experimentation and experience is discouraged -- it takes more work, its more complex, and the first stab at it will likely need changes. Instead, try using the 'ad-hoc' mechanism.

Calls to miscellaneous C++ functions can be implemented by modifying the opencog/guile/SchemeAdHoc.cc file. Its easy: just add a new command name, and the code to execute.

Calling scheme from Hypergraphs

The ExecutionLink, whem used with the GroundedSchemaNode, allows scheme code to be invoked when the given ExecutionLink is evaluated. See that page for details.

Multi-threading

Guile is multi-threading-capable, and can be used with multiple threads just fine, with just a few caveats: it must be initialized in *each* thread in which it is to be used. The method SchemeEval::thread_init() initializes guile only in the *current* thread: therefore, one must call this method in every thread in which scheme is meant to be used. Note that OpenCog has a way of creating threads unexpectedly, in particular, in the server socket connection handling. Caveat Emptor.