Peering into RetePlus

July 25, 2016

 

 

“Is a rule different from an if-statement in Java?” My daughter, a summer intern at my company, asked this question that almost all Java developers do when first introduced to rules. The answer, of course, is that they are vastly different. And those differences reveal the very heart of a rule engine.

 

I explained to her that even though it is common to think of rules as an “if-then” statement, it is more accurate to think of them as “when-then” statements. Whenever the conditions of a rule are satisfied, the rule actions are executed. Unlike procedural languages where you have to explicitly specify the sequence of operations, business rules rely on an inference engine (or rule engine) to determine when to fire rules. This is a delegation of responsibility that comes as a bit of a surprise.

To illustrate this point, I asked her to think of how you could compute the factorial of an integer N using rules. After turning a few gears in her head, she came up with these rules:

Rule 1:

 

If

              N > 1

Then

              Factorial = factorial * N;

              N = N -1;

 

Rule 2:

 

 If

              N == 1

Then

              Stop;

Immediately, she could see that Rule 1 was more of a “when – then” that could fire multiple times until the condition is no longer true. After a little reflection, she also realized that Rule 2 wasn’t really a rule since the rule engine will automatically stop whenever there are no more rules to fire.

How about when N is less than 1? We agreed on its illegality, and that she would return a value of -1. The next rule looked like this:

Rule 3:

 

If

              N < 1

Then

              Factorial = -1;

Implementation

At this point, she was eager to implement these rules.  After creating the rule project, she setup the ruleset parameters with an input integer (called ‘the input’) and the output factorial (called ‘the factorial’).

When she started defining Rule 1, her first realization was that if the input value was to modified within an action rule, it needed to be specified as an IN_OUT parameter.

 

The rules in ODM looked like this:

 

Factorial Calculation Rule:

if

      'the input' is more than 1

then

      set 'the factorial' to 'the factorial' * 'the input' ;

      set 'the input' to 'the input' - 1;

      print "Intermediate Factorial value = " + 'the factorial' ;

 

Invalid Input Rule:

if

      'the input' is less than 0

then

      print "Invalid input of " + 'the input' ;

      set 'the factorial' to -1;

 

She quickly created a simple ruleflow, and for good measure added a final action on the rule task to print out the calculated factorial. When she executed the rules using a Rule Project Configuration, she got her first surprise - the output looked like this:

 

The input is 5

Intermediate Factorial value = 5

The factorial is 5

 

Why did the rule only fire once? After a discussion on rule algorithms, she discovered RetePlus: the algorithm to be used whenever changes in the working memory needed to reactivate rules. By default, the rule task uses Fastpath as the execution algorithm.

 

After switching from Fastpath to RetePlus, the output was yet another disappointment:

 

The input is 5

Intermediate Factorial value = 5

The factorial is 5

 

What could be the issue? This problem brought us to an explanation of the refraction principle. The refraction principle used by RetePlus states that a rule instance that has been executed cannot be reinserted into the agenda if no new fact has occurred, that is, if none of the objects matched by the rule are modified.

 

The conditions in our rule did not change when the value of ‘the input’ changed. The fact “'the input' is more than 1” was unchanged. To get the rule to fire multiple times, the rule should have a binding to the changed value. Now for an ODM trick: this is attained by introducing a definition statement:

 

definitions

      set counter to 'the input';

if

      counter is more than 1

then

      set 'the factorial' to 'the factorial' * 'the input' ;

      set 'the input' to 'the input' - 1;

      print "Intermediate Factorial value = " + 'the factorial' ;

 

Again, to her mild frustration, the execution stopped at the first rule firing. Now this is where I had to dive deeper into the ODM implementation of RetePlus. Looking at the IRL view, you could see:

 

      when {

         IlrContext() from ?context;

         evaluate (counter : (input));

         evaluate (counter.intValue() > 1);

      } then {

 

There was no explicit variable binding outside of the evaluations. This requires a subtle change. The definition should be:

 

definitions

      set counter to a number from 'the input' ;

 

With everything else in the rule remaining the same, the result finally became:

 

The input is 5

Intermediate Factorial value = 5

Intermediate Factorial value = 20

Intermediate Factorial value = 60

Intermediate Factorial value = 120

The factorial is 120

 

Voila!

 

This simple example illustrates not only how rules are different from procedural code - with the control delegated to the inference engine - but also how ODM's RetePlus execution mode can be powerful but sometimes tricky.

 

 

 

Please reload

Featured Posts

ODM’s Achilles’ Heel

September 2, 2015

1/1
Please reload

Recent Posts

September 2, 2015

Please reload

Archive
Please reload

Search By Tags
Follow Us
Please reload

  • Facebook Basic Square
  • Twitter Basic Square
  • Google+ Basic Square