ExecutionOutputLink

From OpenCog
Jump to: navigation, search

The ExecutionOutputLink is a type of FunctionLink that allows the execution of arbitrary scheme, python, haskell and Atomese code, returning either a Value or an Atom as a return value. They should be contrasted with EvaluationLinks, which can evaluate arbitrary code, but are limited to returning only TruthValues instead of generic Values or Atoms.

When ExecutionOutputLinks are paired with GroundedSchemaNodes, they behave like "black boxes" whose inner working are completely opaque to any sort of symbolic reasoning, inference or reduction that could be applied. This is in contrast to, for example, the arithmetic links, such as PlusLink and TimesLink, which do allow symbolic reasoning and manipulation. Thus, in general, the use of ExecutionOutputLinks is strongly discouraged, except in on particular domain: to interface with external systems for sensory systems or for control (e.g. for robot control, video/audio perception systems, etc.)

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.

Definition

If one has a LambdaLink, DefinedSchemaNode or GroundedSchemaNode schema, then

ExecutionOutputLink schema args

represents the application of the function-symbol schema to the argument-list args. That is, it provides a graphical representation of a function call (because it is, after all Atomese). However, it can also be directly executed to actually perform the indicated computation.

The ExecutionOutputLink is in many ways effectively equivalent to a PutLink, when used with LambdaLink and DefinedSchemaNode. The PutLink is meant to to represent a beta-reduction operation, or a beta-reduction step. However, when a PutLink is executed, not only is the beta reduction performed, but also the resulting Atom is executed as well.

The PutLink does NOT support GroundedSchema; only the ExecutionOutputLink can be used with GroundedSchema. This is because the PutLink plugs terms into variables, and then executes the result. GroundedSchema have no variables, so there is nothing to "plug into", there is nothing to beta-reduce.

For more information, see Procedure Nodes.

The ExecutionOutputLink is not to be confused with the ExecutionLink, which yokes together the input and the output of a function. 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

When used with a LambdaLink, it behaves the same way as a PutLink. Thus, for example

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

will return

 (NumberNode 42)

Using a PutLink instead of the ExecutionOutputLink will yield the same result.

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)))))

and invoked with that name:

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

See multiple tutorials in the /examples directory.

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 executed when they are found on the implication side of a BindLink. In this case, the pattern matcher will substitute grounded terms 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 ExecutionOutputLink::do_execute() (in the opencog/atoms/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")
)