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