@eraserhd Agreed with @mikerod that the only facts which will be retracted are those that were provided before. Regarding the inefficiencies of accumulation with non-hash join conditions, I think that’s a more general problem than the :retract-fn actually. For example, I believe when a new fact matching the accumulation condition is added the previous facts will have the accumulator rerun on them in their entirety, whereas the simple case with no joins or only-hash based joins will store the previous result and add onto it. Contrast the following in AccumulateNode, the simple case: https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/engine.cljc#L1156 with the equivalent in AccumulateWithJoinFilterNode, the more complex case: https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/engine.cljc#L1525
The tricky thing is that Clara’s memory is set up to store the elements (facts coming into the current condition) and tokens (facts from ancestor conditions) separately, but in the case of these complex accumulator joins you’d really want to store them together for maximum efficiency. I don’t think there is any fundamental reason why this couldn’t be done though, it
would just be the work to do it and get it right/performant, plus durability changes probably if the memory structure changed
Clara does avoid downstream thrash from the complex accumulators still when the result didn’t actually change - see https://github.com/cerner/clara-rules/issues/182 and the changes made there - but the internal calculation has some room for improvement perf-wise
If you want to understand how the accumulator logic works, some basic terms:
-right-activate: new facts matching the condition at hand
-right-retract: removal of facts matching the condition at hand
-left-activate: facts were added to ancestor conditions and need to be passed down the network
left-retract: facts were removed from ancestor conditions and need to be passed down the network
AccumulateNode - simple no-condition or hash-join-only accumulation conditions
AccumulateWithjoinFilterNode - accumulation condition with arbitrary filter logic taking facts from ancestor conditions as input
probably the best source of truth there is the code, fortunately it is a well commented area imo though i’m biased as i wrote many of those comments
to be clear: when I say to understand how it works, i mean the algorithms at play - the docs should be sufficient for the behavior and if they’re not they should be improved
hopefully this helps
if you’re trying to get a case with complex accumulator joins to perform better, frankly my first reaction would be to use salience most likely
if you’re using truth maintenance without side effects, the salience shouldn’t impact actual behavior so it doesn’t make the code harder to read/understand in that respect
@wparker thanks! This is all good info.
I'm guessing, in [Foo (= ?a a)] [?acc <- (acc/whatever) [Bar (= ?a a)]], Foo would be left and Bar would be right?
@eraserhd basically yes
left is the matched-token-so-far in a rule LHS
tokens consist of all facts involved in a match
In a LHS [A] [B] [C]
if we were looking at the network node for the [C]
condition, incoming C
facts would be right-activate
, left-activate
to this node would be matched up A
+`B`s - represented as a “token”
And for the [B] condition, incoming B facts would be right-activate, left-activate would be tokens passed down from the [A] condition. For the [A] condition the right-activate would be incoming A facts, the left-activate would basically be a dummy placeholder. Does that make sense?