ExecutionOutputLink

From OpenCog
Jump to: navigation, search

The ExecutionOutputLink is a type of FunctionLink that allows the execution of arbitrary scheme or python code, returning an Atom as a return value. They should be contrasted with EvaluationLinks, which can evaluate arbitrary scheme or python code, but return a TruthValue instead of an Atom.

Because ExecutionOutputLinks can execute arbitrary code, they behave like "black boxes" and prevent any sort of reasoning or reduction from being applied to them. The AtomSpace also supports a number of pre-defined "clear-box" links, such as PlusLink and TimesLink, for which reduction can be performed. See FunctionLink and cog-reduce! for additional discussion of clear-box link types.

ExecutionOutputLinks can be executed with the cog-execute! function. They are executed automatically by the pattern matcher when they are used in the implicand of a BindLink. They are also used in the OpenPsi and procedural parts of OpenCog.

If one has a GroundedSchemaNode S, then

ExecutionOutputLink S A

indicates the output obtained by applying S to the argument (potentially argument-list) A.

For instance

ExecutionOutputLink + (3 2)

would have value 5, since 3+2=5. Which could be represented in the atomspace by

SimilarityLink <1>
   NumberNode 5
   ExecutionOutputLink
       GroundedSchemaNode +
       ListLink
           NumberNode 3
           NumberNode 2        

For more information, see Proceedure Nodes.

This is not to be confused with ExecutionLink, which connects the inputs to the output of a function (or a procedure, if it doesn't have output). For instance one may rewrite the example above using ExecutionLink as follows:

ExecutionLink <1>
   GroundedSchemaNode +
   ListLink
       NumberNode 3
       NumberNode 2
   NumberNode 5

A way to automatically create ExecutionLinks from ExecutionOutputLinks is given below.

Usage

The ExecutionOutputLink can be used either with a GroundedSchemaNode, which specifies a callback into scheme, Python or C++ code; a DefinedSchemaNode (defined via a DefineLink; or a LambdaLink, which defies a function body.

GroundedSchemaNode

Thus, for example:

 ExecutionOutputLink
     GroundedSchemaNode func
     ListLink
         SomeAtom val_1
         OtherAtom val_2
         

would cause func(val_1, val_2) to be called. Upon execution, it is expected that an atom is produced as a "return value".

In principle, the schema name could be any URL whatsoever. Currently, only the c++:, scm: and py: URLs for c++ function, scheme and python scripts work. The idea of supporting http: style URL's, for JSON or SOAP or whatever, is just a wild-eyed idea, at the moment.

GroundedSchemaNode in Scheme

For example, suppose one had defined a scheme function

(define (do-stuff handle1 handle2) ( ... ))

Then the evaluation of the following

 ExecutionOutputLink
     GroundedSchemaNode "scm:do-stuff"
     ListLink
         SomeNode arg_1
         OtherNode arg_2

would cause (do-stuff arg_1 arg_2) to be evaluated.

GroundedSchemaNode in Python

Python is supported, as well:

 def my_cat(a,b):
     return ConceptNode(a + b)

The above, after being loaded, ca be invoked as:

 ExecutionOutputLink
     GroundedSchemaNode "py: my_cat"
     ListLink
         ConceptNode "Hello"
         ConceptNode ", World"

which will return the atom

 ConceptNode "Hello, World"

LambdaLink and DefineLink examples

The following example shows how to execute a LambdaLink:

  (ExecutionOutputLink
     (LambdaLink
        (VariableList
           (VariableNode "$X")
           (VariableNode "$Y"))
        (PlusLink
           (VariableNode "$X")
           (TimesLink
              (VariableNode "$Y")
              (NumberNode 10))))
     (ListLink
        (NumberNode 2)
        (NumberNode 4)))

The above works and is supported syntax today. Upon execution (with cog-execute!) it will return

 NumberNode 42

The lambda can be give a name with DefineLink:

 (DefineLink
     (DefinedSchemaNode "the answer")
     (LambdaLink
        (VariableList
           (VariableNode "$X")
           (VariableNode "$Y"))
        (PlusLink
           (VariableNode "$X")
           (TimesLink
              (VariableNode "$Y")
              (NumberNode 10)))))

ad invoked with that name:

  (ExecutionOutputLink
     (DefinedSchemaNode "the answer")
     (ListLink
        (NumberNode 2)
        (NumberNode 4)))

The above works and is supported syntax today (see SCMExecutionOutputUTest.cxxtest).

Execution

One convenient way of using the ExecutionOutputLink is with the pattern matcher: this uses a BindLink to specify a set of variables, the pattern to be matched and the execution link to be called for the grounded variables matching the pattern. See the BindLink page for examples.

ExecutionOutputLinks are evaluated when they are found on the implication side of a BindLink. In this case, the pattern matcher will substitute grounded values for all variables, and then call func. If func returns an atom, then that returned atom is taken as the grounding (or output) of the ExecutionOutputLink. That is, the returned atom is substituted for the ExecutionOutputLink in the implicand.

When used in BindLinks, the ExecutionOutputLinks should not appear in the predicate side of the implication. In such a case, if the goal of executing the function is to determine whether a given pattern matches or not (within the BindLink), virtual links should be used instead.

Execution of ExecutionOutputLinks can be accomplished directly from the C++ interfaces by calling the static C++ method ExecutionLink::do_execute() (in the opencog/execution directory.) For scheme, see the cog-execute! function. For Python, use the scheme_wrapper.

Recording the output

Sometimes, one may want to record the output of an ExecutionOutputLink in an ExecutionLink. How could this be done? There is an easy, if slightly verbose solution: just write this:

ExecutionLink
   SchemaNode "did something"
   ListLink
      ArgAtoms "args"
   ExecutionOutputLink
      GroundedSchemaNode "scm:do-something"
      ListLink
         ArgAtoms "args"

Then, upon evaluation, the atom that is generated by the scheme function "do-something" is used to replace ExecutionOutputLink with the result. So for example, the following scheme code:

(define (do-something atom) (AttractionLink atom atom atom))

(cog-execute!
   (ExecutionLink
      (SchemaNode "did something")
      (ListLink
         (ConceptNode "args"))
      (ExecutionOutputLink
         (GroundedSchemaNode "scm:do-something")
         (ListLink
            (ConceptNode "args")))))

will return the following:

(ExecutionLink
   (SchemaNode "did something")
   (ListLink
      (ConceptNode "args"))
   (AttractionLink
       (ConceptNode "args")
       (ConceptNode "args")
       (ConceptNode "args")))


Similarly, the following:

(cog-execute!
   (ExecutionLink
      (SchemaNode "sum of stuff")
      (ListLink
         (NumberNode 2)
         (NumberNode 3))
      (PlusLink
         (NumberNode 2)
         (NumberNode 3))))

will result in the following output:

(ExecutionLink
  (SchemaNode "sum of stuff")
  (ListLink
     (NumberNode "2.000000")
     (NumberNode "3.000000")
  )
  (NumberNode "5.000000")
)