How to translate JBoss / DROOLS rules to CLIPS clp - jboss

I need to convert from rules defined as DROOLS .drl files to CLIPS .clp.
As I understand, it should be possible to convert DROOLS rules into ruleML, and ruleML into CLIPS .clp.
I understand well that the matter is more complex than converting between file formats. But as far as I dived into the matter (reading papers and info found on the net) it should be well possible. However, this is just not my domain (yet) and the matter is too complex for me to get going without practical advise and example.

If you're translating similar functionality between the two languages, then you could either write a program to do the translation or use search and replace preferably with grep expressions. Unless the program is very simple, you'll still have to do a good bit of work checking and tweaking the translation (which will require a working knowledge of both languages). For example, the code below is a rule translated from IRL (JRules) to CLIPS.
I'm not familiar with tools available for ruleML, but if there are some available that will do even a partial translation, that would be worthwhile to use to get started.
when
{
Phase ( name == "match" ) ;
Rank ( ?p : value; process == "yes" );
Technique ( name == "Hidden-Single"; rank == ?p ) ;
Possible ( ?v : value; ?r : row; ?pid : id );
not Possible ( value == ?v; row == ?r; id != ?pid ) ;
Possible ( ?v2: value; value != ?v; row == ?r; id == ?pid ) ;
not Impossible ( id == ?pid; value == ?v2; rank == ?p ) ;
}
then
{
insert Impossible() { id = ?pid; value = ?v2; rank = ?p; reason = "Hidden Single"; }
}
(defrule hidden-single-row
(phase match)
(rank (value ?p) (process yes))
(technique (name Hidden-Single) (rank ?p))
(possible (value ?v) (row ?r) (id ?id))
(not (possible (value ?v) (row ?r) (id ~?id)))
(possible (value ?v2&~?v) (row ?r) (id ?id))
(not (impossible (id ?id) (value ?v2) (rank ?p)))
=>
(assert (impossible (id ?id) (value ?v2) (rank ?p) (reason "Hidden Single"))))

Related

Drools: Using sub-queries in forall

I'm trying to essentially define an always finally (similar to CTL) query operation using Drools. The tree is composed of nodes (called Artifacts) that are annotated with a path id. Every split in the tree (a parent with more than one child) is represented by generating a new path id and inserting the fact SplitFrom(child, parent) to the knowledge base.
Essentially, we want to see if, from some starting path id, a given Artifact object exists on all paths in the tree. My attempt to o this is shown below:
query alwaysFinally( String $type, String $productName, long $parentPathId )
Artifact( type == $type, productName == $productName, pathId == $parentPathId )
or
forall( SplitFrom( parent == $parentPathId, $childPathId := child )
and
?alwaysFinally( $type, $productName, $childPathId; ) )
end
Unfortunately, this results in the following error:
java.lang.RuntimeException: Build Errors:
Error Messages:
Message [id=1, level=ERROR, path=edu/umn/crisys/sim/agent/cognition/futureworld/rules/process/antecedent/commonAntecedents.drl, line=47, column=0
text=[ERR 102] Line 47:6 mismatched input '?' in query]
Message [id=2, level=ERROR, path=edu/umn/crisys/sim/agent/cognition/futureworld/rules/process/antecedent/commonAntecedents.drl, line=0, column=0
text=Parser returned a null Package]
...
I've played with inserting parenthesis in a number of different ways, but I don't think that is the real problem. If I remove the and and replace it with a comma or a newline, I get the following error:
java.lang.ClassCastException: org.drools.core.rule.QueryElement cannot be cast to org.drools.core.rule.Pattern
at org.drools.compiler.rule.builder.ForallBuilder.build(ForallBuilder.java:57)
at org.drools.compiler.rule.builder.ForallBuilder.build(ForallBuilder.java:32)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:66)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:36)
at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:66)
at org.drools.compiler.rule.builder.RuleBuilder.build(RuleBuilder.java:97)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addRule(KnowledgeBuilderImpl.java:1820)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileRules(KnowledgeBuilderImpl.java:1111)
at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileAllRules(KnowledgeBuilderImpl.java:989)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.buildRules(CompositeKnowledgeBuilderImpl.java:260)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.buildPackages(CompositeKnowledgeBuilderImpl.java:121)
at org.drools.compiler.builder.impl.CompositeKnowledgeBuilderImpl.build(CompositeKnowledgeBuilderImpl.java:105)
at org.drools.compiler.kie.builder.impl.AbstractKieModule.buildKnowledgePackages(AbstractKieModule.java:243)
at org.drools.compiler.kie.builder.impl.AbstractKieProject.verify(AbstractKieProject.java:64)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildKieProject(KieBuilderImpl.java:230)
at org.drools.compiler.kie.builder.impl.KieBuilderImpl.buildAll(KieBuilderImpl.java:198)
I don't think accumulators will work here since a query is not a Pattern object.
Does anyone have a good idea of how to express this in Drools?
You don't need the forall. This is something that Drools does automatically for all matches, to continue the matching process. forall is the universal quantifier which you don't need here since the condition need not be true for all of a set of facts.
query alwaysFinally( String $type, String $productName, long $parentPathId )
Artifact( type == $type, productName == $productName, pathId == $parentPathId )
or
( SplitFrom( parent == $parentPathId, $childPathId := child )
and
?alwaysFinally( $type, $productName, $childPathId; )
end
It may result in multiple matches if the Artifact occurs more than once.
Logic rules won out. The latter part of the "or" statement can be represented as (using set notation)
let childPathIds: { SplitFrom(parent == $parentPathId, child := childPathId }
forall x in childPathIds, P(x)
Now, we can doubly negate and preserve identity. This gets us:
let childPathIds: { SplitFrom(parent == $parentPathId, child := childPathId }
exists x in childPathIds, not P(x)
Thus, it can be represented in dools using the following
accumulate( SplitFrom( parent == $parentPathId, $childPathId := child );
$childPaths: collectSet( $childPathId ) )
and
// Set non-empty
exists( $childPathId: Long() from $childPaths )
and
( not( exists( $childPathId: Long( ) from $childPaths
and
not (?alwaysFinally( $type, $productName, $childPathId; ) ) ) )
)
Notice the additional non-empty check? We need that because the 3rd condition will return true if $childPaths is empty.
So, moral of the story: Yes, we can capture forall with a subquery; however we have to use a much more clunky syntax to do it. At least we don't have to go down to Java to implement it.

Count before order, skip and take

I'm using Entity Framework together with Unit-of-work and repository pattern.
For a function with ordering, pagination, etc. I use the following code:
stammdatenEntityModels =
_unitOfWork.StammdatenRepository.Get()
.Where(
s =>
s.Geloescht == false &&
((s.Auftraggeber != null && s.Auftraggeber.Bezeichnung.ToLower().Contains(keyword)) ||
(s.SerienNummer.Contains(keyword)) ||
(s.Bezeichnung.ToLower().Contains(keyword)) ||
(s.StammdatenKunde != null && s.StammdatenKunde.Name.ToLower().Contains(keyword)) ||
(s.BeginnVos.HasValue && s.BeginnVos == dateTime) ||
(s.VosDauer != null && s.VosDauer.Bezeichnung.ToLower().Contains(keyword)) ||
(s.Geraetewert.HasValue && s.Geraetewert.Value.ToString().Contains(keyword))
))
.OrderBy(orderBy)
.Skip(inputModel.EntriesToDisplay*(inputModel.Page - 1))
.Take(inputModel.EntriesToDisplay)
.ToList();
Now I need to know the numbers of records, but before the skip and take (for pagination) is performed.
Therefore I have the same code again:
totalCount = _unitOfWork.StammdatenRepository.Get()
.Count(
s =>
s.Geloescht == false &&
((s.Auftraggeber != null && s.Auftraggeber.Bezeichnung.ToLower().Contains(keyword)) ||
(s.SerienNummer.Contains(keyword)) ||
(s.Bezeichnung.ToLower().Contains(keyword)) ||
(s.StammdatenKunde != null && s.StammdatenKunde.Name.ToLower().Contains(keyword)) ||
(s.BeginnVos.HasValue && s.BeginnVos == dateTime) ||
(s.VosDauer != null && s.VosDauer.Bezeichnung.ToLower().Contains(keyword)) ||
(s.Geraetewert.HasValue && s.Geraetewert.Value.ToString().Contains(keyword))
));
Unfortunately this leads to a lot of redundance and my query is performed twice. Is there any better solution?
I agree with the redundancy. What you can do is to cache the count result for the specific parameters (keyword, dateTime) a certain amount of time and use it again at subsequent calls to deliver paginated (skip, take) results from the StammDatenRepository.
This way you only have the overall count-call one time for specific parameters.
Found this SO question where a respected member states:
Thinking about it from a SQL point of view, I can't think of a way in
a single normal query to retrieve both the total count and a subset of
the data, so I don't think you will be able to do it in LINQ either.
So, I really think you have to cache some counts results to increase performance. You know best how to do it for your specific situation and if it's worth it at all...

What's Squeryl syntax for exclusion (i.e. != )?

Doing a simple Squeryl database lookup, but trying to exclude a value. I've tried:
j.id not jobExclude and j.id != jobExclude
however the first triggers a compiler error and the second triggers a runtime error.
The whole transaction:
from(DB.jobs)(j =>
where((j.startTime >= todayStart)
and (j.startTime <= todayEnd)
and (j.userId === userId)
and (j.teamId === teamId)
and (j.startOrder >= index)
and (j.id not jobExclude))
select (j)).toList
Thanks!
Courtesy of the Squeryl Groups:
Not equals is <>
so in the bigger picture:
(job.id <> jobExclude)
See http://squeryl.org/functions.html
Credit https://groups.google.com/forum/?fromgroups#!topic/squeryl/Hw7iVyvLLNM

How do I make this Query against EF Efficient?

Using EF4. Assume I have this:
IQueryable<ParentEntity> qry = myRepository.GetParentEntities();
Int32 n = 1;
What I want to do is this, but EF can't compare against null.
qry.Where( parent => parent.Children.Where( child => child.IntCol == n ) != null )
What works is this, but the SQL it produces (as you would imagine) is pretty inefficient:
qry.Where( parent => parent.Children.Where( child => child.IntCol == n ).FirstOrDefault().IntCol == n )
How can I do something like the first comparison to null that won't be generating nested queries and so forth?
Try this:
qry.Where( parent => parent.Children.Any( child => child.IntCol == n ));
Any in Linq translates to exists in sql.
If I understood Your queries correctly, this is what You need.

Problem writing LHS of Drools / JBoss Rules where I'm matching one fact and then using that fact to determine whether another fact exists

I'm using Drools (for the first time) to express some rules and it has been working really well so far. However I've been given a new condition that I'm not able to express in the rules language very clearly.
Essentially I need to perform an action on the players account if they have an outstanding balance on there account between a certain amount, where they haven't made a payment in the last week and where they haven't made a payment in the last 4 weeks that is greater than or equal to a weekly deduction. There are a few other rules but I've removed them in an effort to simplify the rule for this question. It's the last rule that is causing me a problem.
rule "The broken rule"
salience 10
no-loop
when
Player( $playerNumber : playerNumber )
$a : Account( // balance between £5 and £100 and no arrangement
playerNumber == $playerNumber &&
accountBalanceInPence >= 500 &&
accountBalanceInPence <= 10000
)
not ( // no payment in last week
exists AccountTransaction(
playerNumber == $playerNumber &&
transactionDate >= oneWeekAgo &&
transactionCode == "P" // payment
)
)
/* It's this next bit that is broken */
not ( // no payment > (weekly cost * 4) paid within last 4 weeks
$deduction : AccountTransaction( // a recent transaction
playerNumber == $playerNumber &&
transactionDate >= fourWeeksAgo &&
transactionCode == "D" // deduction
)
exists AccountTransaction( // the payment
playerNumber == $playerNumber &&
transactionDate >= fourWeeksAgo &&
transactionCode == "P" // payment
amountInPence >= ($deduction->amountInPence * 4)
)
)
then
// do some action to the account
end
The problem is that it just doesn't work, I keep getting org.drools.rule.InvalidRulePackage exceptions thrown. I was just guessing on the syntax but couldn't seem to find an example that showed what I'm trying to do. Is it even possible?
The full original error message is:
"unknown:50:3 mismatched token: [#255,1690:1695='exists',<39>,50:3]; expecting type RIGHT_PAREN[54,4]: unknown:54:4 mismatched token: [#284,1840:1852='amountInPence',<7>,54:4]; expecting type RIGHT_PAREN[54,22]: unknown:54:22 Unexpected token '$payment'"
After trying the suggestion in the first comment the error is:
"[50,3]: unknown:50:3 mismatched token: [#255,1690:1695='exists',<39>,50:3]; expecting type RIGHT_PAREN[54,4]: unknown:54:4 mismatched token: [#284,1840:1852='amountInPence',<7>,54:4]; expecting type RIGHT_PAREN[54,45]: unknown:54:45 mismatched token: [#293,1881:1881='*',<71>,54:45]; expecting type LEFT_PAREN[55,3]: unknown:55:3 mismatched token: [#298,1890:1890=')',<12>,55:3]; expecting type THEN"
yes as you guessed, you need to put an explicit "and" inside the "not" pattern to join them together.
The only time you don't need the "and" is at the top level:
eg
when Foo() Bar()
Doesn't require a an "and"
but this is implicitly the same as
when Foo() and Bar()
So your solution seems correct. The lack of a top level "and" seems to be convention in most rule languages (going back to CLIPS !)
After some more hacking around the following doesn't cause any runtime errors (though I'm not sure if it's "correct" yet). I rewrote the clause to put the exists around both the facts and used an infix and to group them.
not ( // no payment > (weekly cost * 4) paid within last 4 weeks
exists (
AccountTransaction( // a recent transaction
playerNumber == $playerNumber &&
transactionDate >= fourWeeksAgo &&
transactionCode == "D" // deduction
$recentDeducation : amountInPence
) and
AccountTransaction( // the payment
playerNumber == $playerNumber &&
transactionDate >= fourWeeksAgo &&
transactionCode == "P" // payment
amountInPence >= ($recentDeducation * 4)
)
)
)
Thanks for all the help so far.
What about ($deduction->amountInPence * 4)? I think, the -> should be a . instead.