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

Geoffrey De Smet ge0ffrey.spam at gmail.com
Mon Jul 23 12:53:14 EDT 2007


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?
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?

> 
> 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




More information about the rules-dev mailing list