RelEx2Logic rules

From OpenCog
(Redirected from RelEx2Logic Rules)
Jump to: navigation, search

NOTE: This page contains some rough ideas guiding current R&D [Sep 9, 2013] .... It is mainly intended, at the present time, for use by individuals involved in said R&D. The code and ideas described here are under development.

The core concepts on this page, and the initial text on this page, are the work of Ruiting Lian and Ben Goertzel.

See also:

Introduction

The RelEx2Logic rules (previously called Link2Atom and Syn2Sem rules) are a set of hand-coded rules for mapping the output of RelEx into a style of OpenCog Atom representation that is better suited for automated reasoning by PLN and other tools.

The rules are in /opencog/nlp/relex2logic/rules.

They have multiple purposes:

  1. To serve as part of a functioning OpenCog NLP system, with potential uses including text analysis and natural language dialogue
  2. As conceptual research tools, to help us understand the issue of mapping syntactico-semantic output such as RelEx produces, into more abstracted logical relationships such as PLN prefers
  3. As tools to help generate a corpus of (link parse, abstracted PLN-style Atoms) pairs, which can then be analyzed automatically to generate language understanding and generation rules (see Syn2Sem for more on this)

This page describes the rules at a high level, and gives some example rules. Its main initial purpose is to guide the work of the people who will be helping complete the rule-based and implement the rule engine, etc. So the focus here is on practical rather than theoretical issues, and on how-to rather than on results reporting etc.

Example Rule (Tense Rule)

To ground the rest of the discussion, here is a very simple example of a RelEx2Logic rule. This rule handles tense markers output by RelEx. Of course, most of the rules are more complex than this.

Here the rule input is described, for ease of human comprehension, in RelEx SFF format (one of RelEx's many output formats). In actual implementation, the rules will likely be implemented in a quite similar format.

RULE INPUT:

tense(W, Tense)
pos(W, verb)

RULE OUTPUT:

(tense-rule W (get_instance_name W word_index sentence_index) Tense)

HELPER FUNCTION:

(define (tense-rule verb instance tense)
   (define new_predicate (PredicateNode instance))
   (define verb_node (PredicateNode verb)) 
   (define tense_node (ConceptNode tense))
   (list
      (InheritanceLink new_predicate verb_node)
      (InheritanceLink new_predicate tense_node)
   )
)

EXAMPLE:

tense(eat, Past)
pos(eat, verb)

==>

(tense-rule "eat" "eat77" "Past")

==>

(InheritanceLink eat77 eat)
(InheritanceLink eat77 Past)

You may note that this example uses the additional helper function get_instance_name. This will be explained below; basically it just chooses a name for an instance of a concept, that is unique in a given AtomSpace (e.g. for an instance of chair it might choose chair77). The word_index is k if the word W is the k'th occurrence of that word in the sentence; and the sentence_index is an identifier distinguishing that sentence from others.


Suggested format for this rule is:

RULE FORMAT:
tense($W, $Tense) & pos($W, verb) => (tense-rule $W (get_instance_name $W word_index sentence_index) $Tense)

Implementation in RelEx

There are many possible ways to implement the RelEx2Logic rules within the OpenCog software framework. As examples:

  • A partial prototype implementation, implemented by Ruiting Lian in early 2013, worked by reading in the RelEx SFF text output files, and then applying rules coded in Java to produce appropriate Scheme output
  • It would be possible to implement the rules in Scheme in such a way as to make each rule an OpenCog ImplicationLink. Rule execution would then be carried out within OpenCog, utilizing the pattern matcher as the core of a custom OpenCog rule engine. This might be the most elegant implementation, though we have not chosen it for the initial implementation work due to a desire to maximize speed of development and ease of experimentation.
  • One can implement a simple RelEx2Logic rule engine as the guts of a custom RelEx output generator. This is the approach we have chosen for initial implementation, to be elaborated a bit below.

Guidance on RelEx Output Generators

Anyone interested in making a new RelEx output generator (for RelEx2Logic or other purposes) should look at Sentence algorithms' Step 3, and then at the src/java/relex/output/SimpleView.java class which is a simple example of how to generate output from RelEx.

Also peripherally relevant, the src/java/relex/output/OpenCogScheme.java class, which generates Scheme output from RelEx (and replaces the obsoleted XML output that was previously used to pipe info from RelEx to OpenCog). However, this Scheme output is semantically equivalent to the standard RelEx output, and doesn't use PLN-style semantics like the output of the RelEx2Logic rules.

If you look in src/java/relex/output/SimpleView.java, you will see that essentially what is done is to iterate through all the binary and unary relations in the RelEx feature graph, and translate each one into a string for output.

On the other hand, what needs to be done for applying the RelEx2Logic rules, for now is, roughly, to wrap up a simple rule engine for applying the RelEx2Logic rules inside a RelEx output generator. This rule engine will be doing a little more than simply translating individual RelEx relations into strings for output; more on this below.

Somewhat peripherally, if you like to read Java but are unfamiliar with rule engines, the code for this simple rule engine is easy to read and fairly informative.

Suggested Rule File Format

Rather than having the rules coded in Java, it's better to have the rules live in a text file, loaded into the RelEx output generator. This gives more flexibility in terms of future use of the rules, and make it easier for rules to be created and edited. This is the strategy taken with the link parser and RelEx, both of which have their rules stored in text files.

We suggest two different rule files, actually:

  1. A Scheme file, containing the helper functions associated with the rules
  2. A rule file, consisting of rules in a simple textual format

An example entry in the rule file might be:

[SVO]  {2} <SV, SVP> _subj($y, $x) & _obj($y, $z) => (SVO-rule $x (get_instance_name $x word_index sentence_index) $y (get_instance_name $y word_index sentence_index) $z (get_instance_name $z word_index sentence_index))

Here

  • the [SVO] indicates the name of the rule. This may be optional -- not all rules necessarily need names
  • the number {2} indicates that the rule has Priority 2
  • the expression <SV, SVP> indicates that the rule has mutual exclusion relations with the rules named SV and SVP

Note that the real work of the rule is done by the helper function SVO-rule, which is assumed to reside in a separate Scheme file.

Rule Engine Requirements / Outline

The RelEx2Logic rules have the following properties:

  • INPUT: A set of RelEx (binary or unary) relationships
  • OUTPUT: A string representing Scheme code (to be interpreted within the OpenCog Scheme shell)

The interpretation of the Scheme output of a RelEx2Logic rule presents some minor complications related to naming instances, hinted at above and to be discussed below. But these complications are irrelevant to applying the rules to RelEx output, which is the subject of this particular section of the wiki page.

The two complications that *are* relevant to applying the RelEx2Logic rules to RelEx output, are priority and mutual exclusion. These are pretty common features of rule engines.

To understand the need for rule priority, consider the rules for Subject-Verb (SV) sentences and Subject-Verb-Object (SVO) sentences. The RelEx links produced from an SV sentences are a subset of those produced from an SVO sentence. But if the O is present, we don't want to mistakenly treat the sentence as an SV sentence. So, we want the SVO rule to have a higher priority than the SV rule, so that applying the former will be attempted first. Further, we want the two rules to be mutually exclusive, so that if the SVO rule has already been applied to a sentence, then SV won't be applied afterwards.

Exception:

At least that is the case for very simple sentences.

For complex sentences, however, it is possible for both SVO and SV rule to be applied to different parts of a sentence. So if SVO rule is applied to one part, it only excludes SV rule on that particular part, but not the rest. For example, this will occur on sentences with conjunctions.

A Couple Subtleties

This section describes a couple subtleties that came up in the course of writing RelEx2Logic rules, and were not thought of at the time of initially writing this spec.

The first is the notion of restricted-scope variables. These are variables that can be bound only to entities taking values named in a certain list of values. The RelEx2Logic rule file should contain a list of the scopes of restricted variables, additional to the list of rules. The purpose of this is to avoid the need to copy and paste rules, i.e. to avoid the creation of rules that are identical except for differing in a specific concrete word or relationship name.

The second is "Atomspace-based semantics assembly" -- wherein RelEx2Logic outputs fragmentary logic expressions denoting the meaning of parts of the sentence, which must then be pieced together afterwards. It is suggested that this piecing together can be done in the Atomspace, driven initially by hand-coded ImplicationLinks. This mechanism will be used mainly to handle sentence modifiers (words that are best viewed as modifying the relationship expressed by a sentence or clause, rather than as modifying an individual word).

An alternative to Atomspace-based semantics assembly would be rule chaining, as occurs in many rule engines. But in this case, to use rule chaining would be implementationally complex, as it would require the Atoms created by the Scheme helper functions to be fed back into the RelEx2Logic Java rule engine (which is implemented inside a RelEx output module). As our eventual goal is to replace the RelEx2Logic rule engine with a rule engine utilizing the Atomspace as its native representation, we have opted to do the chaining in the Atomspace even in this first implementation

These two features are both exemplified by the Maybe Rule, given as follows:

Maybe Rule

This rule is intended to deal with cases such as "Maybe she eats lunch."

In this case, RelEx would output

_advmod(eat, maybe)
_obj(eat, lunch)
_subj(eat, she)

but the most natural interpretation of the semantics is that the "maybe" indicates a modification of the strength of the truth value of the link

EvaluationLink
    eat
    ListLink she lunch

rather than of the node "eat."

Also, suppose we have "Possibly she eats lunch" and "Perhaps she eats lunch." As a first pass, we might like to use the same rule for handling these sentences as for "Maybe she eats lunch." But we would rather not have to copy and paste the same rule for the three cases -- we'd like to be able to use a restricted-scope variable that can take on the values {maybe, possibly, perhaps}.

Next, considering "Maybe she is beautiful", one sees that in this case, RelEx would output

_advmod(beautiful, maybe)
predadj(she, beautiful)

and the most natural interpretation of the semantics is that the "maybe" indicates a modification of the strength of the truth value of the link

InheritanceLink
    she
    beautiful

This gives a different case where a restricted-scope variable may be valuable. We would rather not have to write a separate "maybe rule" for the case where an EvaluationLink results from RelEx2Logic interpretation of a verb, versus the case where an InheritanceLink results from this interpretation.

Given these considerations, a Maybe Rule as follows is proposed:

_advmod($v1, $r_maybe)  ==> (maybe-rule $v1 (get_instance_name $v1 word_index sentence_index) $r_maybe  )
scope: maybe
maybe
possibly
perhaps

The scope definitions at the end would have to be provided in the RelEx2Logic rule file (or a separate variable scopes file). When the Maybe Rule is matched, all allowable values of the restricted-scope variables (which have names starting with $r) according to the provided file must be checked.

Alternatively, regular expression can be employed.

The MaybeRule helper function will produce a link of the form

MaybeMarkerLink $v1

In the case of "Maybe she eats lunch.", the SVO rule will give

EvaluationLink eats_1 she_1 lunch_1

and the MaybeRule will give

MaybeMarkerLink eats_1

and then an ImplicationLink in the AtomSpace such as

ImplicationLink
   AND
      MaybeMarkerLink $x
      EvaluationLink $x $y
   ExecutionLink
      MaybeSchema
      EvaluationLink $x $y

will "put the pieces together" and enable the MaybeMarkerLink to do its business and modify the truth value of the EvaluationLink..... So ultimately what will happen is that the truth value of

EvaluationLink eats_1 she_1 lunch_1

will be suitably decreased by the MaybeSchema.

Note that we have to handle cases like "Maybe possibly she ate lunch", as well...

Rule Application Process

Given a collection of Scheme output strings produced by applying the RelEx2Logic rule-base to a sentence S, the process of applying the collection Scheme strings is slightly involved and involves three steps:

  1. Generate instance names for all the words in the sentence (via the Scheme function choose_instance_names)
  2. Apply the output Scheme strings, using get_instance_name to fetch the generated instance names as appropriate
  3. Clean up the Atoms produced in Step 2, by finding useless instances and removing them (via running the clean_up_instance Scheme script on each instance produced in Step 1)

Note that when we integrate anaphor resolution into the process, it will intervene in these steps, via requiring re-use of instance names from the prior sentence within the current sentence. But we can ignore that for the initial RelEx2Logic implementation.

Choosing Instance Names

When the set of Scheme strings produced by RelEx2Logic rules for a given sentence is output and ready to be interpreted by the Scheme shell, a preliminary step must be undertaken: run the Scheme script choose_instance_names the sentence.

This script needs to be written.

What choose_instance_names should do is: assign an instance name to each non-glue word in the sentence, which does not already occur in the given OpenCog instance as a name of a WordNode or ConceptNode.

The functional definition of a "non-glue word" is any word that will never occur as an argument of get_instance_name for any of the RelEx2Logic rules. Examples of "glue words" are articles and prepositions. Note that if instance names are assigned to some glue words by accident, no harm is done other than a bit of wastage of processor time.

choose_instance_names should store the names it has conceived, somewhere that allows them to be gotten shortly afterwards by get_instance_name

To make the job of this script easier, perhaps we can ask RelEx, as part of its RelEx2Logic output, to produce a simple list of the root forms of all the words in the sentence. So for instance, if we have the sentence "Pigs love the smelly tofu" , the root-form word list would be (pig love smelly tofu). This could be output along with the Scheme strings produced from the sentence.

So, if we have the sentence "Pigs love the smelly tofu" and this has sentence index (unique identifier) 44, we can say

(choose_instance_names "(Pig love smelly tofu)" 44)

and this look into the Atomspace and choose the first instance name for *the root form of* each non-glue word, that is not already taken by some WordNode , ConceptNode or PredicateNode in the Atomspace, e.g. it might choose:

pig_5 love_44 smelly_14 tofu_1

This would mean that the specific Atomspace in question doesn't contain any instance of tofu so far (this one, tofu_1, will be the first), but it contains 4 other instances of pig, 43 instances of love, and 13 instances of smelly already.

Getting Instance Names

Assuming choose_instance_names has been called for the sentence being processed, now the Scheme rules produced by RelEx2Logic for that sentence can be invoked. Each of these rules will grab one or more of the instance names produced via choose_instance_names, using the Scheme function get_instance_name (which also remains to be written). For example, recall the tense rule given above, which is invoked via

(tense-rule W (get_instance_name W word_index sentence_index) Tense)

In the specific example given above this might be invoked via

(tense-rule love (get_instance_name love 1 44) Past)

The "1" is the word_index, and means in this case that the instance of "love" under consideration, is the first instance of "love" in that sentence. Most of the time this argument will be "1", but we also have to deal with sentences like "The small dog molested the big dog", in which case there will be a dog with word_index 1 and a dog with word_index 2.

Consistent with the above example, the output of

(get_instance_name love 1 44)

would be

love_44

Cleaning Up Instances

In the approach suggested here, a separate instance node is created for each non-glue word in the sentence. This will, in many cases, produce unnecessary instance nodes.

For instance, in the sentence "I ate the yummy cookie", there is no need to make an instance node such as yummy_15, because it's enough just to create a link like

InheritanceLink cookie_77 yummy

However, in the sentence "I ate the dubiously yummy cookie", an instance node such as yummy_15 is more useful, because one needs to say

InheritanceLink cookie_77 yummy_15
InheritanceLink yummy_15 dubious

Note that there is no need for an instance node for "dubious" in the above case. But what about the sentence "I ate the extremely dubiously yummy cookie?" Etc.

It turns out that, to tell which words really need instance nodes or not, it's necessary to somewhat understand the structure of the sentence.

This could be dealt with in a number of ways, but the present suggestion is to bypass the issue, and simply

  • assign all non-glue words instance names up front, at the start of the RelEx2Logic rule application process
  • at the end of the RelEx2Logic rule application process, remove any instances that were unnecessarily created

This is slightly wasteful, but has the benefit of simplicity.

So, to handle the latter part of this, we can write a script clean_up_instance, which can be invoked such as

clean_up_instance yummy_15

What this script should do is:

  • Check in the Atomspace (or simply in the local output of the RelEx2Logic rules) to see if any of the Atoms with this name are "redundant".
  • If a redundant Atom is found, remove it and move its links to its parent concept

An Atom X named blah_num is redundant, for this purpose, if its only links are:

  • an InheritanceLink to an Atom of the same type named blah
  • other links of the form: Inheritance A X, or Evaluation A X, or Evaluation A X Y or Evaluation A Y X

So for instance, in processing the sentence "I ate the dubiously yummy cookie", one might find

InheritanceLink cookie_77 yummy_15
InheritanceLink yummy_15 dubious_22

The dubious_22 would be found redundant. It would then be removed, and its links moved to its parent "dubious", resulting in

InheritanceLink cookie_77 yummy_15
InheritanceLink yummy_15 dubious

Choosing Variable Names

A variant on the process of choosing instance names, needed in the context of some rules (e.g. some quantifier rules), is the process of choosing names for OpenCog VariableNodes.

This is exemplified by the Those rule, which looks like

(those-rule W (get_instance_name W word_index sentence_index) choose_var_name)

When this is interpreted, choose_var_name must be invoked to select a VariableAtom name that is not already used in the Atomspace in question.

Loading the Output into the Atomspace

The ConceptNodes and PredicateNodes and associated Links created according to the above process, are not the only Atoms that should be entered into the Atomspace as a result of the RelEx2Logic operation.

We also want a SentenceNode, linking to a list of the WordNodes in the sentence. Each WordNode should link to a ConceptNode denoting the WordNode, via a ReferenceLink.

If a ConceptNode has been created in the RelEx2Logic process, with name cat_52, then the associated WordNode should also have name cat_52.

So: *after* RelEx2Logic processing as described above has been done, the SentenceNode and associated WordNodes can be created.

Furthermore, it may also be valuable to have Atoms entered into the Atomspace representing the link parser (definitely) and RelEx (possibly) output. Care must be taken here to use the same instance names at RelEx2Logic did. The details of how to achieve this need to be worked out.

Hang on. RelEx OpenCog format already does this, and it works fine. Note that the instance names are already stored in the relex graph, in the UUID node. Just use that, and all markup will always be consistent. There's also a shell script, a server that automatically connects to OpenCog, and documentation on how to use it, it comes with RelEx already.

Example Rules

It is unclear how many RelEx2Logic rules will be needed to achieve reasonable functionality.

Generally speaking, with any rule based system, there comes a point of diminishing returns after which one finds that one is adding specific rules to cover individual cases. This is a limitation of the rule-based approach to AI. We don't intend to push RelEx2Logic to that limit, because in an overall OpenCog context, RelEx2Logic is a tool intended for a combination of temporary practical usage and exploratory research. (In time it will be replaced by various sorts of learned rules -- it is practical and conceptual "scaffolding", and will likely be very useful as such.)

In it initial form, this page lists a few central RelEx2Logic rules. As more rules are developed, the final representations can be added at the RelEx2Logic representation page, while this page becomes an online guide to the rule-base, augmenting the guidance comprised by the code itself.

SV rule

Priority: 2; Exclusion with SVO

INPUT:
_subj(y, x)
OUTPUT:
(SV-rule x (get_instance_name x word_index sentence_index) y (get_instance_name y word_index sentence_index))
HELPER FUNCTION:

(define (SV-rule subj_concept  subj_instance  verb  verb_instance)
	(define new_predicate (PredicateNode verb_instance ) )
        (define verb_node (PredicateNode verb))
	(define new_concept_subj (ConceptNode subj_instance ) )
	(define subj_node (ConceptNode subj_concept))
	(AndLink 
		(InheritanceLink new_predicate verb_node )
		(InheritanceLink new_concept_subj subj_node )
		(EvaluationLink new_predicate new_concept_subj)
	)
)
EXAMPLE:
_subj(smile, Pumpkin)

==>
(SV-rule Pumpkin Pumpkin_66 smile smile_666) 
==>
(InheritanceLink smile_666 smile )
(InheritanceLink Pumpkin_66 Pumpkin)
(EvaluationLink smile_666 Pumpkin_66)

SVO rule

Priority: 1; Exclusion with SV

INPUT:
_subj(y, x)
_obj(y, z)
OUTPUT:
(SVO-rule x (get_instance_name x word_index sentence_index) y (get_instance_name y word_index sentence_index) z (get_instance_name z word_index sentence_index))
HELPER FUNCTION:

(define (SVO-rule subj_concept  subj_instance  verb  verb_instance  obj_concept  obj_instance )
	(define new_predicate (PredicateNode verb_instance ) )
        (define verb_node (PredicateNode verb))
	(define new_concept_subj (ConceptNode subj_instance ) )
	(define subj_node (ConceptNode subj_concept))
	(define new_concept_obj (ConceptNode obj_instance ) )
        (define obj_node (ConceptNode obj_concept))
	(AndLink 
		(InheritanceLink new_predicate verb_node )
		(InheritanceLink new_concept_subj subj_node )
		(InheritanceLink new_concept_obj obj_node )
		(EvaluationLink new_predicate new_concept_subj new_concept_obj)
	)
)
EXAMPLE:
_subj(eat, pig)
_obj(eat, tofu)
==>
(SVO-rule pig pig_55 eat eat_245 tofu tofu_1 ) 
==>
(InheritanceLink eat_245 eat )
(InheritanceLink pig_55 pig)
(InheritanceLink tofu_1 tofu)
(EvaluationLink eat_245 pig_55 tofu_1)

Note that this output is *before* the application of clean_up_instance.

If the sentence was "The pig ate the tofu", these output links would get cleaned up to

(InheritanceLink pig_55 pig)
(InheritanceLink tofu_1 tofu)
(EvaluationLink eat pig_55 tofu_1)

Whereas, if the sentence was "Pigs eat tofu", the output links would get cleaned up to

(EvaluationLink eat pig tofu)

Or, if the sentence was "The pig swiftly ate the tofu", the output links would remain in their original form -- and of course there would be additional links related to "swiftly", produced by a different rule.

SVP rule

Priority 3;

INPUT:
_preadj(y, x)
OUTPUT:
(SVP-rule x (get_instance_name x word_index sentence_index) y (get_instance_name y word_index sentence_index))
HELPER FUNCTION:

(define (SVP-rule subj  subj_instance  predicative  predicative_instance)
	(define predicativeNode_ins (ConceptNode predicative_instance ) )
        (define predicativeNode (ConceptNode predicative))
	(define subjNode_ins (ConceptNode subj_instance ) )
	(define subjNode (ConceptNode subj))
	(AndLink 
		(InheritanceLink predicativeNode_ins predicativeNode )
		(InheritanceLink subjNode_ins subjNode )
		(InheritanceLink subjNode_ins predicativeNode_ins)
	)
)
EXAMPLE:
_predadj(I, happy)

==>
(SVP-rule I I_56 happy happy_556) 
==>
(InheritanceLink happy_556 happy )
(InheritanceLink I_56 I)
(InheritanceLink happy_556 I-56)

Amod rule

Priority 3 (?)

INPUT
_amod(N,A)
OUTPUT
(amod-rule N (get_instance_name N word_index sentence_index) A (get_instance_name A word_index sentence_index))
HELPER FUNCTION

(define (amod-rule concept instance adj adj_instance )
	(define new_concept (ConceptNode instance) )
	(define new_concept_adj (ConceptNode adj_instance) )
        (define adj_node (ConceptNode adj))
        (define concept_node (ConceptNode concept))
	(AndLink
		(InheritanceLink  new_concept_adj adj_node )
		(InheritanceLink  new_concept new_concept_adj )
		(InheritanceLink  new_concept concept_node )
	)
)
EXAMPLE

amod(cookie, yummy)
==>
(amod-rule cookie cookie_77 yummy yummy_15)
==>
InheritanceLink cookie_77 cookie
InheritanceLink yummy_15 yummy
InheritanceLink cookie_77 yummy_15

If the sentence were "I ate a yummy cookie at work today", the final output would be cleaned up to

InheritanceLink cookie_77 cookie
InheritanceLink cookie_77 yummy

as there is no special information about the instance yummy_15, hence no need to make a special Atom for it.

Those rule

Priority 3 (?)

INPUT
insert appropriate relex expression for "those W" here
OUTPUT
(those-rule W (get_instance_name W word_index sentence_index) choose_var_name)
HELPER FUNCTION:

(define (those-rule concept instance var_name)

	(define new_instance (ConceptNode instance) )
	(define var (VariableNode var_name))
        (define new_concept (ConceptNode concept))
  	(ImplicationLink 
    		(MemberLink var new_instance)
    	        (InheritanceLink var new_concept)
	)
  
)


EXAMPLE
Those cookies
==>
(insert RelEx relationships here)
==>
(those-rule cookie cookie_55 var_444)
==>
(ImplicationLink 
    (MemberLink var_444 cookie_55)
    (InheritanceLink var_444 cookie)
)

ThatRule

(this rule is described somewhat informally and messily at the moment because Ben was in a hurry when he wrote this section)

The rule

$r_that_rel($v1,$v2) ==> (that-rule $v1 (get_instance_name $v1 word_index sentence_index) $v2 (get_instance_name $v2 word_index sentence_index) )

with

scope: that_rel
that
_that

would create a Scheme helper function that would produce output

ThatMarkerLink $v1 $v2

Consider the sentence:

I think you are insane

    _that(think, insane)
    _subj(think, I)
    _predadj(you, insane)

So then, after the action of RelEx2Logic, we'd have:

EvaluationLink
   think_1
   I_1

(from the SV rule) with

EvaluationLink
   PredicateNode: ThatMarker
   ListLink
       think_1
       insane_1

from the ThatRule, and

Inheritance you_1 insane_1

from the predadj rule (which should have a low priority, so it only applies to predadj instances that weren't already grabbed up as part of some larger construct addressed by some other rule).

Then these pieces will need to be put together somewhere, but we can do that inside the OpenCog Atomspace, e.g. via

ImplicationLink
   AND
      EvaluationLink
            PredicateNode: ThatMarker
            ListLink $X $Y
     EvaluationLink $X $Z
     Inheritance $W $Y
   EvaluationLink $X $Z (Inheritance $W $Y)

That is, the "extra" post-processing rules needed by constructs like "that you are insane" will be handled via ImplicationLinks to be executed in the Atomspace. This seems good given that we are looking to eventually port the other half of RelEx2Logic (the stuff that is currently done in the Java RelEx2Logic rule engine implemented in the LogicView and LogicProcessor classes) into the Atomspace ...

We can handle "I tell you that Pumpkin is delicious" similarly.. the RelEx links are

    that(tell, delicious)
    _obj(tell, you)
    _subj(tell, I)
    _predadj(Pumpkin, delicious)

The SVO rule, ThatRule and predadj rule give us

EvaluationLink tell_1 I_1 you_1

EvaluationLink
     ThatMarker
     Link
         tell_1
         delicious_1

InheritanceLink Pumpkin_1 delicious_1

and again, the pieces can be put toegether in the Atomspace, ultimately producing

EvaluationLink
   tell_1
   I_1
   you_1
   InheritanceLink Pumpkin_1 delicious_1

PostProcessing Discussion

This section gathers some discussion on RelEx2Logic "postprocessing", i.e. the piecing together of links created via rules like ThatRule and MaybeRule which create "marker" Atoms.

At time of writing (June 25, 2014), these ideas are still under active discussion and some changes are possible.

Update: At September 1, 2014, changes are made to simplify what actually needed post-processing for PLN. In this new discussion, ThatRule no longer needs to be post-processed. See http://github.com/opencog/relex/issues/124

Example Postprocessing Rule

Consider the sentence form

"The $subj which $verb $obj is very famous."

A rule to put together the pieces output by the WhichRule would look somewhat like the following...

Implication A B

where


A = ANDLink A1 A2  A3

where

A1 =

EvaluationLink

       PredicateNode $S

       ListLink

             ConceptNode $R

             ConceptNode $F


A2 =

InheritanceLink

        ConceptNode $R

        ConceptNode $F1


A3 =

EvaluationLink

       PredicateNode "WhichMarker"

       ListLink

             ConceptNode $R

             PredicateNode $S


B=

MemberLink
         $R
         SatisfyingSetLink $X
                 AndLink
                     EvaluationLink
                           $S
                           ListLink
                                 $X
                                 $F
                     InheritanceLink $X $F1


A Bunch Of Other Examples

Rodas has assembled a bunch of other examples of related but different forms, for comparison to the above example:


"$subj $verb $antecedent which $verb2 $obj."
"I like a restaurant which serves fish."

A1 =

EvaluationLink
		PredicateNode Like
		ListLink
			ConceptNode I
			ConceptNode Restaurant


A2 =

EvaluationLink
		PredicateNode serve
		ListLink
			ConceptNode Restaurant
			ConceptNode Fish

A1 = A1($verb, $subj, $antecedent)
A2 = A2($verb2, $antecedent, $obj)
where $antecedent= Restaurant

...

$antecedent which $subj $verb $obj are interesting."
"Books which you give me are interesting."
A1 =

InheritanceLink
		ConceptNode Book
		ConceptNode interesting.

A2 =

EvaluationLink
		PredicateNode give
		ListLink
			ConceptNode you
			ConceptNode me
			ConceptNode book

A1 = A1($antecedent, $adj)
A2 = A2($verb, $subj, $obj,$antecedent)
where $antecedent= Book

...

$antecedent which $subj $verb ,is very healthy."
"Pizza, which people love, is very healthy."
A1=

InheritanceLink
		ConceptNode Pizza
		ConceptNode healthy


A2 =

EvaluationLink
		PredicateNode love
		ListLink
			ConceptNode people
			ConceptNode pizza

A1 = A1($antecedent, $adj)
A2 = A2($verb, $subj,$antecedent)
where $antecedent= pizza

...

The $antecedent which $verb $obj is very famous
"The restaurant which serves fish is very famous."

A1 =

InheritanceLink
		ConceptNode restaurant
		ConceptNode famous


A2 =

EvaluationLink
		PredicateNode serve
		ListLink
			ConceptNode restarant
			ConceptNode fish

A1 = A1($antecedent, $adj)
A2 = A2($verb, $antecedent j,$obj)
where $antecedent= restaurant

...

"The $antecedent which $subj $verb in the library were $verb2 by $subj2"
"The books which I read in the library were written by Charles Dickens."
A1=
EvalautionLink
		PredicateNode write
		ListLink
			ConceptNode Charles_Dickens
			ConceptNode Book

A2 =
AndLink
		EvaluationLink
			PredicateNode read
			ListLink
				ConceptNode I
				ConceptNode Book
		EvaluationLink
		 	PredicateNode in
			 ListLink
		 		PredicateNode read
				ConceptNode library

Toward a General Rule

To handle all these different sorts of examples, conceptually, we would want an abstract overall rule like


ImplicationLink

    AndLink
        EvaluationLink
            PredicateNode "WhichMarker"
            ListLink
                ConceptNode $R
                PredicateNode $S
        SomeRelationship_A1 $S $R $F
        SomeOtherRelationship_A2 $S $R $F1

    MemberLink
         $R
         SatisfyingSetLink $X
                 AndLink
                   SomeRelationship_A1 $S $X $F
                   SomeOtherRelationship_A2 $S $X $F1

Here SomeRelationship_A1 and SomeRelationship_A2 may be complex constructs involving multiple links, potentially...

But this kind of conceptual rule isn't directly expressible in any language that can now be executed in the OpenCog framework.

So practically (in Ben's view) there seem to be two choices here

  • 1) Write specialized rules for each of these cases

or

  • 2) Write one generalized rule, and then normalize each of the cases into the format of the generalized rule

Regarding Option 2, we could write the general rule e.g. as

ImplicationLink

    AndLink
        EvaluationLink
            PredicateNode "WhichMarker"
            ListLink
                ConceptNode $R
                PredicateNode $S
        EvaluationLink $A1 $R $S
        EvaluationLink $A2 $R $S

    MemberLink
         $R
         SatisfyingSetLink $X
                 AndLink
                   EvaluationLink $A1 $X $S
	           EvaluationLink $A2 $X $S

So then, taking the above example

"I like a restaurant which serves fish."

we’d have

A1 =

        EvaluationLink
		PredicateNode Like
		ListLink
			ConceptNode I
			ConceptNode Restaurant

	= 

 	EvaluationLink
		PredicateNode PA1
		ListLink
			ConceptNode Restaurant
			PredicateNode Serve

 	where

	EquivalenceLink
		EvaluationLink PA1 $R $S
		EvaluationLink Like I $R


A2 =

         EvaluationLink
		PredicateNode serve
		ListLink
			ConceptNode Restaurant
			ConceptNode Fish

	= 

 	EvaluationLink
		PredicateNode PA2
		ListLink
			ConceptNode Restaurant
			PredicateNode Serve

 	where

	EquivalenceLink
		EvaluationLink PA2 $R $S
		EvaluationLink $S $R 


As you see, the basic idea in this approach would be to write the basic postprocessing rule using a single format, but then translate (“normalize”) other outputs into that format…

This requires one to write normalization rules, but these normalization rules should be of generic use…. For instance, for case A1 in the above example, the normalization used is simply

L(I,R) = PA1(R,S) & [ PA1(R,S) = L(I,R) ] ⇒ PA1(R,S)

(using a more concise notation where e.g. A1 = L(I,R) )

This normalization follows from two inference steps:


Equivalence
     X
     AND
	Y
	Equivalence X Y

and

Implication 
	AND X Z
	X


(where X = L(I,R), and Y = PA1(R,S), and Z = “Y & (X=Y)” )


Sooo -- if we had an inference engine capable of applying these inference steps, it could backward chain from

	EvaluationLink
		PredicateNode $PA1
		ListLink
			ConceptNode Restaurant
			PredicateNode Serve

to find

EvaluationLink
		PredicateNode Like
		ListLink
			ConceptNode I
			ConceptNode Restaurant


	EquivalenceLink
		EvaluationLink PA1 $R $S
		EvaluationLink Like I $R

The output B of the rule then looks like

    MemberLink
         $R
         SatisfyingSetLink $X
                 AndLink
                   EvaluationLink $A1 $R $S
	           EvaluationLink $A2 $R $S
=

MemberLink
	Restaurant
	SatisfyingSetLink $X
		AndLink
			Evaulation PA1 Restaurant Serve
			Evaluation PA2 Restaurant Serve

=

MemberLink
	Restaurant
	SatisfyingSetLink $X
		AndLink
                   EvaluationLink
				PredicateNode Like
				ListLink
					ConceptNode I
					ConceptNode Restaurant
		   EvaluationLink
				PredicateNode serve
				ListLink
					ConceptNode Restaurant
					ConceptNode Fish

The other examples given above can be dealt with similarity…

What Direction to Take?

Overall, my (Ben's) conclusion (on June 5, 2014) is that: The only way I see to avoid a proliferation of special-case rules of some sort, is to use inference for normalization of various forms into a common form.

Using inference is the right approach, but to get PLN to do this sort of inference will, will require some fiddling…

I think it makes sense to make enough specialized rules now that we can do interesting experimentation with RelEx2Loic output, but not to try to get anywhere near complete coverage of English with the specialized rules. Then, we should focus on using PLN to do the normalization automatically, to avoid needing an infinite proliferation of highly specialized rules in future ...

Unless someone sees a better alternative ;-) ...