CLIPS beginner: How CLIPS rules interpret the facts order? - clips

I would like to know in each day which activities I do, so I constructed the following code:
(deftemplate schedule
(slot activity)
(slot starthour)
(slot endhour)
)
(defrule r1
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(not (busy ?start ?a))
=>
(assert (busy ?start ?a))
)
(defrule r2
(busy ?d ?a)
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(test (< ?d ?end))
=>
(assert (busy ( + ?d 1) ?a))
)
CLIPS> (assert (schedule (activity reading) (starthour 3) (endhour 5)))
<Fact-1>
CLIPS> (assert (schedule (activity music) (starthour 4) (endhour 7)))
<Fact-2>
For the facts that I inserted I obtained a result that does not order the days.
How CLIPS interpret the order of the facts?
Is there a way to me to combine in a day more than 1 activity?
Thanks very much!

By default CLIPS uses a depth first strategy for determining which rule to execute next, so generally the next rule executed will have been activated by the last fact asserted or retracted. Section 5.3, Conflict Resolution Strategies, in the Basic Programming Guide describes this process in greater detail.
As long as the correct facts are being generated by the rules, you shouldn't be particularly concerned with the order in which they're place on the fact-list because 1) It's not practical to use the fact-list to display the output of a program and 2) forcing a particular order of placement can be difficult and overly complex.
Instead, to order facts in your program output, collect all of the relevant facts using one of the fact query functions and then use the sort function with a custom comparator to order the facts before printing them:
CLIPS (6.4 2/9/21)
CLIPS>
(deftemplate schedule
(slot activity)
(slot starthour)
(slot endhour))
CLIPS>
(deftemplate busy
(slot activity)
(slot hour))
CLIPS>
(defrule r1
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(not (busy (activity ?a) (hour ?start)))
=>
(assert (busy (activity ?a) (hour ?start))))
CLIPS>
(defrule r2
(busy (activity ?a) (hour ?d))
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(test (< ?d ?end))
=>
(assert (busy (activity ?a) (hour (+ ?d 1)))))
CLIPS>
(deffacts schedules
(schedule (activity reading) (starthour 3) (endhour 5))
(schedule (activity music) (starthour 4) (endhour 7)))
CLIPS>
(deffunction busy-compare (?f1 ?f2)
;; Sort by hour
(if (> (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
then (return FALSE))
(if (< (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
then (return TRUE))
;; And then sort by activity
(if (> (str-compare (fact-slot-value ?f2 activity)
(fact-slot-value ?f1 activity)) 0)
then (return FALSE)
else (return TRUE)))
CLIPS>
(defrule print
(declare (salience -10))
=>
(bind ?schedule (find-all-facts ((?f busy)) TRUE))
(bind ?schedule (sort busy-compare ?schedule))
(foreach ?s ?schedule
(format t "%2d %s%n" (fact-slot-value ?s hour) (fact-slot-value ?s activity))))
CLIPS> (reset)
CLIPS> (run)
3 reading
4 music
4 reading
5 music
5 reading
6 music
7 music
CLIPS> (facts)
f-1 (schedule (activity reading) (starthour 3) (endhour 5))
f-2 (schedule (activity music) (starthour 4) (endhour 7))
f-3 (busy (activity music) (hour 4))
f-4 (busy (activity music) (hour 5))
f-5 (busy (activity music) (hour 6))
f-6 (busy (activity music) (hour 7))
f-7 (busy (activity reading) (hour 3))
f-8 (busy (activity reading) (hour 4))
f-9 (busy (activity reading) (hour 5))
For a total of 9 facts.
CLIPS>
Here's another way to do it storing multiple activities in each busy fact:
CLIPS> (clear)
CLIPS>
(deftemplate schedule
(slot activity)
(slot starthour)
(slot endhour))
CLIPS>
(deftemplate busy
(multislot activity)
(slot hour))
CLIPS>
(defrule r1
(schedule (activity ?a) (starthour ?start) (endhour ?end))
=>
(loop-for-count (?hour ?start ?end)
(assert (busy (activity ?a) (hour ?hour)))))
CLIPS>
(defrule combine
?b1 <- (busy (activity $?a) (hour ?d))
?b2 <- (busy (activity ?n&:(not (member$ ?n ?a))) (hour ?d))
=>
(modify ?b1 (activity ?a ?n))
(retract ?b2))
CLIPS>
(deffacts schedules
(schedule (activity reading) (starthour 3) (endhour 5))
(schedule (activity music) (starthour 4) (endhour 7)))
CLIPS>
(deffunction busy-compare (?f1 ?f2)
(if (> (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
then (return FALSE))
(if (< (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
then (return TRUE))
(return FALSE))
CLIPS>
(defrule print
(declare (salience -10))
=>
(bind ?schedule (find-all-facts ((?f busy)) TRUE))
(bind ?schedule (sort busy-compare ?schedule))
(foreach ?s ?schedule
(format t "%2d %s%n" (fact-slot-value ?s hour) (implode$ (fact-slot-value ?s activity)))))
CLIPS> (reset)
CLIPS> (run)
3 reading
4 reading music
5 reading music
6 music
7 music
CLIPS> (facts)
f-1 (schedule (activity reading) (starthour 3) (endhour 5))
f-2 (schedule (activity music) (starthour 4) (endhour 7))
f-5 (busy (activity music) (hour 6))
f-6 (busy (activity music) (hour 7))
f-7 (busy (activity reading) (hour 3))
f-8 (busy (activity reading music) (hour 4))
f-9 (busy (activity reading music) (hour 5))
For a total of 7 facts.
CLIPS>

Related

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

How to fire off the rule in this example?

I'm trying to build a simple expert system for recommending courses and want to implement certainty factor in my program, however I'm stuck looking for a simple integration method.
I've stumbled upon this example but can't seems to figure out how to make it fire.
; Allow facts that are duplicates:
(defrule start
(declare (salience 1000))
(initial-fact)
=>
(set-fact-duplication TRUE))
(defrule combine-certainities-both-positive
?fact1 <- (organism ?attribute ?value ?C1&:(>= ?C1 0))
?fact2 <- (organism ?attribute ?value ?C2&:(>= ?C2 0))
(test (neq ?fact1 ?fact2))
=>
(retract ?fact1 ?fact2)
(bind ?C3 (- (+ ?C1 ?C2) (* ?C1 ?C2)))
(assert (organism ?attribute ?value ?C3)))
(defrule combine-certainities-both-negative
?fact1 <- (organism ?attribute ?value ?C1&:(< ?C1 0))
?fact2 <- (organism ?attribute ?value ?C2&:(< ?C2 0))
(test (neq ?fact1 ?fact2))
=>
(retract ?fact1 ?fact2)
(bind ?C3 (+ (+ ?C1 ?C2) (* ?C1 ?C2)))
(assert (organism ?attribute ?value ?C3)))
(defrule combine-certainities-with-opposite-signs
?fact1 <- (organism ?attribute ?value ?C1)
?fact2 <- (organism ?attribute ?value ?C2)
(test (< (* ?C1 ?C2) 0))
(test (neq ?fact1 ?fact2))
=>
(retract ?fact1 ?fact2)
(bind ?C3 (/ (+ ?C1 ?C2) (- 1 (min (abs ?C1) (abs ?C2)))))
(assert (organism ?attribute ?value ?C3)))
I try to assert two new organism facts to kick start the first rule:
CLIPS> (assert (organism morpholgy1 rod1 0.25)
(organism morpholgy2 rod2 0.25))
==> f-4 (organism morpholgy1 rod1 0.25)
==> f-5 (organism morpholgy2 rod2 0.25)
<Fact-5>
CLIPS> (run)
<== Focus MAIN
0 rules fired Run time is 0.00300693511962891 seconds.
0.0 rules per second.
2 mean number of facts (2 maximum).
0 mean number of instances (0 maximum).
0 mean number of activations (0 maximum).
And use the matches but still don't get how to make it match here..
CLIPS> (matches combine-certainities-both-positive)
Matches for Pattern 1
f-4
f-5
Matches for Pattern 2
f-4
f-5
Partial matches for CEs 1 - 2
None
Activations
None
(4 0 0)
The attribute and the value have to match (morpholgy1 != morpholgy2 and rod1 != rod2). That's why fact duplication has to be enabled to allow multiple copies of the same attribute/value.
CLIPS> (set-fact-duplication TRUE)
FALSE
CLIPS>
(assert (organism morpholgy rod 0.25)
(organism morpholgy rod 0.25))
<Fact-2>
CLIPS> (watch facts)
CLIPS> (run)
<== f-2 (organism morpholgy rod 0.25)
<== f-1 (organism morpholgy rod 0.25)
==> f-3 (organism morpholgy rod 0.4375)
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.

Finding facts of a template which has something in common with another template

I am using CLIPS for a project.
I am using this template A which has an attribute model and another template B which has an attribute model as well.
So what I want to achieve is based on the attribute model, return those facts of template A which has the same attribute model value as of facts from template B.
I tried using this format
(find-all-facts((?a template_A)(?b template_B))
(and
//condition to be met
)
)
it does give me the results, but it is giving me both the results for A and B which are duplicates.. How do I make it in a way it returns non duplicate values, either A or B?
CLIPS>
(deftemplate template_A
(slot model))
CLIPS>
(deftemplate template_B
(slot model))
CLIPS>
(deffacts start
(template_A (model 1))
(template_A (model 2))
(template_A (model 3))
(template_B (model 2))
(template_B (model 3))
(template_B (model 4)))
CLIPS>
(deffunction extract-every-nth-value (?values ?start ?increment)
(bind ?rv (create$))
(while (<= ?start (length$ ?values))
(bind ?rv (create$ ?rv (nth$ ?start ?values)))
(bind ?start (+ ?start ?increment)))
(return ?rv))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (template_A (model 1))
f-2 (template_A (model 2))
f-3 (template_A (model 3))
f-4 (template_B (model 2))
f-5 (template_B (model 3))
f-6 (template_B (model 4))
For a total of 7 facts.
CLIPS>
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
(<Fact-2> <Fact-4> <Fact-3> <Fact-5>)
CLIPS>
(extract-every-nth-value
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
1 2)
(<Fact-2> <Fact-3>)
CLIPS>

Resources