How to further check fact values for only selected facts? - clips

I have following template:
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
I have the following rule:
(defrule drule
(drule
(id ?id))
(forall
(drule
(id ?id)
(name1 ?name1)
(field1 $?f11 ?field1 $?)
(value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
(name2 ?name2)
(field2 $?f22 ?field2 $?)
(value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
(claim
(name ?name1)
(field $?f1 ?field1 $?)
(value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
(claim
(name ?name2)
(field $?f2 ?field2 $?)
(value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))
(not
(claim (field $?f3 ?field1 $?)(value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?)))
(not (claim (field $?f4 ?field2 $?)(value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?))))
(forall
(claim
(field $?f5 ?field5 $?)(value $?v5&:(= (length$ ?f5)(length$ ?v5)) ?value5 $?))
(not
(claim (field $?f6 ?field5 $?)(value $?v6&:(= (length$ ?f6)(length$ ?v6)) ~?value5 $?))))
=>
(assert (success)))
The above rule does the following:
Check whether all fields:value pair in drule are found in claim facts.
Check whether all field:value pair in drule are also the same in other matched claims. (only if field is found
in the claim) answered
Check whether all field:values pairs are same in each selected pair. (Only if field is found).
For example,
(assert
(claim (name 'Employee')
(field 'EmpName' 'Company')
(value 'Bob' 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
Above should be successful, while the below should fail. Because EmpName mismatched.
(assert
(claim (name 'Employee')
(field 'EmpName' 'Company')
(value 'Bob' 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Adam' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
But my rule fails when there is the following assertion
(assert
(claim (name 'Employee')
(field 'EmpName' 'Company')
(value 'Bob' 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Adam' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
I want to fire the rule even if there is one claim with same EmpName field. My rule checks all the claims and if there
is any field:value mismatch then it wont fire.
EDIT: Is there a way to retrieve the field EmpName or other field like Company to the RHS? For example, for the last set of facts above, can I get the RHS to (assert (User ?name successfully entered)). I only want the for the claim which was matched in the forall (CLIPS throws me an error).
Thank you.

I'm not sure if this is precisely what you want in satisfying criterion #3 since I don't fully understand your explanation, but it works properly for the examples you've given.
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(forall
(drule (id ?id)
(name1 ?name1)
(field1 $?f11 ?field1 $?)
(value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
(name2 ?name2)
(field2 $?f22 ?field2 $?)
(value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
(claim (name ?name1)
(field $?f1 ?field1 $?)
(value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
(claim (name ?name2)
(field $?f2 ?field2 $?)
(value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))
(not (claim (field $?f3 ?field1 $?)
(value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?)))
(not (claim (field $?f4 ?field2 $?)
(value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?))))
(forall
(claim (name ?name3&?name1|?name2)
(field $?f5 ?field5 $?)
(value $?v5&:(= (length$ ?f5)(length$ ?v5)) ?value5 $?))
(exists
(claim (name ~?name3&?name1|?name2)
(field $?f6 ?field5 $?)
(value $?v6&:(= (length$ ?f6)(length$ ?v6)) ?value5 $?))))
=>
(assert (success)))
For values you want to use on the RHS, you can retrieve them outside the forall conditional elements by either adding them to the drule pattern (as is done here for name1 and name2) or by adding an additional pattern for retrieving them from a specific claim fact.
Since you've got some complex logic, you might find it easier to debug your code if you split the logic between multiple rules:
(defrule check-1
(drule (id ?id)
(name1 ?name1)
(field1 $?f11 ?field1 $?)
(value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
(name2 ?name2)
(field2 $?f22 ?field2 $?)
(value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
(not (and (claim (name ?name1)
(field $?f1 ?field1 $?)
(value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
(claim (name ?name2)
(field $?f2 ?field2 $?)
(value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))))
=>
(assert (mismatch ?id)))
(defrule check-2
(drule (id ?id)
(name1 ?name1)
(field1 $?f11 ?field1 $?)
(value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
(name2 ?name2)
(field2 $?f22 ?field2 $?)
(value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
(or (claim (field $?f3 ?field1 $?)
(value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?))
(claim (field $?f4 ?field2 $?)
(value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?)))
=>
(assert (mismatch ?id)))
(defrule check-3
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(exists (claim (name ?name3&?name1|?name2)
(field $?f3 ?field3 $?)
(value $?v3&:(= (length$ ?f3) (length$ ?v3)) ?value3 $?))
(not (claim (name ~?name3&?name1|?name2)
(field $?f4 ?field3 $?)
(value $?v4&:(= (length$ ?f4)(length$ ?v4)) ?value3 $?))))
=>
(assert (mismatch ?id)))
(defrule drule
(declare (salience -10))
(drule (id ?id))
(not (mismatch ?id))
=>
(assert (success)))

Related

Rule does not trigger even having the facts in WM

As the title suggests, I have a "generate-hotels" rule which, although being in the correct module and having all the facts that are needed in WM, this rule does not occur.
(defrule HOTELS::generate-hotels
(hotel (name ?name) (tr ?tr) (stars ?s) (price-per-night ?ppn))
(tourism-resort (name ?tr) (region ?r) (type $? ?t $?) (score ?s))
(tourism-type ?t)
(attribute (name best-region) (value ?r) (certainty ?certainty-1))
(attribute (name best-city) (value ?city&:(eq ?city (sym-cat (str-cat ?r "-") ?tr))) (certainty ?certainty-2))
(attribute (name best-tourism-type) (value ?t) (certainty ?certainty-3))
=>
(assert (attribute (name hotel) (value ?name)
(certainty (min ?certainty-1 ?certainty-2 ?certainty-3)))))

Can I bind a variable on some condition?

I have a template:
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
Can I have rule which will
Check whether there are any claims with name same as the one in drule.
Check whether claim:field has value 'EmpName', if found then bind it to ?name.
Currently I have something as follows:
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
(claim (name ?name2)
(field $?pf2 'EmpName' $?)
(value $?pv2&:(= (length$ ?pf2) (length$ ?pv2)) ?name $?))
...
=>
(assert (success ?name))
But it will bind only if all the selected claims have a field EmpName. I only want to check those claims with name
mentioned in drule. And if any of those claims have field 'EmpName' then it should be bound. If not just continue. My
rule fails if some claims do not have EmpName field at all. Assumption is that some claim will have EmpName field
and value.
Desired input and output:
(assert
(claim (name 'Employee')
(field 'Company')
(value 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
Output:
(success 'Bob')
Sorry if it is very stupid, I tried using test$ and member$ on field. But could not figure it out. I am self-learning CLIPS in my free time.
CLIPS>
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
CLIPS>
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
CLIPS>
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1))
(claim (name ?name2))
(claim (name ?name1 | ?name2)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
=>
(assert (success ?name)))
CLIPS>
(deffacts initial
(claim (name 'Employee')
(field 'Company')
(value 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
CLIPS> (reset)
CLIPS> (agenda)
0 drule: f-3,f-1,f-2,f-2
For a total of 1 activation.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (claim (name 'Employee') (field 'Company') (value 'ABC'))
f-2 (claim (name 'Event') (field 'EmpName' 'EventName' 'Company') (value 'Bob' 'Conference' 'ABC'))
f-3 (drule (name1 'Employee') (id '001') (field1 'Company') (value1 'ABC') (name2 'Event') (field2 'EventName') (value2 'Conference'))
f-4 (success 'Bob')
For a total of 5 facts.
CLIPS>

CLIPS runtime crash

I wrote a program which asserts the facts in the LHS of this rule:
(defrule check-open-better (declare (salience 50))
?f1 <- (newnode (ident ?id) (gcost ?g) (fcost ?f) (father ?anc))
(status (ident ?id) (subject ?subject) (data $?eqL))
?f2 <- (status (ident ?old) (subject ?subject) (data $?eqL))
?f3 <- (node (ident ?old) (gcost ?g-old) (open yes))
(test
(eq
(implode$
(find-all-facts ((?f status))
(and
(eq(str-compare ?f:ident ?id) 0)
(eq(str-compare ?f:subject ?subject) 0)
(eq(str-compare (implode$ ?f:data) (implode$ $?eqL)) 0)
)
)
)
(implode$
(find-all-facts ((?f status))
(and
(eq(str-compare ?f:ident ?old) 0)
(eq(str-compare ?f:subject ?subject) 0)
(eq(str-compare (implode$ ?f:data) (implode$ $?eqL)) 0)
)
)
)
0)
)
(test (< ?g ?g-old))
?f4 <- (open-better ?a)
=>
(assert (node (ident ?id) (gcost ?g) (fcost ?f) (father ?anc) (open yes)))
(assert (open-better (+ ?a 1)))
(retract ?f1 ?f2 ?f3 ?f4)
(pop-focus)
(pop-focus))
node, newnode and status are defined as deftemplate.
When this rule is in the agenda, CLIPS crash like it was typed the (exit) command.
I'm sure it's not fault of the rules that assert facts that allow this rule to be added in the agenda. Does anyone know why?
If CLIPS is crashing, it's a bug in CLIPS. I tried reproducing the problem by filling in the missing pieces and running in CLIPS 6.3, 6.31, and 6.4, but was unable to get a crash.
(deftemplate newnode
(slot ident)
(slot gcost (type INTEGER))
(slot fcost)
(slot father))
(deftemplate status
(slot ident)
(slot subject)
(multislot data))
(deftemplate node
(slot ident)
(slot gcost (type INTEGER))
(slot open))
(deffacts start
(node (ident "1") (gcost 10) (open yes))
(open-better 0)
(newnode (ident "2"))
(status (ident "1"))
(status (ident "2")))
Generally, it's a bad idea to use the query functions from the conditions of a rule because 1) you can use pattern matching and 2) the query contained within a test CE will not be reevaluated unless there's some changes to prior patterns.
It's not clear what you're trying to do with the find-all-facts calls. First, there's cruft in there that you don't need. The str-compare and implode$ function calls are unnecessary and the third argument of 0 to eq will cause the test CE to always fail since the return values of the find-all-facts calls will never be 0.
(test
(eq
(find-all-facts ((?f status))
(and
(eq ?f:ident ?id)
(eq ?f:subject ?subject)
(eq ?f:data $?eqL)
)
)
(find-all-facts ((?f status))
(and
(eq ?f:ident ?old)
(eq ?f:subject ?subject)
(eq ?f:data $?eqL)
)
)
)
)
Both find-all-fact calls must return the same facts in order for the test CE to be satisfied. That can only be true if there are no status facts or the ?id and ?old variables have the same value.
Try this one, it should work. :)
(defrule check-open-better (declare (salience 50))
?f1 <- (newnode (ident ?id) (gcost ?g) (fcost ?f) (father ?anc))
(status (ident ?id) (subject ?subject) (data $?eqL))
?f2 <- (status (ident ?old) (subject ?subject) (data $?eqL))
?f3 <- (node (ident ?old) (gcost ?g-old) (open yes))
(test (< ?g ?g-old))
?f4 <- (open-better ?a)
=>
(if (eq
(implode$
(find-all-facts ((?f status))
(and
(eq ?f:ident ?id)
(eq ?f:subject ?subject)
(eq (implode$ ?f:data) (implode$ $?eqL)))))
(implode$
(find-all-facts ((?f status))
(and
(eq ?f:ident ?old)
(eq ?f:subject ?subject)
(eq (implode$ ?f:data) (implode$ $?eqL)) 0))))
then
(assert (node (ident ?id) (gcost ?g) (fcost ?f) (father ?anc) (open yes)))
(assert (open-better (+ ?a 1)))
(retract ?f1 ?f2 ?f3 ?f4))
(pop-focus)
(pop-focus))

Looping defrule in CLIPS

I am trying to solve a problem, where I have to fill a 5x5 matrix with letters A, B, C, D, and E. Each letter cannot occur more than once in each row and in each column. With some initial letter positions given.
I created every position as separate facts eg. "M 1 1 X".
I am struggling how to loop a defrule in way to assert a fact with correct letter and check the conditions again.
(defrule solveA5
?a <-(M 5 ?c X)
(not (M ?x ?c A))
=>
(retract ?a)
(assert (M 5 ?c A))
)
Code above for example is only to check presence of A in every position of 5th row, but the problem is that conditions are checked at the beginning only and instead of asserting correct fact and checking again it asserts A in every position.
I've tried using deffunction to loop defrule.
(deffunction solve (?letter)
(loop-for-count (?x 1 5) do
(loop-for-count (?y 1 5) do
(build (str-cat"defrule costam
?a <-(M ?x ?y X)
(not (and(M ?x ?a ?letter) (M ?b ?y ?letter))
=>
(retract ?a)
(assert (M ?x ?y ?letter))")
)
)
)
)
Unfortunately running
(solve A)
returns "FALSE" and doesn't modify any facts.
To handle iteration within rules, you must assert the iteration information as facts to allow the rules to match and modify this information. In the placement, it's not essential to do this in any particular order, so you can just assert information containing the rows, columns, and letters to place and allow the rules fire arbitrarily:
CLIPS>
(deftemplate element
(slot row)
(slot column)
(slot value))
CLIPS>
(deftemplate print
(slot row)
(slot column)
(slot end-of-row))
CLIPS>
(deffacts initial
(rows 1 2 3 4 5)
(columns 1 2 3 4 5)
(letters A B C D E))
CLIPS>
(defrule place
(rows $? ?r1 $?)
(columns $? ?c1 $?)
(letters $? ?l $?)
(not (element (row ?r1) (column ?c1)))
(not (and (element (row ?r2)
(column ?c2)
(value ?l))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))))
=>
(assert (element (row ?r1) (column ?c1) (value ?l))))
CLIPS>
(defrule print-start
(declare (salience -10))
(rows ?r $?)
(columns ?c $?rest)
=>
(assert (print (row ?r)
(column ?c)
(end-of-row (= (length$ ?rest) 0)))))
CLIPS>
(defrule print-next-column
(declare (salience -10))
?f <- (print (column ?c))
(columns $? ?c ?nc $?rest)
=>
(modify ?f (column ?nc)
(end-of-row (= (length$ ?rest) 0))))
CLIPS>
(defrule print-next-row
(declare (salience -10))
?f <- (print (column ?c) (row ?r))
(columns $?first ?c)
(rows $? ?r ?nr $?)
=>
(if (= (length$ ?first) 0)
then
(bind ?eor TRUE)
(bind ?nc ?c)
else
(bind ?eor FALSE)
(bind ?nc (nth$ 1 ?first)))
(modify ?f (row ?nr)
(column ?nc)
(end-of-row ?eor)))
CLIPS>
(defrule print-placed
(print (row ?r) (column ?c) (end-of-row ?eor))
(element (row ?r) (column ?c) (value ?l))
=>
(if ?eor
then
(printout t ?l crlf)
else
(printout t ?l " ")))
CLIPS>
(defrule print-unplaced
(print (row ?r) (column ?c) (end-of-row ?eor))
(not (element (row ?r) (column ?c)))
=>
(if ?eor
then
(printout t "?" crlf)
else
(printout t "? ")))
CLIPS> (reset)
CLIPS> (run)
E D C B A
? C D A B
? B A D C
? A B C D
A ? ? ? E
CLIPS>
In this example, the print rules iterate over the rows and columns by storing the iteration information in facts. You can see how much more complicated this is than the place rule which assigns the elements in an arbitrary manner.
Whether you assign the values arbitrarily or in a specific order, it's possible to assign values that prevent a solution, so you must implement backtracking in order to guarantee finding the solution if one exists. In this example, the facts store information about the order of the value placements and the values that have been tried:
CLIPS> (clear)
CLIPS>
(deftemplate element
(slot row)
(slot column)
(slot value (default unset))
(multislot values)
(slot placement))
CLIPS>
(deffacts initial
(placement 0)
(rows 1 2 3 4 5)
(columns 1 2 3 4 5)
(letters A B C D E))
CLIPS>
(defrule prime
(placement ?p)
(rows $? ?r $?)
(columns $? ?c $?)
(letters $?l)
(not (element (placement ?p)))
(not (element (row ?r) (column ?c)))
=>
(assert (element (placement ?p) (values ?l) (row ?r) (column ?c))))
CLIPS>
(defrule place-good
?f1 <- (placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(row ?r1)
(column ?c1)
(values ?v $?rest))
(not (and (element (row ?r2)
(column ?c2)
(value ?v))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))))
=>
(retract ?f1)
(assert (placement (+ ?p 1)))
(modify ?f2 (value ?v) (values ?rest)))
CLIPS>
(defrule place-bad
(placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(row ?r1)
(column ?c1)
(values ?v $?rest))
(element (row ?r2)
(column ?c2)
(value ?v))
(test (or (= ?r1 ?r2) (= ?c1 ?c2)))
=>
(modify ?f2 (values ?rest)))
CLIPS>
(defrule backtrack
?f1 <- (placement ?p)
?f2 <- (element (placement ?p)
(value unset)
(values))
?f3 <- (element (placement =(- ?p 1))
(value ~unset))
=>
(retract ?f1)
(assert (placement (- ?p 1)))
(retract ?f2)
(modify ?f3 (value unset)))
CLIPS>
(defrule print
(declare (salience -10))
(rows $?rows)
(columns $?columns)
=>
(progn$ (?r ?rows)
(progn$ (?c ?columns)
(if (not (do-for-fact ((?f element))
(and (= ?r ?f:row) (= ?c ?f:column))
(printout t ?f:value " ")))
then
(printout t "? ")))
(printout t crlf)))
CLIPS> (reset)
CLIPS> (run)
B C D E A
A B C D E
C A E B D
D E A C B
E D B A C
CLIPS>
The print rules have been simplified into a single rule that iterates over the row and columns in the actions of the rule and uses the fact query functions to retrieve values that have been assigned.
The program also works if you preassign some of the values:
CLIPS> (reset)
CLIPS> (assert (element (row 1) (column 1) (value A)))
<Fact-5>
CLIPS> (assert (element (row 3) (column 3) (value C)))
<Fact-6>
CLIPS> (assert (element (row 5) (column 4) (value E)))
<Fact-7>
CLIPS> (run)
A C E D B
B A D C E
D E C B A
E D B A C
C B A E D
CLIPS>

CLIPS counting facts or template instances that match the pattern

First I declare:
(deftemplate worker
(slot id
(type STRING)
(default ?DERIVE))
(slot salary
(type FLOAT)
(default ?DERIVE)))
then I add:
(assert(worker(id "a")(salary 30.0)))
(assert(worker(id "b")(salary 40.0)))
(assert(worker(id "c")(salary 60.0)))
(assert(worker(id "d")(salary 70.0)))
(assert(worker(id "e")(salary 10.0)))
How can I count how many 'workers' I have?
How can I count for example how many workers have salary over 30?
Use the fact-set query functions:
CLIPS>
(deftemplate worker
(slot id (type STRING) (default ?DERIVE))
(slot salary (type FLOAT) (default ?DERIVE)))
CLIPS> (assert (worker (id "a") (salary 30.0)))
<Fact-1>
CLIPS> (assert (worker (id "b") (salary 40.0)))
<Fact-2>
CLIPS> (assert (worker (id "c") (salary 60.0)))
<Fact-3>
CLIPS> (assert (worker (id "d") (salary 70.0)))
<Fact-4>
CLIPS> (assert (worker (id "e") (salary 10.0)))
<Fact-5>
CLIPS> (find-all-facts ((?f worker)) (> ?f:salary 30.0))
(<Fact-2> <Fact-3> <Fact-4>)
CLIPS> (length$ (find-all-facts ((?f worker)) (> ?f:salary 30.0)))
3
CLIPS> (do-for-all-facts ((?f worker)) (> ?f:salary 30.0) (printout t ?f:id crlf))
b
c
d
CLIPS>

Resources