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.
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(a)gmail.com
> <mailto:ge0ffrey.spam@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(a)lists.jboss.org <mailto:rules-dev@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(a)lists.jboss.org
>
https://lists.jboss.org/mailman/listinfo/rules-dev
>
------------------------------------------------------------------------
_______________________________________________
rules-dev mailing list
rules-dev(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/rules-dev