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