clara

http://www.clara-rules.org/
2018-11-02T11:56:12.092500Z

@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

đź‘Ť 1
2018-11-02T11:58:11.095Z

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

2018-11-02T11:58:40.095600Z

would just be the work to do it and get it right/performant, plus durability changes probably if the memory structure changed

2018-11-02T12:00:22.097400Z

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

2018-11-02T12:03:07.098300Z

If you want to understand how the accumulator logic works, some basic terms:

2018-11-02T12:03:26.098700Z

-right-activate: new facts matching the condition at hand

2018-11-02T12:03:46.099100Z

-right-retract: removal of facts matching the condition at hand

2018-11-02T12:04:04.099600Z

-left-activate: facts were added to ancestor conditions and need to be passed down the network

2018-11-02T12:04:39.100300Z

left-retract: facts were removed from ancestor conditions and need to be passed down the network

2018-11-02T12:05:10.101Z

AccumulateNode - simple no-condition or hash-join-only accumulation conditions

2018-11-02T12:05:56.102100Z

AccumulateWithjoinFilterNode - accumulation condition with arbitrary filter logic taking facts from ancestor conditions as input

2018-11-02T12:09:33.105200Z

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

2018-11-02T12:10:15.106100Z

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

2018-11-02T12:10:28.106400Z

hopefully this helps

2018-11-02T12:11:06.107Z

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

2018-11-02T12:12:02.108100Z

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

eraserhd 2018-11-02T13:44:12.108600Z

@wparker thanks! This is all good info.

eraserhd 2018-11-02T13:48:39.109700Z

I'm guessing, in [Foo (= ?a a)] [?acc <- (acc/whatever) [Bar (= ?a a)]], Foo would be left and Bar would be right?

2018-11-02T15:46:35.110Z

@eraserhd basically yes

2018-11-02T15:46:47.110300Z

left is the matched-token-so-far in a rule LHS

2018-11-02T15:47:13.110700Z

tokens consist of all facts involved in a match

2018-11-02T15:48:26.112Z

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”

2018-11-02T17:24:35.114300Z

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?

đź‘Ť 2