[rules-dev] [accumulate] sum is always a double? What if it's only an int?

Mark Proctor mproctor at codehaus.org
Mon Jul 23 13:12:58 EDT 2007


Geoffrey De Smet wrote:
>
> With kind regards,
> Geoffrey De Smet
>
>
> Mark Proctor wrote:
>> accumulate still returns objects, as we don't allow patterns against 
>> primitives. maybe for special use cases we can allot $i : int() from 
>> accumulate (......) in the future.
>
> Would be nice, I am reading a 25% performance hit while switching to 
> accumulates and the int instead of double problem should only account 
> to 10%. However I still have to fine tune it and apply it to the 
> entire score calculation.
>
>>
>> Btw did you try the sequential engine mode in your stateless approach?
>
> No, what is it?
Sorry thought you where using stateless at the moment. Anyway here is 
the information for the future:
http://blog.athico.com/search/label/sequential
> But I am using a statefull working memory,
> as I make only 1 WM and use that for 100+ * 2000 move evaluations (for 
> small problems ;).
>
> Could playing with the sallience also make a difference as it might 
> tell drools to first create all logical Hops and only then start 
> summing their distance?
accumulate/collect can be very inneficient if you keep changing values, 
as it re-evaluates the entire set, so yes staging your operations might 
well help. You can use salience, but I would recommend you use 
agenda-groups if it is very simple, or ruleflow-groups if its more complex.
>
>>
>> Mark
>> Edson Tirelli wrote:
>>>
>>>     Geoffrey,
>>>
>>>     The "sum" function that is shipped with drools always uses 
>>> double internally, but all numeric accumulate functions must have 
>>> return type Number to avoid class cast exceptions... I must write 
>>> that in the docs... so, recommendation is to do:
>>>
>>>      Number( $total : intValue ) from accumulate(
>>>         Hop($distance : distance ), // distance is an int
>>>         sum($distance)
>>>      );
>>>
>>>     Now, having said that, the decision to use double is because we 
>>> needed a one size fits all to ship with drools. On the other hand I 
>>> tried to make as simple as possible to plug new or replace built-in 
>>> functions.  So, if you want a sum function that uses only integers, 
>>> you can easily develop and plug it your own. Since I just wrote the 
>>> docs on how to do it, may I ask you please to read them and provide 
>>> a feedback if they are good, need more info, shall I write them in 
>>> another way, etc?
>>>     BTW, when I say it is really simple to create another sum 
>>> function, I mean, you can do it in about 20 minutes. Really!
>>>
>>>     Find attached the HTML doc page I'm talking about.
>>>
>>>     []s
>>>     Edson
>>>
>>>
>>> 2007/7/22, Geoffrey De Smet < ge0ffrey.spam at gmail.com 
>>> <mailto:ge0ffrey.spam at gmail.com>>:
>>>
>>>     Hi guys,
>>>
>>>     I finally got around to experimenting with the accumulate 
>>> support in
>>>     LocalSearchSolver. Thanks for implementing it :)
>>>
>>>     I use nothing but int's in my calculations,
>>>     but I get a ClassCastExceptions, as sum() always returns a 
>>> Double and
>>>     setSoftConstraintsBroken(int) failes.
>>>
>>>     In a previous benchmarks I 've proven that summing integers in
>>>     double's
>>>     instead of int's hurts performance for 10% or more (which is
>>>     rather big).
>>>
>>>     Is there a sumInteger() available (or do I need to write it)? Or
>>>     can it
>>>     be overloaded over sum() in a much cleaner way?
>>>
>>>
>>>
>>>     rule "ScoreCalculator"
>>>        when
>>>          $scoreFact : ScoreFact(); // singleton
>>>          $total : Integer() from accumulate(
>>>             Hop($distance : distance ), // distance is an int
>>>             sum($distance)
>>>          );
>>>        then
>>>          $scoreFact.setSoftConstraintsBroken($total);
>>>     end
>>>
>>>
>>>     svn is still here, till it's ready for drools:
>>>     https://taseree.svn.sourceforge.net/svnroot/taseree/trunk
>>>
>>>     --
>>>     With kind regards,
>>>     Geoffrey De Smet
>>>
>>>     _______________________________________________
>>>     rules-dev mailing list
>>>     rules-dev at lists.jboss.org <mailto:rules-dev at lists.jboss.org>
>>>     https://lists.jboss.org/mailman/listinfo/rules-dev
>>>
>>>
>>>
>>>
>>> -- 
>>>   Edson Tirelli
>>>   Software Engineer - JBoss Rules Core Developer
>>>   Office: +55 11 3529-6000
>>>   Mobile: +55 11 9287-5646
>>>   JBoss, a division of Red Hat @ www.jboss.com <http://www.jboss.com>
>>> ------------------------------------------------------------------------ 
>>>
>>>
>>> 3.6. Advanced Conditional Elements
>>> Prev <ch03s05.html>      Chapter 3. The Rule Language      Next 
>>> <ch03s07.html>
>>>
>>> ------------------------------------------------------------------------ 
>>>
>>>
>>>
>>>     3.6. Advanced Conditional Elements
>>>
>>>
>>>       Note
>>>
>>> /|(updated to Drools 4.0)|/
>>>
>>> Drools 4.0 introduces a whole new set of conditional elements in 
>>> order to support full First Order Logic expressiveness, as well as 
>>> some facilities for handling collections of facts. This section will 
>>> detail the following new Conditional Elements:
>>>
>>>    *
>>>
>>>       from
>>>
>>>    *
>>>
>>>       collect
>>>
>>>    *
>>>
>>>       accumulate
>>>
>>>    *
>>>
>>>       forall
>>>
>>>
>>>       3.6.1. From
>>>
>>> The *from* Conditional Element allows users to specify a source for 
>>> patterns to reason over. This allows the engine to reason over data 
>>> not in the Working Memory. This could be a sub-field on a bound 
>>> variable or the results of a method call. It is a powerful 
>>> construction that allows out of the box integration with other 
>>> application components and frameworks. One common example is the 
>>> integration with data retrieved on-demand from databases using 
>>> hibernate named queries.
>>>
>>> The expression used to define the object source is any expression 
>>> that follows regular MVEL syntax. I.e., it allows you to easily use 
>>> object property navigation, execute method calls and access maps and 
>>> collections elements.
>>>
>>> Here is a simple example of reasoning and binding on another pattern 
>>> sub-field:
>>>
>>> rule "validate zipcode"
>>> when
>>>     Person( $personAddress : address )     Address( zipcode == 
>>> "23920W") from $personAddress then
>>>     # zip code is ok
>>> end
>>>  
>>> With all the flexibility from the new expressiveness in the Drools 
>>> engine you can slice and dice this problem many ways. This is the 
>>> same but shows how you can use a graph notation with the 'from':
>>>
>>> rule "validate zipcode"
>>> when
>>>     $p : Person( )     $a : Address( zipcode == "23920W") from 
>>> $p.address then
>>>     # zip code is ok
>>> end
>>>  
>>> Previous examples were reasoning over a single pattern. The *from* 
>>> CE also support object sources that return a collection of objects. 
>>> In that case, *from* will iterate over all objects in the collection 
>>> and try to match each of them individually. For instance, if we want 
>>> a rule that applies 10% discount to each item in an order, we could do:
>>>
>>> rule "apply 10% discount to all items over US$ 100,00 in an order"
>>> when
>>>     $order : Order()
>>>     $item  : OrderItem( value > 100 ) from $order.items
>>> then
>>>     # apply discount to $item
>>> end
>>>  
>>> The above example will cause the rule to fire once for each item 
>>> whose value is greater than 100 for each given order.
>>>
>>> The next example shows how we can reason over the results of a 
>>> hibernate query. The Restaurant pattern will reason over and bind 
>>> with each result in turn:
>>>
>>>
>>>       3.6.2. Collect
>>>
>>> The *collect* Conditional Element allows rules to reason over 
>>> collection of objects collected from the given source or from the 
>>> working memory. A simple example:
>>>
>>> import java.util.ArrayList
>>>
>>> rule "Raise priority if system has more than 3 pending alarms"
>>> when
>>>     $system : System()
>>>     $alarms : ArrayList( size >= 3 )
>>>               from collect( Alarm( system == $system, status == 
>>> 'pending' ) )
>>> then
>>>     # Raise priority, because system $system has
>>>     # 3 or more alarms pending. The pending alarms
>>>     # are $alarms.
>>> end
>>>  
>>> In the above example, the rule will look for all pending alarms in 
>>> the working memory for each given system and group them in 
>>> ArrayLists. If 3 or more alarms are found for a given system, the 
>>> rule will fire.
>>>
>>> The *collect* CE result pattern can be any concrete class that 
>>> implements tha java.util.Collection interface and provides a default 
>>> no-arg public constructor. I.e., you can use default java 
>>> collections like ArrayList, LinkedList, HashSet, etc, or your own 
>>> class, as long as it implements the java.util.Collection interface 
>>> and provide a default no-arg public constructor.
>>>
>>> Both source and result patterns can be constrained as any other 
>>> pattern.
>>>
>>> Variables bound before the *collect* CE are in the scope of both 
>>> source and result patterns and as so, you can use them to constrain 
>>> both your source and result patterns. Although, the /collect( ... )/ 
>>> is a scope delimiter for bindings, meaning that any binding made 
>>> inside of it, is not available for use outside of it.
>>>
>>> Collect accepts nested *from* elements, so the following example is 
>>> a valid use of *collect*:
>>>
>>> import java.util.LinkedList;
>>>
>>> rule "Send a message to all mothers"
>>> when
>>>     $town : Town( name == 'Paris' )
>>>     $mothers : LinkedList()                from collect( Person( 
>>> gender == 'F', children > 0 )                              from 
>>> $town.getPeople()                            )
>>> then
>>>     # send a message to all mothers
>>> end
>>>  
>>>
>>>       3.6.3. Accumulate
>>>
>>> The *accumulate* Conditional Element is a more flexible and powerful 
>>> form of *collect* Conditional Element, in the sense that it can be 
>>> used to do what *collect* CE does and also do things that *collect* 
>>> CE is not capable to do. Basically what it does is it allows a rule 
>>> to iterate over a collection of objects, executing custom actions 
>>> for each of the elements, and at the end return a result object.
>>>
>>> The general syntax of the *accumulate* CE is:
>>>
>>> /|<result pattern>|/ from accumulate( /|<source pattern>|/,
>>>                                   init( /|<init code>|/ ),
>>>                                   action( /|<action code>|/ ),
>>>                                   reverse( /|<reverse code>|/ ),
>>>                                   result( /|<result expression>|/ ) )
>>>  
>>> The meaning of each of the elements is the following:
>>>
>>>    *
>>>
>>>       *<source pattern>*: the source pattern is a regular pattern that
>>>       the engine will try to match against each of the source objects.
>>>
>>>    *
>>>
>>>       *<init code>*: this is a semantic block of code in the selected
>>>       dialect that will be executed once for each tuple, before
>>>       iterating over the source objects.
>>>
>>>    *
>>>
>>>       *<action code>*: this is a semantic block of code in the
>>>       selected dialect that will be executed for each of the source
>>>       objects.
>>>
>>>    *
>>>
>>>       *<reverse code>*: this is an optional semantic block of code in
>>>       the selected dialect that if present will be executed for each
>>>       source object that no longer matches the source pattern. The
>>>       objective of this code block is to "undo" any calculation done
>>>       in the <action code> block, so that the engine can do
>>>       decremental calculation when a source object is modified or
>>>       retracted, hugely improving performance of these operations.
>>>
>>>    *
>>>
>>>       *<result expression>*: this is a semantic expression in the
>>>       selected dialect that is executed after all source objects are
>>>       iterated.
>>>
>>>    *
>>>
>>>       *<result pattern>*: this is a regular pattern that the engine
>>>       tries to match against the object returned from the <result
>>>       expression>. If it matches, the *accumulate* conditional element
>>>       evaluates to *true* and the engine proceeds with the evaluation
>>>       of the next CE in the rule. If it does not matches, the
>>>       *accumulate* CE evaluates to *false* and the engine stops
>>>       evaluating CEs for that rule.
>>>
>>> It is easier to understand if we look at an example:
>>>
>>> rule "Apply 10% discount to orders over US$ 100,00"
>>> when
>>>     $order : Order()
>>>     $total : Number( doubleValue > 100 )              from 
>>> accumulate( OrderItem( order == $order, $value : value ),
>>>                               init( double total = 0; ),
>>>                               action( total += $value; ),
>>>                               reverse( total -= $value; ),
>>>                               result( total ) )
>>> then
>>>     # apply discount to $order
>>> end
>>>  
>>> In the above example, for each Order() in the working memory, the 
>>> engine will execute the *init code* initializing the total variable 
>>> to zero. Then it will iterate over all OrderItem() objects for that 
>>> order, executing the *action* for each one (in the example, it will 
>>> sum the value of all items into the total variable). After iterating 
>>> over all OrderItem, it will return the value corresponding to the 
>>> *result expression* (in the above example, the value of the total 
>>> variable). Finally, the engine will try to match the result with the 
>>> Number() pattern and if the double value is greater than 100, the 
>>> rule will fire.
>>>
>>> The example used java as the semantic dialect, and as such, note 
>>> that the usage of ';' is mandatory in the init, action and reverse 
>>> code blocks. The result is an expression and as such, it does not 
>>> admit ';'. If the user uses any other dialect, he must comply to 
>>> that dialect specific syntax.
>>>
>>> As mentioned before, the *reverse code* is optional, but it is 
>>> strongly recommended that the user writes it in order to benefit 
>>> from the /improved performance on update and retracts/.
>>>
>>> The *accumulate* CE can be used to execute any action on source 
>>> objects. The following example instantiates and populates a custom 
>>> object:
>>>
>>> rule "Accumulate using custom objects"
>>> when
>>>     $person   : Person( $likes : likes )
>>>     $cheesery : Cheesery( totalAmount > 100 )
>>>                 from accumulate( $cheese : Cheese( type == $likes ),
>>>                                  init( Cheesery cheesery = new 
>>> Cheesery(); ),
>>>                                  action( cheesery.addCheese( $cheese 
>>> ); ),
>>>                                  reverse( cheesery.removeCheese( 
>>> $cheese ); ),
>>>                                  result( cheesery ) );
>>> then
>>>     // do something
>>> end
>>>
>>>
>>>         3.6.3.1. Accumulate Functions
>>>
>>> The accumulate CE is a very powerful CE, but it gets real 
>>> declarative and easy to use when using predefined functions that are 
>>> known as Accumulate Functions. They work exactly like accumulate, 
>>> but instead of explicitly writing custom code in every accumulate 
>>> CE, the user can use predefined code for common operations.
>>>
>>> For instance, the rule to apply discount on orders written in the 
>>> previous section, could be written in the following way, using 
>>> Accumulate Functions:
>>>
>>> rule "Apply 10% discount to orders over US$ 100,00"
>>> when
>>>     $order : Order()
>>>     $total : Number( doubleValue > 100 )              from 
>>> accumulate( OrderItem( order == $order, $value : value ),
>>>                               sum( $value ) )
>>> then
>>>     # apply discount to $order
>>> end
>>>  
>>> In the above example, sum is an AccumulateFunction and will sum the 
>>> $value of all OrderItems and return the result.
>>>
>>> Drools 4.0 ships with the following built in accumulate functions:
>>>
>>>    *
>>>
>>>       average
>>>
>>>    *
>>>
>>>       min
>>>
>>>    *
>>>
>>>       max
>>>
>>>    *
>>>
>>>       count
>>>
>>>    *
>>>
>>>       sum
>>>
>>> These common functions accept any expression as input. For instance, 
>>> if someone wants to calculate the average profit on all items of an 
>>> order, a rule could be written using the average function:
>>>
>>> rule "Average profit"
>>> when
>>>     $order : Order()
>>>     $profit : Number()               from accumulate( OrderItem( 
>>> order == $order, $cost : cost, $price : price )
>>>                                average( 1 - $cost / $price ) )
>>> then
>>>     # average profit for $order is $profit
>>> end
>>>  
>>> Accumulate Functions are all pluggable. That means that if needed, 
>>> custom, domain specific functions can easily be added to the engine 
>>> and rules can start to use them without any restrictions. To 
>>> implement a new Accumulate Functions all one needs to do is to 
>>> create a java class that implements the 
>>> org.drools.base.acumulators.AccumulateFunction interface and add a 
>>> line to the configuration file or set a system property to let the 
>>> engine know about the new function. As an example of an Accumulate 
>>> Function implementation, the following is the implementation of the 
>>> "average" function:
>>>
>>> /*
>>>  * Copyright 2007 JBoss Inc
>>>  *  * Licensed under the Apache License, Version 2.0 (the "License");
>>>  * you may not use this file except in compliance with the License.
>>>  * You may obtain a copy of the License at
>>>  *  *      http://www.apache.org/licenses/LICENSE-2.0
>>>  *  * Unless required by applicable law or agreed to in writing, 
>>> software
>>>  * distributed under the License is distributed on an "AS IS" BASIS,
>>>  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
>>> implied.
>>>  * See the License for the specific language governing permissions and
>>>  * limitations under the License.
>>>  *
>>>  * Created on Jun 21, 2007
>>>  */
>>> package org.drools.base.accumulators;
>>>
>>>
>>> /**
>>>  * An implementation of an accumulator capable of calculating 
>>> average values
>>>  *  * @author etirelli
>>>  *
>>>  */
>>> public class AverageAccumulateFunction implements AccumulateFunction {
>>>
>>>     protected static class AverageData {
>>>         public int    count = 0;
>>>         public double total = 0;
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#createContext()
>>>      */
>>>     public Object createContext() {
>>>         return new AverageData();
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)
>>>      */
>>>     public void init(Object context) throws Exception {
>>>         AverageData data = (AverageData) context;
>>>         data.count = 0;
>>>         data.total = 0;
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object, 
>>> java.lang.Object)
>>>      */
>>>     public void accumulate(Object context,
>>>                            Object value) {
>>>         AverageData data = (AverageData) context;
>>>         data.count++;
>>>         data.total += ((Number) value).doubleValue();
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object, 
>>> java.lang.Object)
>>>      */
>>>     public void reverse(Object context,
>>>                         Object value) throws Exception {
>>>         AverageData data = (AverageData) context;
>>>         data.count--;
>>>         data.total -= ((Number) value).doubleValue();
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object) 
>>>
>>>      */
>>>     public Object getResult(Object context) throws Exception {
>>>         AverageData data = (AverageData) context;
>>>         return new Double( data.count == 0 ? 0 : data.total / 
>>> data.count );
>>>     }
>>>
>>>     /* (non-Javadoc)
>>>      * @see 
>>> org.drools.base.accumulators.AccumulateFunction#supportsReverse()
>>>      */
>>>     public boolean supportsReverse() {
>>>         return true;
>>>     }
>>>
>>> }
>>>  
>>> The code for the function is very simple, as we could expect, as all 
>>> the "dirty" integration work is done by the engine. Finally, to plug 
>>> the function into the engine, we added it to the configuration file:
>>>
>>> drools.accumulate.function.average = 
>>> org.drools.base.accumulators.AverageAccumulateFunction
>>>  
>>> Where "drools.accumulate.function." is a prefix that must always be 
>>> used, "average" is how the function will be used in the rule file, 
>>> and "org.drools.base.accumulators.AverageAccumulateFunction" is the 
>>> fully qualified name of the class that implements the function 
>>> behavior.
>>>
>>>
>>>       3.6.4. Forall
>>>
>>> *Forall* is the Conditional Element that completes the First Order 
>>> Logic support in Drools. The syntax is very simple:
>>>
>>> forall( /|<select pattern>|/ /|<constraint patterns>|/ )
>>>
>>> The *forall* Conditional Element will evaluate to true when all 
>>> facts that match the /|<select pattern>|/ match all the 
>>> /|<constraint patterns>|/. Example:
>>>
>>> rule "All english buses are red"
>>> when
>>>     forall( $bus : Bus( type == 'english')                    Bus( 
>>> this == $bus, color = 'red' ) )
>>> then
>>>     # all english buses are red
>>> end
>>>  
>>> In the above rule, we "select" all Bus object whose type is 
>>> "english". Then, for each fact that matchs this pattern we evaluate 
>>> the following patterns and if they match, the forall CE will 
>>> evaluate to true. Another example:
>>>
>>> rule "all employees have health and dental care programs"
>>> when
>>>     forall( $emp : Employee()
>>>             HealthCare( employee == $emp )
>>>             DentalCare( employee == $emp )
>>>           )
>>> then
>>>     # all employees have health and dental care
>>> end
>>>  
>>> Forall can be nested inside other CEs for complete expressiveness. 
>>> For instance, *forall* can be used inside a *not* CE:
>>>
>>> rule "not all employees have health and dental care"
>>> when     not forall( $emp : Employee()
>>>                 HealthCare( employee == $emp )
>>>                 DentalCare( employee == $emp )
>>>               )
>>> then
>>>     # not all employees have health and dental care
>>> end
>>>  
>>> As a side note, forall Conditional Element is equivalent to writing:
>>>
>>> not( /|<select pattern>|/ and not ( and /|<constraint patterns>|/ ) )
>>>
>>> Also, it is important to note that *forall is a scope delimiter*, so 
>>> it can use any previously bound variable, but no variable bound 
>>> inside it will be available to use outside of it.
>>>
>>> ------------------------------------------------------------------------ 
>>>
>>> Prev <ch03s05.html>      Up <ch03.html>      Next <ch03s07.html>
>>> 3.5. Rule      Home <title.html> | ToC <bk01-toc.html>      3.7. Query
>>>
>>> ------------------------------------------------------------------------ 
>>>
>>>
>>> _______________________________________________
>>> rules-dev mailing list
>>> rules-dev at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/rules-dev
>>>   
>>
>>
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> rules-dev mailing list
>> rules-dev at lists.jboss.org
>> https://lists.jboss.org/mailman/listinfo/rules-dev
>
> _______________________________________________
> rules-dev mailing list
> rules-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-dev
>




More information about the rules-dev mailing list