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

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)

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.

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))

Related

clips how to make rule match forall but one

How could i make a rule that test if all facts from a deftemplate but one matches an specific condition?
Example: with
(deftemplate person (field name)(field hair-color))
having several blonde people, only one is not
get
(printout t "Only " ?name-not-blond " is not blonde" crlf)
CLIPS (6.4 2/9/21)
CLIPS>
(deftemplate person
(slot name)
(slot hair-color))
CLIPS>
(defrule only-one-not-blonde
(person (name ?name-not-blonde)
(hair-color ~blonde))
(not (person (name ~?name-not-blonde)
(hair-color ~blonde)))
=>
(println "Only " ?name-not-blonde " is not blonde."))
CLIPS>
(deffacts initial
(person (name Sue) (hair-color blonde))
(person (name Frank) (hair-color blonde))
(person (name Josh) (hair-color brown)))
CLIPS> (reset)
CLIPS> (facts)
f-1 (person (name Sue) (hair-color blonde))
f-2 (person (name Frank) (hair-color blonde))
f-3 (person (name Josh) (hair-color brown))
For a total of 3 facts.
CLIPS> (agenda)
0 only-one-not-blonde: f-3,*
For a total of 1 activation.
CLIPS> (assert (person (name Anne) (hair-color red)))
<Fact-4>
CLIPS> (agenda)
CLIPS>

CLIPS sister, predecesor and age rules

I am with the following CLIPS programming problem:
New family relationship knowledge base. Please apply the following specification:
use deftemplate to define the person
a person’s attributes are: name, age, sex, children,
each attribute should be defined as a slot except children, that are multi slot
in sex slot use the (allowed-symbols female male)
add data type to each slot
use deffacts to add people from diagram
define rules:
—mother,
—father,
—sister,
—predecessor
—old — giving the difference in age between predecessor and offspring
This is the code I made:
(deftemplate person
(slot name (type SYMBOL))
(slot age (type INTEGER))
(slot sex (type SYMBOL) (allowed-symbols female male))
(multislot children (type SYMBOL))
)
(deffacts init
(person (name Fernando) (age 55) (sex male) (children Jesus Celia))
(person (name Maria) (age 50) (sex female) (children Jesus Celia))
(person (name Jesus) (age 25) (sex male))
(person (name Celia) (age 20) (sex female))
(person (name Antonio) (age 70) (sex male) (children Fernando Ana))
(person (name Ana) (age 38) (sex female))
(person (name Calra) (age 68) (sex female) (children Fernando Ana))
)
(defrule mother
(person (name ?name) (sex female) (children $?before ?child $?after))
=>
(assert (mother ?name ?child))
(printout t ?name " is mother of " $?child crlf))
(defrule father
(person (name ?name) (sex male) (children $?before ?child $?after))
=>
(assert (father ?name ?child))
(printout t ?name " is father of " $?child crlf))
(defrule sister
(children ?z ?x)
(children ?z ?y)
(female ?x)
(not (test (eq ?x ?y)))
=>
(assert sister ?x ?y))
(printout t ?x "is a sister to " ?y crlf))
(defrule predecessor
(or
(children ?x ?y)
(and (children ?x ?z)(predecessor ?z ?y))
)
=>
(assert (predecessor ?x ?y)
(printout t ?x "is a predecessor to " ?y crlf))
(defrule old
?fact_no <- (person (name ?n) (age ?a1 ?a2))
(test (<= ?a1 ?a2))
=>
(assert (result (- ?a1 ?a2)))
(printout t "There are " ?result " years" crlf))
The mother and father relationship is working but not the rest. It gives me the following ERRORS:
CLIPS> Loading Selection...
[CSTRCPSR4] Cannot redefine deftemplate person while it is in use.
ERROR:
(deftemplate MAIN::person
[CSTRCPSR1] WARNING: Redefining deffacts: init
[CSTRCPSR1] WARNING: Redefining defrule: mother +j+j
Defining defrule: father +j+j
[CSTRCPSR1] WARNING: Redefining defrule: sister
[PRNTUTIL2] Syntax Error: Check appropriate syntax for RHS patterns.
ERROR:
(defrule MAIN::sister
(children ?z ?x)
(children ?z ?y)
(female ?x)
(not (test (eq ?x ?y)))
=>
(assert sister
Defining defrule: predecessor
[EXPRNPSR3] Missing function declaration for defrule.
ERROR:
(defrule MAIN::predecessor
(or (children ?x ?y)
(and (children ?x ?z)
(predecessor ?z ?y)))
=>
(assert (predecessor ?x ?y)
(printout t ?x "is a predecessor to " ?y crlf))
(defrule
Thank you so much in advance
If you already have code loaded, issue a clear command before you try to load the same code a second time. That will remove the [CSTRCPSR4] error message.
You have missing parentheses in the sister and predecessor rules. Correcting these will allow the rules to load without errors.
(defrule sister
(children ?z ?x)
(children ?z ?y)
(female ?x)
(not (test (eq ?x ?y)))
=>
(assert (sister ?x ?y))
(printout t ?x "is a sister to " ?y crlf))
(defrule predecessor
(or
(children ?x ?y)
(and (children ?x ?z)(predecessor ?z ?y))
)
=>
(assert (predecessor ?x ?y))
(printout t ?x "is a predecessor to " ?y crlf))
These rules will still not execute with your existing code because they require children and female facts and none of your rules assert these facts.
It's not clear what you're trying to do with your old rule, but since you're trying to match two values in the age slot this rule will generate errors because that slot can only contain one value.

CLIPS: check more than one fact

Let's consider the next trivial template:
(deftemplate person (ssn ?s))
I want to check that, if a person "is registered", there are no other person with same ssn, however, I've tried with something like:
(defrule repeated-person
(person (ssn ?s1))
(person (ssn ?s2))
(test (= ?s1 ?s2))
=>
(printout t "No, no, no..." clrf))
or even,
(defrule repeated-person
(person (ssn ?s))
(person (ssn ?s))
=>
(printout t "No, no, no..." clrf))
but it didn't work.
How can I accomplish something like that?
By default, you can't create duplicates of facts:
CLIPS (6.31 2/3/18)
CLIPS>
(deftemplate person
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS> (assert (person (SSN 123-45-6789)))
FALSE
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS>
You can change this behavior using the set-fact-duplication function:
CLIPS> (set-fact-duplication TRUE)
FALSE
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
f-2 (person (SSN 123-45-6789))
For a total of 3 facts.
CLIPS>
You can then write a rule which checks to see if there are two different facts with the same SSN:
CLIPS>
(defrule repeated-person
?f1 <- (person (SSN ?ss))
?f2 <- (person (SSN ?ss))
(test (< (fact-index ?f1) (fact-index ?f2)))
=>
(printout t "Duplicated SSN " ?ss crlf))
CLIPS> (agenda)
0 repeated-person: f-1,f-2
For a total of 1 activation.
CLIPS>
Since each fact has a unique fact index, the comparison in the test conditional element ensures that the facts matching the first and second patterns are not the same.
If we add another person with an identical SSN, we'll get multiple activations of the rule:
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (agenda)
0 repeated-person: f-1,f-3
0 repeated-person: f-2,f-3
0 repeated-person: f-1,f-2
For a total of 3 activations.
CLIPS>
We can dynamically assign a unique id to each created fact which allows to create "duplicate" facts even when facts duplication is disabled:
CLIPS> (clear)
CLIPS> (set-fact-duplication FALSE)
TRUE
CLIPS>
(deftemplate person
(slot id (default-dynamic (gensym*)))
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (id gen1) (SSN 123-45-6789))
f-2 (person (id gen2) (SSN 123-45-6789))
f-3 (person (id gen3) (SSN 123-45-6789))
For a total of 4 facts.
CLIPS>
We can then create a rule which prints a single message regardless of the number of people with the same SSN:
CLIPS>
(defrule repeated-person
(person (id ?id) (SSN ?ssn))
(not (person (id ?id2&:(< (str-compare ?id2 ?id) 0)) (SSN ?ssn)))
(exists (person (id ~?id) (SSN ?ssn)))
=>
(printout t "Duplicated SSN " ?ssn crlf))
CLIPS> (agenda)
0 repeated-person: f-1,*,*
For a total of 1 activation.
CLIPS> (run)
Duplicated SSN 123-45-6789
CLIPS>

CLIPS: match when value not in multislot

I am trying to write a rule that will match when a certain value is not in a multislot, and then add that value to it.
(deftemplate person
(multislot packing_list
(type SYMBOL)
(default ?DERIVE)))
(defrule apply_adapter
(travel international)
?p <- (person (packing_list $? ~travel_adaptor ))
=>
(modify ?p (packing_list travel_adaptor)))
(println "Added to list" crlf)
)
(deffacts start
(travel international)
(person)
)
Two parts of this I know aren't correct:
?p <- (person (packing_list $? ~travel_adaptor )) does not fire the rule - what is the correct syntax?
(modify ?p (packing_list travel_adaptor))) probably does not do what I want, which is to insert the value, not replace the list.
Any ideas how to fix this?
CLIPS (Cypher Beta 8/21/18)
CLIPS>
(deftemplate person
(multislot packing_list
(type SYMBOL)
(default ?DERIVE)))
CLIPS>
(defrule apply_adapter
(travel international)
?p <- (person (packing_list $?pl))
(test (not (member$ travel_adaptor ?pl)))
=>
(modify ?p (packing_list ?pl travel_adaptor))
(println "Added to list"))
CLIPS>
(deffacts start
(travel international)
(person))
CLIPS> (reset)
CLIPS> (run)
Added to list
CLIPS> (facts)
f-1 (travel international)
f-2 (person (packing_list travel_adaptor))
For a total of 2 facts.
CLIPS>

Clips Family Expert System

I'm trying to implement a basic expert system in the Clips programming language. I have a knowledge base of children with their respective parents. I want to set up a rule so that if two children have the same parents then it asserts the fact that they are siblings.
(deftemplate person "family tree"
(slot name)
(slot father)
(slot mother))
(assert
(person
(name "William")
(father "John")
(mother "Megan")))
(assert
(person (name "David")
(father "John")
(mother "Megan")))
(defrule sibling
(person
(name ?name1)
(father ?x)
(mother ?x))
(person
(name ?name2)
(father ?y)
(mother ?y)))
and when I define the rule I get a syntax error:
Syntax Error: Check appropriate syntax for defrule.
The correct syntax for your rule is:
(defrule sibling
(person (name ?name1) (father ?x) (mother ?x))
(person (name ?name2) (father ?y) (mother ?y))
=>
...)
Within a rule, a template is referred as:
(template_name (slot_name value) (slot_name value))
A rule is divided in two sides: the LHS (Left-Hand Side) where you define the conditions satisfying such rule and the RHS (Right-Hand Side) where you define the consequent actions.
In CLIPS, the => operator separates the two sides.
Example:
(defrule animal-is-a-duck
(animal ?name)
(quacks)
(two legs)
(lay eggs)
=>
(assert (animal ?name is a duck)))
You can read more about CLIPS syntax in the basic programming guide.
Your rule should be something like this:
(defrule sibling
(person
(name ?name1)
(father ?x)
(mother ?y))
(person
(name ?name2)
(father ?x)
(mother ?y))
(test (neq ?name1 ?name2))
=>
(assert (siblings ?name1 ?name2)))
The original rule could be satisfied only if the father and the mother were the same person in each fact being matched.
This rule permits duplicates:
f-3 (siblings "David" "William")
f-4 (siblings "William" "David")
so you can either catch that in another rule or you can write a more complex rule (or another rule) also matching against currently generated matched siblings facts.

Resources