RulesFindMax

Sometimes you may want to find a fact with the highest value. You can do this using not, like the example below (you could also put it in a query if you like):

 

rule "Highest Temperature for a Day"
       when
               Day(highest : temp)
               not Day(temp > highest)
       then...

 

 

Thanks to Mitch Christensen (mictchellch AT comcast.net) for this suggestion.

 

Its possible that we may introduce some "sugar" syntax to make this more declarative (open to suggestions, paste below ideas).

 

-


Ordered Processing of Facts

 

A similar approach is generally used for doing ordered processing across a set of related facts.  For example, to process a set of Alert facts in the order specified by their timestamp property you could do the following:

 

rule "Process Alerts by time received"
    when
        $alert : Alert($oldest : timestamp)
        not Alert(timestamp < $oldest)
    then
        // process the Alert in some application specific way
        $alert.process()

        // remove this Alert from working memory
        retract($alert);
end

 

This rule will process all Alert objects in working memory in the order in which they were generated (according to the timestamp property).  As each Alert fact is processed, it is removed from working memory, and the next oldest Alert is processed next.

 

-Mitch Christensen

 

-


The Expert Systems book (Expert Systems: Principles and Programming, 4th ed., Giarratano & Riley) suggests that whilst the pattern Mitch

provided is the simplest it is not the most efficient. For N days

there would be N-squared comparisons will need to be made to find the

partial match. So if you have a large data set you may want to use the

following which should cut the partial matches down to 2N. I ran this

with 1000 days and got 2000 milliseconds for the simple rule and 234

milliseconds for the following rule. For 2000 days it was 8110 ms

versus 375 milliseconds.

 

TryDay and Highest are just marker classes that have one property - day.
rule "Try day"
   when
       $d : Day($temp : temp)
   then
       assert(new TryDay($d));
end

rule "Highest unknown"
   when
       $attempt : TryDay($d : day)
       not (Highest())
   then
       assert (new Highest($d));
end

rule "Highest lower"
   when
       $highest: Highest($highDay : day)
       $attempt : TryDay($day : day -> ($day.getTemp() > $highDay.getTemp()))
   then
       $highest.setDay($day);
       modify($highest);
end

rule "Highest higher"
   when
       Highest($highest: day)
       $attempt : TryDay($day : day -> ($day.getTemp() <= $highest.getTemp()))
   then
       retract($attempt);
end
rule "Print highest"
   salience -10
   when
       Highest($day : day)
   then
       System.out.println("Highest day is " + $day);
end

 

Steven Williams

 

-


Re: Highest Value Fact

My first thought at a solution was to compare each val to some temporary holder, and set the holder to the val if the val is greater.  That turns out to be TERRIBLE.

 

Mitch's solution has the advantage of ease of understanding.  Important if your rule definer  is closer to a Biz Analyst than an Engineer.

 

Steven's  obviously kicks butt in performance.

 

Mike Panihill

 

-


More on 'Finding Max'.

 

The problem of 'finding max' is often not restricted to a single field or attribute of the objects under consideration.

 

 

 

And while we are at it, let us abstract it to 'find best' or 'find optimal' pattern, which should be very applicable to many real-world situations.  Best investment opportunity, best network node, best insurance policy...

 

 

 

Such problems have two characteristics that make a rules-based approached tempting.  One, the 'goodness' criteria are subject to frequent change, and rules allow us to redefine that w/out extending our code base (e.g., the 'best new home for someone with children may not be the best for someone without kids).  Two, 'goodness' can be complicated affair, often hard to capture in English, let alone in a procedural programming language.

 

 

 

The solution above inspired by Giarratano & Riley, while being much more efficient, will grow very rapidly once more attributes are added.

 

 

 

Imagine if our task was not just to find the highest temp for a day, but, assuming there can be duplicate temps, the highest temp with the highest dewPoint?

 

 

 

Consider

public class HeatRecord
{
    int temp;
    int dewPoint;
    .
    .
}

A simple solution could be:

rule "Filter" // examine each, and remove if exceeded by another
     salience 100
     when
          HR1 : HeatRecord($t1:temp, $d1:dewPoint )
          HeatRecord( temp > $t1 ) || HeatRecord( temp == $t1, dewPoint > $d1  )
     then 
          retract( HR1 );
end

rule "Report" // report on what is left
     salience 50
     when
          HR1 : HeatRecord( )
     then
          System.out.println("The Most Sweltering Conditions were: " + HR1.getTemp() +" "+ HR1.getDewPoint() ); 
end

If we were to try the Giarratano & Riley approach, how big would it be?  How maintainable?

 

Thoughts?

 

Mike Panihill