CLIPS Clause Interdependence - clips

I'd like to remove two of the test conditional elements from the following rule as well as improve the readability of it.
(defrule compare-things
(logical ?thing0 <- (object (is-a TYPED_THING)
(type-results $? ?t0 $?)))
(logical ?thing1 <- (object (is-a TYPED_THING)
(type-results $? ?t1 $?)))
(thing-comparable ?type)
?type0 <- (object (is-a TYPING)
(qualified-type ?type ?model ?mode ?comp0))
?type1 <- (object (is-a TYPING)
(qualified-type ?type ?model ?mode ?comp1))
; This test exists to restrict the number of rule firings
(test (> (str-compare (instance-name ?thing0) (instance-name ?thing1)) 0))
; Ideally, the following two tests can be removed
(test (= (str-compare (instance-name ?type0) (instance-name ?t0)) 0))
(test (= (str-compare (instance-name ?type1) (instance-name ?t1)) 0))
=>
(make-instance of COMPARISON
(compares ?thing0 ?thing1)
(score nil)
)
(printout t "comparing: " (instance-name ?thing0) (instance-name ?thing1) crlf)
)
The multislot field values ?t0 and ?t1 should correspond to the same instances as ?type0 and ?type1. If I replace ?t0 and ?t1 with ?type0 and ?type1 (which is intuitive first attempt), then I receive the following error while loading the rules:
Defining defrule: compare-things
[ANALYSIS2] Pattern-address ?type0 used in CE #4 was previously bound within a pattern CE.
[ANALYSIS2] Pattern-address ?type1 used in CE #5 was previously bound within a pattern CE.
ERROR:
(defrule MAIN::compare-things
(logical
?thing0 <- (object (is-a TYPED_THING)
(type-results $? ?type0 $?)))
(logical
?thing1 <- (object (is-a TYPED_THING)
(type-results $? ?type1 $?)))
(thing-comparable ?type)
?type0 <- (object (is-a TYPING)
(qualified-type ?type ?model ?mode ?comp0))
?type1 <- (object (is-a TYPING)
(qualified-type ?type ?model ?mode ?comp1))
(test (> (str-compare (instance-name ?thing0) (instance-name ?thing1)) 0))
=>
(make-instance of COMPARISON
(compares ?thing0 ?thing1)
(score nil))
(printout t "comparing: " (instance-name ?thing0) (instance-name ?thing1) crlf))
FALSE
The following data is being used to stimulate the rule in development:
(defclass TYPING (is-a USER)
(role concrete)
(multislot qualified-type (access initialize-only)
(type STRING)
(cardinality 4 4))
(slot score (access initialize-only)
(type FLOAT))
)
(defclass TYPED_THING (is-a USER)
(slot id (access initialize-only)
(type INTEGER))
(multislot type-results (access initialize-only)
(type INSTANCE)) ; of TYPING
)
(defclass COMPARISON (is-a USER)
(multislot compares (access initialize-only)
(type INSTANCE) ; of TYPED_THING
(cardinality 2 2))
(slot score (access read-write)
(type FLOAT))
)
; These facts tag top-level types that are comparable
(deffacts KNOWN_COMPARABLE_TYPES
(thing-comparable "cat-a")
(thing-comparable "cat-c")
)
(definstances KNOWN_THINGS
(thing0 of TYPED_THING
(id 0)
(type-results (make-instance of TYPING (qualified-type "cat-a" "x0" "y0" "z0")(score 0.9))
(make-instance of TYPING (qualified-type "cat-b" "x0" "y0" "z0")(score 0.9))))
(thing1 of TYPED_THING
(id 1)
(type-results (make-instance of TYPING (qualified-type "cat-a" "x0" "y0" "z1")(score 0.9))
(make-instance of TYPING (qualified-type "cat-a" "x1" "y1" "z0")(score 0.9))))
(thing2 of TYPED_THING
(id 2)
(type-results (make-instance of TYPING (qualified-type "cat-b" "x0" "y0" "z1")(score 0.9))))
)
Which should produce the following output (as it currently does):
CLIPS> (reset)
CLIPS> (run)
comparing: [thing1][thing0]

Working within the limitation indicated by the error message, you can get the rule to compile with this modification:
(defrule compare-things
(logical ?thing0 <- (object (is-a TYPED_THING)
(type-results $? ?t0 $?)))
(logical ?thing1 <- (object (is-a TYPED_THING)
(type-results $? ?t1 $?)))
(thing-comparable ?type)
(object (is-a TYPING)
(name =(instance-name ?t0))
(qualified-type ?type ?model ?mode ?comp0))
(object (is-a TYPING)
(name =(instance-name ?t1))
(qualified-type ?type ?model ?mode ?comp1))
; This test exists to restrict the number of rule firings
(test (> (str-compare (instance-name ?thing0) (instance-name ?thing1)) 0))
=>
(make-instance of COMPARISON
(compares ?thing0 ?thing1)
(score nil)
)
(printout t "comparing: " (instance-name ?thing0) (instance-name ?thing1) crlf)
)

Related

How to use comparison operators on previous user input in CLIPS?

I'm a beginner to CLIPS. The goal of this program is to return the name of the company that best matches the user's demographics. I ask the user their gender and (might be storing it wrong) store it into input, ethnicity stored into input2, and age into input3. I'm running into errors when trying to do comparisons to return the company name. Eg. if input=1 (man), then compare the demographics of men in both companies and return the name of the company with more men.
(= 1 input) this line in the last rule gives me error "Function '=' expected argument #2 to be of type integer or float." which I thought I've already only allowed responses of integers. Any help would be appreciated, thank you!
(deftemplate company
(slot name)
(slot men)
(slot women)
(slot Asian)
(slot Black)
(slot Latinx)
(slot Indigenous)
(slot White)
(slot Other)
(slot <18)
(slot 18-20)
(slot 21-29)
(slot 30-39)
(slot 40+))
(deffacts demographics
(company (name Google)(men 0.669)(women 0.331)(Asian 0.428)(Black 0.088)(Latinx 0.088)(Indigenous 0.007)(White 0.445)(Other 0)(<18 0.02)(18-20 0.11)(21-29 0.59)(30-39 0.19)(40+ 0.07))
(company (name Apple)(men 0.652)(women 0.348)(Asian 0.279)(Black 0.094)(Latinx 0.148)(Indigenous 0.007)(White 0.438)(Other 0.032)(<18 0.01)(18-20 0.1)(21-29 0.57)(30-39 0.22)(40+ 0.06)))
(defrule begin => (assert (phase select-gender)))
(defrule menu (phase select-gender) =>
(printout t "Do you identify as a (1) man or (2) woman?: ") (assert (userinput (read))))
(defrule selection-okay
?phase <- (phase select-gender)
?input <- (userinput ?select&1|2)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase select-ethnicity)))
(defrule selection-nokay
?phase <- (phase select-gender)
?input <- (userinput ?select&~1&~2)
=>
(retract ?phase)
(assert (phase select-gender))
(printout t ?select " is not a valid response. Please enter 1 or 2."crlf))
(defrule menu2 (phase select-ethnicity) =>
(printout t "Do you identify as (1)Asian, (2)Black, (3)Latinx, (4)Indigenous, (5)White, or (6)Other?: ") (assert (userinput2 (read))))
(defrule selection-okay2
?phase <- (phase select-ethnicity)
?input2 <- (userinput2 ?select&1|2|3|4|5|6)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase select-age)))
(defrule selection-nokay2
?phase <- (phase select-ethnicity)
?input2 <- (userinput2 ?select&~1&~2&~3&~4&~5&~6)
=>
(retract ?phase)
(assert (phase select-ethnicity))
(printout t ?select " is not a valid response. Please enter 1 through 6."crlf))
(defrule menu3 (phase select-age) =>
(printout t "You are age: (1)<18, (2)18-20, (3)20-30, (4)30-40, (5)40+?: ") (assert (userinput3 (read))))
(defrule selection-okay3
?phase <- (phase select-age)
?input3 <- (userinput3 ?select&1|2|3|4|5)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase company-gender-men)))
(defrule selection-nokay3
?phase <- (phase select-age)
?input3 <- (userinput3 ?select&~1&~2&~3&~4&~5)
=>
(retract ?phase)
(assert (phase select-age))
(printout t ?select " is not a valid response. Please enter 1 through 5."crlf))
(defrule get-company-by-gender-men
?phase <- (company-gender-men)
(= 1 input)
(> (fact-slot-value 1 men) (fact-slot-value 2 men))
=>
(retract ?phase)
(printout t (fact-slot-value 1 name) crlf))
;(fact-slot-value 1 name)
;(> (fact-slot-value 1 men) (fact-slot-value 2 men))
I have tried removing some extra code just to see if I can access the previous user input but I'm still not getting an output.
(deftemplate company
(slot name)
(slot men)
(slot women)
(slot Asian)
(slot Black)
(slot Latinx)
(slot Indigenous)
(slot White)
(slot Other)
(slot <18)
(slot 18-20)
(slot 21-29)
(slot 30-39)
(slot 40+))
(deffacts demographics
(company (name Google)(men 0.669)(women 0.331)(Asian 0.428)(Black 0.088)(Latinx 0.088)(Indigenous 0.007)(White 0.445)(Other 0)(<18 0.02)(18-20 0.11)(21-29 0.59)(30-39 0.19)(40+ 0.07))
(company (name Apple)(men 0.652)(women 0.348)(Asian 0.279)(Black 0.094)(Latinx 0.148)(Indigenous 0.007)(White 0.438)(Other 0.032)(<18 0.01)(18-20 0.1)(21-29 0.57)(30-39 0.22)(40+ 0.06)))
(defrule begin => (assert (phase select-gender)))
(defrule menu (phase select-gender) =>
(printout t "Do you identify as a (1) man or (2) woman?: ") (assert (userinput (read)))(assert(phase select-ethnicity)))
(defrule menu2 (phase select-ethnicity) =>
(printout t "Do you identify as (1)Asian, (2)Black, (3)Latinx, (4)Indigenous, (5)White, or (6)Other?: ") (assert (userinput2 (read)))(assert(phase select-age)))
(defrule menu3 (phase select-age) =>
(printout t "You are age: (1)<18, (2)18-20, (3)20-30, (4)30-40, (5)40+?: ") (assert (userinput3 (read)))(assert(phase company-gender-men)))
(defrule get-company-by-gender-men
?phase <- (company-gender-men)
(userinput ?userinput)
=>
(retract ?phase)
(printout t ?userinput "can see user input"crlf))
(defrule get-company-by-gender-men
?phase <- (phase company-gender-men)
(userinput 1)
(company (name ?name) (men ?men1))
(company (men ?men2))
(test (> ?men1 ?men2))
=>
(retract ?phase)
(printout t ?name crlf))

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

correlation between deffunctions and defrules CLIPS

(deftemplate client
(slot idClient (type INTEGER))
(multislot nome)
(multislot birthdate)
(multislot registryCard)
(multislot endOfRegistryCard))
(deffunction reservation(?idfunc)
(build (str-cat"(defrule existsClient
(exists(client(idClient ?idfunc)))
=>
(printout t "exists" ?idfunc crlf))"
))
(run)
)
I made this deffunction and I want to see if exists the client with that idfunc that is received as parameter. what happens is that the defrule inside doesn't process this variable any thoughts how can i resolve?
I think you should set condition (assert your idfunc) and then fire the execution of your "existsClient" rule (defined as rule and not inside your function). That would be a clearer design in my opinion.
Typically you'll directly define your rules rather than using a deffunction, but here's how you can do it both ways:
CLIPS> (clear) ; Create rule and run with deffunction
CLIPS>
(deftemplate client
(slot idClient (type INTEGER))
(multislot nome)
(multislot birthdate)
(multislot registryCard)
(multislot endOfRegistryCard))
CLIPS>
(deffunction reservation (?idfunc)
(build (str-cat
"(defrule existsClient
(exists (client (idClient " ?idfunc ")))
=>
(printout t \"exists " ?idfunc "\" crlf))"))
(assert (client (idClient ?idfunc)))
(run))
CLIPS> (reservation 2)
exists 2
CLIPS> (ppdefrule existsClient)
(defrule MAIN::existsClient
(exists
(client (idClient 2)))
=>
(printout t "exists 2" crlf))
CLIPS> (clear) ; Create rule directly
CLIPS>
(deftemplate client
(slot idClient (type INTEGER))
(multislot nome)
(multislot birthdate)
(multislot registryCard)
(multislot endOfRegistryCard))
CLIPS>
(defrule existsClient
(exists (client (idClient 2)))
=>
(printout t "exists 2" crlf))
CLIPS> (ppdefrule existsClient)
(defrule MAIN::existsClient
(exists
(client (idClient 2)))
=>
(printout t "exists 2" crlf))
CLIPS> (assert (client (idClient 2)))
<Fact-1>
CLIPS> (run)
exists 2
CLIPS>

how to get the index of facts in RHS of rule?

I'm asking if there is a possibility of accessing a get the index of fact in RHS of defrule ?
It gives me that undefined every time I try to index a fact in a RHS of defrule.
because I have a while loop , I want to be able to modify elevator fact depending on my input data.
(deftemplate elevator
(slot goal))
(deffacts elevator
(elevator (goal 0)))
(defrule read-data
=>
?f1 <- (elevator)
(modify ?f2 (goal 1))
)
this an example of my code , since I can't put all online :
(deftemplate data
(slot data)
)
(deffacts data
(data (data 1))
)
(defrule rule1
?f1 <-(data)
=>
(bind ?value (readline input) )
(while (neq ?value EOF)
do
(bind ?data (fact-slot-value ?f1 data))
(printout t "data " ?data crlf )
(retract ?f1)
(modify ?f1 (data ?value))
(bind ?value (readline input)
)
)
)
this is my input file :
2
3
4
5
6
7
this is what I'm getting :
CLIPS> (run)
data 1
data FALSE
data FALSE
data FALSE
data FALSE
data FALSE
CLIPS>
I want it to print out
data 2
data 3
data 4 ..ect
You can do it this way from the RHS of a rule, but if your rule actually has no LHS conditions, it's pointless to use a rule to change the value of the fact. Just use a function.
CLIPS>
(deftemplate elevator
(slot goal))
CLIPS>
(deffacts elevator
(elevator (goal 0)))
CLIPS>
(defrule read-data
=>
(do-for-fact ((?f elevator)) TRUE
(modify ?f (goal 1))))
CLIPS> (watch facts)
CLIPS> (reset)
<== f-0 (initial-fact)
==> f-0 (initial-fact)
==> f-1 (elevator (goal 0))
CLIPS> (run)
<== f-1 (elevator (goal 0))
==> f-2 (elevator (goal 1))
CLIPS>
Alternately, you can bind the fact you want to modify in the conditions of the rule:
CLIPS> (clear)
CLIPS>
(deftemplate elevator
(slot goal))
CLIPS>
(deffacts elevator
(elevator (goal 0)))
CLIPS>
(defrule read-data
?f <- (elevator (goal 0))
=>
(modify ?f (goal 1)))
CLIPS> (watch facts)
CLIPS> (reset)
<== f-0 (initial-fact)
==> f-0 (initial-fact)
==> f-1 (elevator (goal 0))
CLIPS> (run)
<== f-1 (elevator (goal 0))
==> f-2 (elevator (goal 1))
CLIPS>
Updated:
You can get your original rule to "work" by removing the retract and rebinding ?f1 to the value returned by modify:
CLIPS> (clear)
CLIPS>
(deftemplate data
(slot data))
CLIPS>
(deffacts data
(data (data 1)))
CLIPS>
(defrule rule1
?f1 <- (data)
=>
(bind ?value (readline input))
(while (neq ?value EOF)
(bind ?data (fact-slot-value ?f1 data))
(printout t "data " ?data crlf )
(bind ?f1 (modify ?f1 (data ?value)))
(bind ?value (readline input))))
CLIPS> (reset)
CLIPS> (open input.txt input)
TRUE
CLIPS> (run)
data 1
data 2
data 3
data 4
data 5
data 6
CLIPS> (close input)
TRUE
CLIPS>
It's still suspiciously complicated to be modifying the same fact multiple times on the RHS.

Resources