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

Geoffrey De Smet ge0ffrey.spam at gmail.com
Sun Jul 22 15:45:40 EDT 2007


Thanks Edson,

I 'll write sumInteger() function, as the performance impact of summing 
double's is to big.
However, I probably only have time next weekend to continue my work on this,
I 'll provide feed-back on the docs then too.

With kind regards,
Geoffrey De Smet


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




More information about the rules-dev mailing list