PLN scheme wrapper

From OpenCog


The PLN scheme wrapper allows one to build PLN inference step by step by applying the PLN rules in a forward way. By extension it can also be used to write and experiment with inference strategies in Scheme, but note that it has not been optimized for speed.


The usage is as follows

(RuleName Arg1 ... Argn)

where Arg1, ..., Argn are the premises (atoms) of the rule.

Simple Example

Here's a simple example one can directly run in the OpenCog shell

(define antecedent (EvaluationLink (stv 1 1) (PredicateNode "A")))
(define conclusion (EvaluationLink (PredicateNode "B")))
(define implication (ImplicationLink (stv 1 1) antecedent conclusion))

Notice how the TV of the conclusion has been let undefined. When the TV of the target is already defined then the resulting TV is equal to the old TV merged with the new TV produced by the inference rule (via the C++ function TruthValue::merge).

Now time to apply the inference rule

(ModusPonensRule implication antecedent)

which returns the following

(EvaluationLink (stv 1 0.99999988) (PredicateNode "B"))

Complex example

A more complex example (but still pretty simple), involving universal quantifier can be found in the file tests/reasoning/pln/scm/new/aboutGreaterThan.scm.

; example to proof a >~ c using that ForAll x,y if x >~ y and y >~ z then x >~ z
(define agt (PredicateNode "AboutGreaterThan"))
; axiom
(define x (VariableNode "x"))
(define y (VariableNode "y"))
(define z (VariableNode "z"))
(define antecedant (AndLink (EvaluationLink agt (ListLink x y))
                           (EvaluationLink agt (ListLink y z))))
(define conclusion (EvaluationLink agt (ListLink x z)))
(define body (ImplicationLink antecedant conclusion))
(define axiom (ForAllLink (stv 0.8 0.9) (ListLink x y z) body))
; facts
(define a (ConceptNode "a"))
(define b (ConceptNode "b"))
(define c (ConceptNode "c"))
(define a_agt_b (EvaluationLink (stv .5 0.8) agt (ListLink a b)))
(define b_agt_c (EvaluationLink (stv .5 0.7) agt (ListLink b c)))

; Inference
; csn stands from conclusion of step n
(define cs1 (UniversalInstantiationForAllRule axiom a b c))
(define cs2 (SimpleANDRule a_agt_b b_agt_c))
(define target (ModusPonensRule cs1 cs2))

Producing the target

guile> target
(EvaluationLink (stv 0.2 0.56) (PredicateNode "AboutGreaterThan")
   (ListLink (ConceptNode "a")
      (ConceptNode "c")))

PLN Scheme Wrapper Implementation

The PLN Scheme wrapper code is split in 3 parts

  • Scheme code to pre-process the rule application to be used in a user friendly way
  • C++ glue code between Scheme and C++
  • C++ side to apply a rule

Scheme Side

The Scheme side of the code is located at opencog/reasoning/pln/scm/pln-rules.scm.

Simple Rules

The Scheme code in that file defines macros so that applying a rule can be as simply as:

(ModusPonensRule A B)

That rule application is then transformed into

(pln-ar "ModusPonensRule" (A B))

by the following code in pln-rules.scm

(define (ModusPonensRule implication antecedant)
  (pln-ar "ModusPonensRule" (list implication antecedant)))

Except for the UniversalIntantiationForAllRule which is explained below, the code in pln-rules.scm is fairly obviously and easy to complete.

Complex Rules

Some rules demand more pre-processing mostly because the C++ PLN inference engine has been implemented to be used backward firstly. In fact the PLN forward chainer uses multiple calls of 1-step look-behind backward chainer in a forward way.

For instance, for UniversalInstantiationForAllRule pln-rules.scm contains code to

  1. perform the universal instantiation
  2. pass the body of the ForAll now instantiated to the C++ rule CustomCrispUnificationRule to compute the TV.

That code relies on the function universal-instantiate provided in opencog/scm/apply.scm.

Glue Code

There's only one Scheme function in charge of calling the C++ code, it works a follows

(pln-ar RuleName ArgumentList)
  • RuleName must correspond to the name of the rule, that is a string returned by the C++ method Rule::get_name(), expect for UniversalInstantiationForAllRule where the appended Handle ID has to be translated into a pHandle (all that is done on the C++ side of the wrapper).
  • ArgumentList is a list of atoms that the rule requires as input to be run in a forward way.

C++ Side

The C++ side of the code is located at opencog/reasoning/pln/PLNModule.[h|cc] and contained in the function applyRule.

It dispatching the call depending on whether the rule is a Generator or a Composer (see opencog/reasoning/pln/rules/Rule.h to know more about Composer and Generator).