IBM Agent Building Environment Developer's Toolkit


Chapter 5. More on Writing Rules

As a continuation of the tutorial on Rule Formulation (after a deliberate diversion on the use of rules by adapters), this section focuses on some of the finer points of rule writing. The emphasis is on some current restrictions, some fine points of the RAISE engines interpretation and optimization of rules, and some subtle dangers that you should be aware of with complex rules.

This section returns to the use of the KIF syntax, as a further enforcement of that syntax, which is needed for the writers of rule authoring tools, and, to some extent, by adapter writers.

Rule Reformulation by The Engine

The engine does internal reformulation of rules and the conduct set in which they are contained. There are several reasons for this, including processing efficiency, simplification of the inferencing algorithms, and sequencing problem avoidance. While the positioning of atoms within an antecedent or consequent does not matter logically and the sequence that rules are applied to inferencing does not affect the processing of rules by an engine, without some care by the rule author (or rule editor) and some special reordering of atoms by the engine before inferencing, sensors can be affected by some anomalies in the sequence of binding variables.

There are currently two aspects of internal rule reformulation:

These are discussed in detail below.

Because of this reformulation of rules, the logging of inferencing activities and some engine diagnostics may not directly reflect the original form of the rule as created in KIF syntax by a rule editor.

This internal form of rules as reformulated by the engine for inferencing is referred to as the agenda.


           RuleSet/  *-------> Conduct Set  *---------->  Agenda
           LTFactSet              *                         *
              *                   |                         |
              |                   |                         |
              *                   *                         *
           Library             Gathered                  Internal
           Objects               For                       Rule
                              Inferencing               Reformation


Rule Normalization

When the engine finds the following conditions in your rule, it factors the rule into multiple rules, as indicated:

To illustrate this consider the following rule:

    (=>(OR (CloseFamilyOf ?x Fred) (From ?message ?x)) (AND(PutInMailFolder Important)
                                                           (PageMe "Hot Mail")))

Internally, this would result in the following rule factoring:

  1. (=>(CloseFamilyOf ?x Fred) (PutInMailFolder Important))

  2. (=>(CloseFamilyOf ?x Fred) (PageMe "Hot Mail"))

  3. (=>(From ?message ?x) (PutInMailFolder Important))

  4. (=>(From ?message ?x) (PageMe "Hot Mail"))

In this case four rules result from the original rule, two for factoring out the OR from the antecedent and two for eliminating the multiple consequent atoms. Note: ANDed atoms in an antecedent do not require this factoring and are retained in the internal form of the rule.

This rule normalization allows for significant efficiencies and simplifications in the engine's inferencing algorithms.

Reordering Consequent Atoms for Sensor Bindings

To understand the need for reordering consequent atoms that is required by the engine in some cases, you need to understand the following aspects of internal engine inferencing:

The problem is that a sensor with dependencies on other (non-sensor or sensor) atoms (cases 1 or 3 above) requires that those dependent atoms be evaluated before the sensor is invoked. Otherwise the sensor could return a null fact set, rather than returning fact(s) expected of it. This results in a failed inference. To avoid this problem, the engine implicitly reorders sensor atoms to follow non-sensor atoms. This is to insure binding from the fact pool is done before sensors are invoked.

It may be more efficient and helps your external form of the rules to be more like the internal form if you do this ordering of sensors yourself. You may consider writing the rule in a way that is natural; then reorder the sensor atoms to be last in the antecedent.

Avoiding Cyclic Rules

Rules in an agenda can be dependent on each other when an atom in the antecedent of one rule is also a consequent atom of another rule in the same agenda. The inferencing algorithm handles this independent of the sequence of the rules in the conduct set. This is done by reiterating the inferencing as new derived facts are formed from consequent atoms.

However, recursion can occur, causing inferencing to fail when there are cyclic dependencies in the agenda. This is analogous to deadlocks in multi-threaded programs that employ locks for resource serialization, where a holds a lock on resource x, but waits for a lock on resource y; while b holds the lock on resource y and waits for a lock on resource x. The equivalent situation for a set of rules would be for rule 1 to have a consequent atom, a, that is dependent on an antecedent atom, b, in rule (1); while rule 2 has a consequent atom, b, that is dependent on an antecedent atom, a.

Every consequent atom is dependent upon its antecedent atoms in the same rule. You only have potential for cyclic situations when you write rules such that a consequent atom of one rule is also an antecedent atom of another rule. This in itself is not a problem. In fact, this is quite common and is a preferred way of formulating rules. The cyclic situation occurs when you also reverse the dependency.

To avoid this situation you should examine your conduct sets for cyclic dependencies.

Following is an example of a set of rules that contain a cyclic dependency. Lets see if you can spot it:

(=>(AND (d ?x) (e ?x)) (b,?x))

(=>(a ?x) (b ?x))

(=>(a ?x) (c ?x))

(=>(AND (c ?x) (g ?x)) (h,?x))

(=>(AND (b ?x) (f ?x)) (a ?x))

The cyclic dependency is usually analyzed by drawing a graph, representing the atoms by their predicate names. Dependent atoms are below the atom on which the depend. Following is the dependency graph for the previous example, showing the cycle between the atoms identified by their predicate names: a and b.


                              h
                              *
                        *-----*-----*
                        *           *
                        c           g
                        *
                        |
                  *-----*
                  *
                  a <--------*
                  *          |
                  |          |
           *------*-----*    |
           *            *    |
           f            b*---*
                        *
                        |
                  *-----*-----*
                  *           *
                  d           e


As with many other validations that one could do "manually", this check for cyclic rules could be provided by a rule editor. In this way you would avoid having errors detected during inferencing.

Consistency in the Use of Variables in a Consequent

When you use a variable name in a consequent atom that same variable name must also appear in an antecedent atom of the same rule. This is to insure that when inferencing is performed, and the antecedent succeeds, then a binding is made available for each consequent variable, making the consequent atoms fully bound facts. This is necessary because consequent atoms do not attain variable bindings on their own, but they attain those bindings as a by-product of antecedent bindings.

Limitations and Ways to Get Around Them

Fixed Number of Signatures for the Terms in an Atom

Currently a particular named (predicate) atom has a fixed signature. For one thing this means that you cannot have optional terms. You can get around this problem by establishing more than one atom (named predicate) where you would have had just one. Each variation has a different number of terms or term types according to your particular needs, even though logically it is the same predicate (atom) but with different signature requirements.

No Negations

This current version of the engine does not support logical negation prefixes for atoms. For now you can represent negation by using a predicate which is the negation of the positive one. For example, you could use NotMotherOfWendy instead of MotherOfWendy. Of course the engine has no special recognition for this because currently it does not recognize negatives in predicates or elsewhere. For this reason it will not enforce mutual exclusion between the positive and negative predicates or do anything with the negative predicate that it would not do for any other predicate.

Style and Optimizations

Factoring and Chaining for Rule Reduction

Improved efficiency and simplicity can be attained through factoring and chaining. In many cases this can allow you to reduce the number of rules in your conduct sets. These efficiencies are important when the same rules are used repeatedly for event processing. When estimating the potential savings, you need to consider not only the rule as you may have written it, but also the internal normalization of the rules, as described previously. This normalization can be the cause of even more rules that you see at the external level. Generally this internal normalization of rules makes an even stronger case for factoring and chaining improvements. In some cases, normalization can result in an exponential build-up of rules.

Factoring involves finding cases where the same atoms are connected in the same way, that is, where the same expression (call it E) is used more than once and replacing each instance of that expression with a single atom (call it A). Then construct a new rule that takes the original expression, E, as the antecedent, and implies a consequent that is the same as the replacement atom, A. Chaining is simply the use of a derived fact as an antecedent of another rule, such as deriving A from E, and using it to replace all instances of E in other rules. An example will help:

Consider the following set of 5 rules in external form:

  1. (=>(AND (OR (a) (b)) (OR (c) (d))) (AND (x) (AND (y) (z))))
  2. (=>(AND (OR (a) (b)) (OR (e) (f))) (AND (x) (AND (y) (z))))
  3. (=>(AND (OR (a) (b)) (OR (g) (h))) (AND (x) (AND (y) (z))))
  4. (=>(AND (OR (c) (d)) (OR (g) (h))) (AND (x) (AND (y) (z))))
  5. (=>(AND (OR (e) (f)) (OR (g) (h))) (AND (x) (AND (y) (z))))

You can see here several cases of duplicate, connected atom pairs and triplets, which is a strong hint that it is a candidate for factoring. Although the 5 rules look fairly compact, they result in 60 rules when normalized internally! That is, eliminating the antecedent ORs requires 4 rules for each of the 5 original rules (20) and eliminating the ANDed consequents requires 3 rules for each of the 5 original rules (20 x 3 = 60).

First factor out the common antecedent atoms and form four new rules for them:

These four rules normalize to eight.

Now factor the original consequents into a single atom, xyz:

This single rule results in three normalized rules.

Finally, use the derived atoms from the above factoring to chain to the original five rules, forming five replacement rules:

These five replacement rules require no further normalization. The total number of normalized rules required after this factoring is 16 (8 + 3 + 5), a significant reduction from the 60 required previously. Of course, this is a bit of a set up, with quite a few repeated atoms, significantly more than normal. On the other hand, you could easily encounter conduct sets that have even more repetition, where, without such factoring, you could have an exponential explosion of normalized rules, perhaps without realizing it.

Rules With Time Dependencies

The inferencing engine is and should be impervious to time. You should never build time directly into rules, but rather use sensors and the Time Adapter to determine dates, time, and time intervals. Each trigger event causes date and time facts, representing the arrival of that event to be added to the fact pool. These are automatic and are additions to the other short term facts that are carried by the trigger event. By using these facts and sensing for additional time or date information, you can make rules sensitive to time and overcome the timelessness of the inferencing engine.


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]