This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-10-06
Channels
- # beginners (32)
- # boot (17)
- # cider (4)
- # clara (112)
- # cljs-dev (3)
- # cljsjs (2)
- # clojure (222)
- # clojure-germany (3)
- # clojure-greece (1)
- # clojure-italy (4)
- # clojure-losangeles (4)
- # clojure-russia (46)
- # clojure-spec (24)
- # clojure-uk (71)
- # clojurescript (78)
- # community-development (5)
- # component (88)
- # cursive (6)
- # datomic (7)
- # duct (5)
- # figwheel (2)
- # fulcro (21)
- # graphql (22)
- # leiningen (3)
- # luminus (9)
- # off-topic (1)
- # om (16)
- # onyx (46)
- # portkey (30)
- # re-frame (47)
- # reagent (5)
- # remote-jobs (1)
- # ring (12)
- # ring-swagger (13)
- # rum (1)
- # shadow-cljs (81)
- # spacemacs (1)
- # specter (33)
- # sql (2)
- # test-check (2)
- # vim (16)
- # yada (11)
I have a question about accumulators — is there a reason to do aggregation in a clara accumulator vs. in a RHS of something that uses all
to get the values?
@steve313 Technically there may be performance advantages in that you are allowing the engine to have potential optimizations to avoid doing unnecessary work. However, some of that would relate to how performant the accumulator implementation is.
Also, you keep things a bit more structured and declarative to pull as much out of the RHS as you can
However, there are times when the RHS may be justified just due to it being too complicated to try to fit into an accumulator. There is somewhat of a judgement call at times.
I am still early in the learning curve; think I figured out how to do the LHS relatively simply
One pattern that is easy to fall into, when new to trying to implement logic via rules, is to have a tendency to put a lot of code in the RHS
which sort of defeats the purpose. Accumulators are a bit of a more complex case, but in general it is often beneficial to try to keep the RHS as minimal as possible. Typically just “insert some facts”
I have pretty large tables of values… do people find that it’s often necessary to do some “fact” lookups outside of the engine?
Well, there are facts related to combinations of values (i.e. if a person is female, 18, and single, the value of some new fact should be X, but if they are male, 29, and married, the value of some new fact should be Y) — seems like I could use unification and declare each one of those mappings to be facts (->MyValue :female 18 :single X), (->MyValue :male 29 :married Y)
I’m sure I could generate rules from the table values but I’m not sure if that is a good approach
@steve313 I am trying to understand the situation still. It isn’t clear to me what a row of this table represents or what information it has
The table itself is structured like (k1, k2, k3, v1, v2, v3, v4, v5)… imagine something like that
k1 k2 k3 v1 v2 v3 v4 v5 v6 v7 v8
17 F S 2.1647 3.8128 2.9743 1.4490 2.7630 0.3837 3.4900 1.1400 3.1700
18 F S 2.8955 3.4350 1.4777 1.4490 2.4809 0.9656 2.9640 1.1058 2.6980
19 F S 2.2341 2.7573 1.3664 1.4490 2.2500 0.8800 2.5947 1.1058 2.3622
20 F S 1.8131 2.1821 1.9299 1.4490 1.7000 0.8900 2.2692 1.0944 2.0646
21 F S 1.7448 2.0645 1.9294 1.4490 1.6500 0.8800 2.2176 1.1286 2.0196
now, in the rules engine we have facts that can provide (k1, k2, k3) values and want to know what a v1 value for that combination is
However, I’m not too sure it would even be an issue to just put each row in as a fact
Clara will automatically hash on binding keys and use that hash-based lookup for equal-based joins
there are values that aren’t strict equality based for that. I can probably hack it so the engine would only use equality based
So if the majority are equal based you may be fine still. Not completely sure though.
But the non-eq-based table rows I’d put under a differnet fact type since that would ensure partitioning into groups like this
(r/defrule tester
[A (= ?id id)]
[B (= ?id id)]
[:or
<your P,E and P,F joins here using the result from A,B above>]
=>
<etc>)
Using :or
above makes it so the network will share the work done in the prior conditions in the rule before the :or
. So “node sharing”, as it is called in Rete terminology.
If you do it in separate rules, there still may be node sharing though. Clara tries to detect duplicate conditions.
Just pointing out times when :or
may be nice. It also lets you not write stuff twice.
and I’d still probably try it and profile/ take note of the runtime degradation before trying a non-fact based approach
Also, it’s good to be aware of your “hot spots”. A rule like
(r/defrule joining
[P (= ?age age)]
[F (< ?age (:threshold this))]
=>
<etc>)
Is going to evaluate (< ?age (:threshold this))
(number of P) X (number of F) timesso if that is a big number, then it becomes a hot spot and you may end up just being able to improve whatever test is done tehre
OK. So if many of these facts would be reused from case to case, is the right thing to do to load the standard facts (from the tables above), keep around a binding to the initial session, and then for each subsequent set of queries, reuse the initial session?
Or eliminate P
facts that you know won’t match F
(because they match something else with eq already perhaps).
assuming that works on account of persistent data structures etc., just don’t want to load stuff from files many times
you can reuse an “initially loaded” working memory state for those rules for different runs of new facts
There is some way to dedup on insert?
Example, if I insert (insert! ^{:type :foo}{:foo 33})
and then (insert! ^{:type :foo}{:foo 33})
, then querty all :foo
get just one?
@souenzzo if you’re using queries, you could put an accumulator or an :exists on your query conditions to remove duplication in your results
It's not about query... It's about session Duplicated facts are fire'ing rules in a loop..
Assuming you want to go from X
facts to B
facts, but do not want duplicates of B
“downstream”:
(r/defrule a-rule
[X (= ?v v)]
=>
(r/insert! (->Intermediate ?v)))
(r/defrule aggregate-rule
[?all <- (acc/all) :from [Intermediate]]
[:test (seq ?all)]
=>
;; Assume everything in `?all` is `=` otherwise do some merge, etc
(r/insert! (map->B (first ?all))))
(r/defrule b-rule
[B]
=>
<etc>)
This method is not working because I need acc
both Intermediate
and 'B`, to ensure that there are no repeated elements