How can you count how many rules were fired during the execution of a program in CLIPS? But I don't want to modify the existing rules, like adding a counter inside each.
Use the (watch statistics) command:
CLIPS> (clear)
CLIPS> (defrule rule-1 (data ?) =>)
CLIPS> (assert (data 1) (data 2) (data 3))
<Fact-3>
CLIPS> (watch statistics)
CLIPS> (run)
3 rules fired Run time is 1.60000054165721e-05 seconds.
187499.936524567 rules per second.
4 mean number of facts (4 maximum).
1 mean number of instances (1 maximum).
2 mean number of activations (3 maximum).
CLIPS>
Related
The LHS of a rule R_blup contains
(test (>= ?s2 2))
that is, it checks if ?s2 is greater or equal to 2. ?s2 corresponds to an instance slot named s2.
Unfortunately, I get the error
Function >= expected argument #1 to be of type integer or float
The problem is that my code executes the (test ... before I can set argument #1, i.e. before I can set s2 to an integer or float value. s2 is supposed to be set to an integer inside a python-call that is triggered by another rule R_blah.
The error is triggered in the middle of another python-call belonging to another rule R_xyz. This python-call modifies an instance via clips_instance.Slots["slot_name"] = some_value.
How is this normally handled? I see three solutions I don't like too much:
Setting a default (integer) value for s2.
Modifying the (test ... to check against nil first.
Adding another check/rule to wait until s2 is not nil any more
Is it maybe possible to try/except/pass the error?
Use the function object-pattern-match-delay to delay pattern matching to create an atomic operation for a series of changes:
CLIPS> (defclass POINT (is-a USER) (slot x) (slot y))
CLIPS>
(defrule check
(object (is-a POINT) (x ?s2))
(test (>= ?s2 2))
=>)
CLIPS> (make-instance [p1] of POINT)
[ARGACCES5] Function >= expected argument #1 to be of type integer or float
[DRIVE1] This error occurred in the join network
Problem resides in associated join
Of pattern #1 in rule check
[p1]
CLIPS> (agenda)
CLIPS>
(object-pattern-match-delay
(make-instance [p2] of POINT)
(make-instance [p3] of POINT)
(send [p2] put-x 3)
(send [p3] put-x 0))
0
CLIPS> (agenda)
0 check: [p2]
For a total of 1 activation.
CLIPS>
Is it possible to cause CLIPS to re-evaluate the value of a global variable in a defrule? I have this:
(defrule encourage "Do we have a GPA higher than 3.7?"
(test (> (gpa) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
gpa is function that calculates and returns a number based on two global variables (grade points and number of credits). I read somewhere that changes to global variables do not invoke pattern matching. How do I go about forcing this? I want to print that string every time I do (run) as long as the GPA is higher than 3.7.
Don't attempt to use global variables or function calls in this manner. First, global variables are specifically designed to not trigger pattern matching. Second, it would take a bit of magic for CLIPS to know when a function call needs to be reevaluated as there are any number of changes which could cause a function to return a different value, not just changes to globals. If you want a particular piece of information to trigger pattern matching, then stick it in a fact or instance. It will make your code easier to understand if you parameterize the function calls and bind the values to be used as arguments in the conditions of the rule.
CLIPS> (clear)
CLIPS>
(deffunction gpa (?grade-points ?number-of-credits)
(/ ?grade-points ?number-of-credits))
CLIPS>
(defrule encourage "Do we have a GPA higher than 3.7?"
(grade-points ?gp)
(number-of-credits ?noc)
(test (> (gpa ?gp ?noc) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
CLIPS> (assert (grade-points 35) (number-of-credits 10))
<Fact-2>
CLIPS> (agenda)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (grade-points 35)
f-2 (number-of-credits 10)
For a total of 3 facts.
CLIPS> (retract 1)
CLIPS> (assert (grade-points 38))
<Fact-3>
CLIPS> (agenda)
0 encourage: f-3,f-2
For a total of 1 activation.
CLIPS>
Alternately, you can use the fact query functions to iterate over a group of facts to dynamically compute the gpa based on facts rather than globals. Each time you modify one of these facts (add or remove), you can also assert a fact indicating the gpa needs to be rechecked to trigger the encourage rule.
CLIPS> (clear)
CLIPS>
(deftemplate grade
(slot class)
(slot grade-points)
(slot credits))
CLIPS>
(deffunction gpa ()
(bind ?grade-points 0)
(bind ?credits 0)
(do-for-all-facts ((?g grade)) TRUE
(bind ?grade-points (+ ?grade-points ?g:grade-points))
(bind ?credits (+ ?credits ?g:credits)))
(if (= ?credits 0)
then 0
else (/ ?grade-points ?credits)))
CLIPS>
(defrule encourage
?f <- (check-gpa)
=>
(retract ?f)
(if (> (gpa) 3.7)
then
(printout t "Keep up the excellent work!" crlf)))
CLIPS> (gpa)
0
CLIPS> (assert (check-gpa))
<Fact-1>
CLIPS> (run)
CLIPS> (assert (grade (class Algebra) (grade-points 12) (credits 3)))
<Fact-2>
CLIPS> (gpa)
4.0
CLIPS> (assert (check-gpa))
<Fact-3>
CLIPS> (run)
Keep up the excellent work!
CLIPS> (assert (grade (class History) (grade-points 6) (credits 2)))
<Fact-4>
CLIPS> (gpa)
3.6
CLIPS> (assert (check-gpa))
<Fact-5>
CLIPS> (run)
CLIPS> (assert (grade (class Science) (grade-points 12) (credits 3)))
<Fact-6>
CLIPS> (gpa)
3.75
CLIPS> (assert (check-gpa))
<Fact-7>
CLIPS> (run)
Keep up the excellent work!
CLIPS>
If I have a bunch of facts like (example (fact 1)), (example (fact 2)), (example (fact 3)), and have another list of facts like (myfact (number 2)), how can I perform a printout on each item in the first list that is not in the second (based on the number in the fact/number slots)? I suspect I need do-for-all-facts, but I'm not sure exactly how. Here's my incomplete code:
(deffunction difference ()
(do-for-all-facts ((?f1 example)) TRUE
(find-all-facts ((?f2 myfact)) (eq 1 1))
(if (somehow check if ?f1:fact does not equal ANY of number slots in ?f2) then
(printout t "..." crlf))))
CLIPS> (clear)
CLIPS> (deftemplate example (slot fact))
CLIPS> (deftemplate myfact (slot number))
CLIPS>
(deffacts start
(example (fact 1))
(example (fact 2))
(example (fact 3))
(myfact (number 2))
(myfact (number 4)))
CLIPS>
(deffunction difference ()
(do-for-all-facts ((?f1 example))
(not (any-factp ((?f2 myfact)) (eq ?f1:fact ?f2:number)))
(printout t "difference " ?f1:fact crlf)))
CLIPS> (reset)
CLIPS> (difference)
difference 1
difference 3
CLIPS>
(defrule difference
(example (fact ?n))
(not (myfact (number ?n)))
=>
(printout t "difference " ?n crlf))
CLIPS> (run)
difference 3
difference 1
CLIPS>
i am writing an expert system on cheese.
when reset all the facts about various cheese is loaded into the system and by asking question such as texture smell etc this will retract certain facts from the system.
My Question how do you keep track of the amount of rules in the system. I created a count but i was wondering if there was a way to see the amount of facts currently in the system when running a rule.
Any help would be appriciated
You can make a call to get-defrule-list or get-fact-list to determine the number of rules/facts present in the system:
CLIPS> (assert (a) (b) (c) (d))
<Fact-4>
CLIPS> (defrule x =>)
CLIPS> (defrule y =>)
CLIPS> (defrule z =>)
CLIPS> (length$ (get-defrule-list *))
3
CLIPS> (length$ (get-fact-list *))
5
CLIPS> (facts)
f-0 (initial-fact)
f-1 (a)
f-2 (b)
f-3 (c)
f-4 (d)
For a total of 5 facts.
CLIPS> (rules)
x
y
z
For a total of 3 defrules.
CLIPS>
From what I understand in "matching" step several rules might get "enabled" because their conditions are satisfied by the facts in WM. However I thought in conflict resolution step only one of the rules in agenda will be fired.
Now I have a program in which 2 rules are enabled into agenda and in run step both get fired! Isn't it supposed that only one rule be fired?
CLIPS> (defrule testrule1 (declare (salience 1))
(testfact1) (testfact2) => (printout t "testrule1 firing." crlf))
CLIPS> (defrule testrule2
(testfact1) => (printout t "testrule2 firing." crlf))
CLIPS> (assert (testfact1) (testfact2))
==> f-1 (testfact1)
==> Activation 0 testrule2: f-1
==> f-2 (testfact2)
==> Activation 1 testrule1: f-1,f-2
<Fact-2>
CLIPS> (agenda)
1 testrule1: f-1,f-2
0 testrule2: f-1
For a total of 2 activations.
CLIPS> (run)
FIRE 1 testrule1: f-1,f-2
testrule1 firing.
FIRE 2 testrule2: f-1
testrule2 firing.
CLIPS>
Conflict resolution does not prevent both rules from firing - it just determines which is fired first. If you only want one of the two rules to fire, then you should either retract testfact1 in the RHS of the selected rule or remove the other rule from the agenda by some other means (e.g., using a control fact).