Version 16

    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"
                   Day(highest : temp)
                   not Day(temp > highest)



    Thanks to Mitch Christensen (mictchellch AT 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"
            $alert : Alert($oldest : timestamp)
            not Alert(timestamp < $oldest)
            // process the Alert in some application specific way
            // remove this Alert from working memory


    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"
           $d : Day($temp : temp)
           assert(new TryDay($d));
    rule "Highest unknown"
           $attempt : TryDay($d : day)
           not (Highest())
           assert (new Highest($d));
    rule "Highest lower"
           $highest: Highest($highDay : day)
           $attempt : TryDay($day : day -> ($day.getTemp() > $highDay.getTemp()))
    rule "Highest higher"
           Highest($highest: day)
           $attempt : TryDay($day : day -> ($day.getTemp() <= $highest.getTemp()))
    rule "Print highest"
       salience -10
           Highest($day : day)
           System.out.println("Highest day is " + $day);


    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?





    public class HeatRecord
        int temp;
        int dewPoint;

    A simple solution could be:

    rule "Filter" // examine each, and remove if exceeded by another
         salience 100
              HR1 : HeatRecord($t1:temp, $d1:dewPoint )
              HeatRecord( temp > $t1 ) || HeatRecord( temp == $t1, dewPoint > $d1  )
              retract( HR1 );
    rule "Report" // report on what is left
         salience 50
              HR1 : HeatRecord( )
              System.out.println("The Most Sweltering Conditions were: " + HR1.getTemp() +" "+ HR1.getDewPoint() ); 

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




    Mike Panihill