Duetto
Duetto

Reputation: 19

how do i prevent a rule from firing until asserts are complete?

this a fup to a post i made about using the results from a query in a rule. based on laune's response, i dropped the query. i re-thought the problem. the seniority comes into play only if multiple members request the same dock. so i wrote a rule, "id-candidate-bids", to assert a new set of facts comprised of members who request a given dock. i then apply the "find-senior" rule.

the "id-candidate-bids" works as intended and creates the facts. however, the rule, "find-senior" is not always correctly identifying the most senior. based on testing, varying the order of the deffacts for bids, i get a correct answer or a wrong answer. the print output of the rules show that the candidate rule fires, followed by the senior rule. since there would only be one candidate at that point, the candidate is most senior. subsequent additions to the candidate fact base then yield another most senior if the most senior candidate was not added first to the fact base.

i understand that the rule engine is constantly firing and reasons based on the fact base at any moment, so the question is how do i account for the timing issue ..add a boolean to prevent firing until all candidates are asserted..?

thanks,

duetto

(deffacts bids
  (bid (person Joe) (slipRequestedID A13) (boatID FarNiente))
  (bid (person John) (slipRequestedID A13) (boatID GEM))
  (bid (person Frank) (slipRequestedID B9) (boatID DoryO)))

(defrule id-candidate-bids
  (bid (slipRequestedID ?sid)(person ?p)(boatID ?b))
  (slip (slipID ?sid))
  (person (name ?p) (bycseniority ?s))
    => 
  (assert (candidatebid (person ?p)(seniority ?s) (slipRequestedID ?sid)))
  (printout t ?p " seniority # is " ?s crlf))
  
(defrule find-senior
  (candidatebid (person ?p)(seniority ?s1))
  (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
  (printout t ?p " is most senior" crlf))
  
(reset)
(run)  

Upvotes: 0

Views: 73

Answers (2)

Gary Riley
Gary Riley

Reputation: 10757

The simplest solution is to assign a salience to the rule:

(defrule find-senior
   (declare (salience -10))
   (candidatebid (person ?p)(seniority ?s1))
   (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
   (printout t ?p " is most senior" crlf))

If you have a large number of rules, it's a better idea to declare all of the salience values in one place using global variables and then use the variables when assigning a salience to a rule:

(defglobal ?*initially* =  10
           ?*exception* =  5
           ?*otherwise* = -5
           ?*finally*   = -10)
           
(defrule find-senior
   (declare (salience ?*finally*))
   (candidatebid (person ?p)(seniority ?s1))
   (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
   (printout t ?p " is most senior" crlf))

You can also constrain find-senior rule by adding patterns so that it won't fire until all of the candidate bids have been asserted the id-candidate-bids rule, but this can overly complicate the rule especially if there are multiple rules that must be allowed to fire first:

(defrule find-senior
   (forall (and (bid (slipRequestedID ?sid)(person ?p)(boatID ?b))
                (slip (slipID ?sid))
                (person (name ?p) (bycseniority ?s)))
           (candidatebid (person ?p)))
   (candidatebid (person ?p)(seniority ?s1))
   (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
   (printout t ?p " is most senior" crlf))

You can use facts to represent the current phase of execution and have a low salience rule change the phase when there are no longer any rules to execute in the current phase:

(deffacts rule-sequencings
   (phases process pick))

(defrule id-candidate-bids
  (phases process $?)
  (bid (slipRequestedID ?sid)(person ?p)(boatID ?b))
  (slip (slipID ?sid))
  (person (name ?p) (bycseniority ?s))
    => 
  (assert (candidatebid (person ?p)(seniority ?s) (slipRequestedID ?sid)))
  (printout t ?p " seniority # is " ?s crlf))
  
(defrule find-senior
  (phases pick $?)
  (candidatebid (person ?p)(seniority ?s1))
  (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
  (printout t ?p " is most senior" crlf))

(defrule next-phase
  (declare (salience -10))
  ?f <- (phases ?current ?next $?rest)
  =>
  (retract ?f)
  (assert (phases ?next ?rest)))

Finally, you can use defmodules which were designed to allow you to sequence the order in which groups of rules are executed:

(defmodule MAIN (export ?ALL)

;; Deftemplate and deffacts definitions shared
;; by all modules would be placed here.

(defrule start
   =>
   (focus PROCESS PICK))

(defmodule PROCESS (import MAIN ?ALL))

(defrule id-candidate-bids
  (bid (slipRequestedID ?sid)(person ?p)(boatID ?b))
  (slip (slipID ?sid))
  (person (name ?p) (bycseniority ?s))
    => 
  (assert (candidatebid (person ?p)(seniority ?s) (slipRequestedID ?sid)))
  (printout t ?p " seniority # is " ?s crlf))

(defmodule PICK (import MAIN ?ALL))

(defrule find-senior
  (candidatebid (person ?p)(seniority ?s1))
  (not (candidatebid (person ~?p) (seniority ?s2 &:(< ?s2 ?s1))))  
   => 
  (printout t ?p " is most senior" crlf))

Upvotes: 0

laune
laune

Reputation: 31300

You could add salience to the rules, enticing the id-candidate-bids rule to fire until no more matches are available. Then, all candidatebid facts will be in WM, and find-senior will find the oldest bidder.

But using salience should not be used except as a last resort. It's better to use combinations of constraints. The following rule associates a slip with a bid and matching bidder if there is not another bid for the same slot from another person that has a higher seniority.

(defrule id-candidate-bids
  (slip (slipID ?sid))
  (bid (slipRequestedID ?sid)(person ?p1)(boatID ?b1))
  (person (name ?p1) (bycseniority ?s1))
  (not (bid (slipRequestedID ?sid)(person ?p2 ~?p1)(boatID ?b2))
       (person (name ?p2) (bycseniority ?s2 :(> ?s2 ?s1))) )
  =>
  printout t ?sid " to " ?p1 " with " ?b1 crlf)

Warning: I don't have Jess for testing, and I wrote my last Jess code 10 years ago.

Edit: Another way would be to retract each bid after being processed by rule id-candidate-bids and add a corresponding constraint (non-existence of any bid) to the second rule. But this may not be suitable for the complete solution.

Upvotes: 0

Related Questions