My goal is simple: to trigger a rule again using the same facts that previously triggered it. How can I refresh a fact to do this quickly?
You can use the refresh command to place activations for a rule back on the agenda.
CLIPS (6.31 6/12/19)
CLIPS>
(defrule xy
(x ?x)
(y ?y)
=>)
CLIPS> (assert (x 1) (x 2) (y 1) (y 2))
<Fact-4>
CLIPS> (agenda)
0 xy: f-1,f-4
0 xy: f-2,f-4
0 xy: f-1,f-3
0 xy: f-2,f-3
For a total of 4 activations.
CLIPS> (run)
CLIPS> (agenda)
CLIPS> (refresh xy)
CLIPS> (agenda)
0 xy: f-2,f-3
0 xy: f-1,f-3
0 xy: f-2,f-4
0 xy: f-1,f-4
For a total of 4 activations.
CLIPS>
Related
I have the following deftemplate
(deftemplate potential
(multislot values (type INTEGER))
)
and I want to remove instances of this deftemplate if an integer only occurs in it.
Example:
fact-1: potential 1 2 3
fact-2: potential 2 3 4
fact-3: potential 2 3 4 5
I want to retract fact-1 and fact-3 because they include 1 and 5 uniquely.
I'm trying to achieve it using a rule like the following:
1 (defrule remove_if_only_option
2 ?p<-(potential (values $? ?value $? ))
3 (not (exists (potential (values $? ?value $?) )))
4 =>
5 (retract ?p)
7 )
Obviously it doesn't work as line 3 can match the initial fact. Is there any way to make this run in such a way the rule doesn't consider ?p for the rest of it?
Thanks.
Neither of the patterns in your rule will be effected by the presence or absence of the initial-fact since they both match potential facts. The exists conditional element is superfluous in the second pattern, so your rule is equivalent to
(defrule remove_if_only_option
?p <- (potential (values $? ?value $?))
(not (potential (values $? ?value $?)))
=>
(retract ?p))
and since the condition x and not x is never true, this rule can never be satisfied.
You can tell if two facts of the same type are different by comparing their fact address, but since you can't bind a fact address within a not conditional element, you can't do that in this case. Alternatively, you can include a slot containing a unique value for each fact that can be used to tell if the facts are different:
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER)))
CLIPS>
(defrule remove_if_only_option
?p <- (potential (id ?id) (values $? ?value $?))
(not (potential (id ~?id) (values $? ?value $?)))
=>
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (agenda)
0 remove_if_only_option: f-3,*
0 remove_if_only_option: f-1,*
For a total of 2 activations.
CLIPS>
Initially, this appears to work, but once it runs you can see there are issues:
CLIPS> (run 1)
CLIPS> (agenda)
0 remove_if_only_option: f-2,*
0 remove_if_only_option: f-1,*
For a total of 2 activations.
CLIPS>
Once f-3 is removed, the value 4 in f-2 now becomes unique and so this fact will now also be removed by this rule. The problem is that the common set of values is implicitly represented by the collection of potential facts, and once you start removing them you're altering the common set of values.
In order to do this, you'll need at least two steps and consequently at least two rules. One way to do it is to mark the facts that need to be deleted in one step and then delete them in another:
CLIPS> (clear)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER))
(slot delete (default no)))
CLIPS>
(defrule remove_if_only_option
(not (done))
?p <- (potential (id ?id) (values $? ?value $?) (delete no))
(not (potential (id ~?id) (values $? ?value $?)))
=>
(modify ?p (delete yes)))
CLIPS>
(defrule remove
(declare (salience -10))
?p <- (potential (delete yes))
=>
(assert (done))
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (agenda)
0 remove_if_only_option: *,f-3,*
0 remove_if_only_option: *,f-1,*
For a total of 2 activations.
CLIPS> (run 1)
CLIPS> (agenda)
0 remove_if_only_option: *,f-1,*
-10 remove: f-4
For a total of 2 activations.
CLIPS> (facts)
f-0 (initial-fact)
f-1 (potential (id gen4) (values 1 2 3) (delete no))
f-2 (potential (id gen5) (values 2 3 4) (delete no))
f-4 (potential (id gen6) (values 2 3 4 5) (delete yes))
For a total of 4 facts.
CLIPS> (run 1)
CLIPS> (agenda)
-10 remove: f-5
-10 remove: f-4
For a total of 2 activations.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-2 (potential (id gen5) (values 2 3 4) (delete no))
f-6 (done)
For a total of 3 facts.
CLIPS>
Another way to create a fact containing the unique values:
CLIPS> (clear)
CLIPS>
(deftemplate potential
(slot id (default-dynamic (gensym*)))
(multislot values (type INTEGER)))
CLIPS>
(defrule add-to-unique
(not (done))
?c <- (unique $?unique)
(potential (id ?id) (values $? ?value $?))
(not (potential (id ~?id) (values $? ?value $?)))
(test (not (member$ ?value ?unique)))
=>
(retract ?c)
(assert (unique $?unique ?value)))
CLIPS>
(defrule remove_if_only_option
(declare (salience -10))
(unique $?unique)
?p <- (potential (values $? ?value $?))
(test (member$ ?value ?unique))
=>
(assert (done))
(retract ?p))
CLIPS>
(assert (potential (values 1 2 3))
(potential (values 2 3 4))
(potential (values 2 3 4 5)))
<Fact-3>
CLIPS> (assert (unique))
<Fact-4>
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-2 (potential (id gen26) (values 2 3 4))
f-6 (unique 5 1)
f-7 (done)
For a total of 4 facts.
CLIPS>
I'm trying to write a general rule that will activate when two facts like these two are present:
(Vector v1 3 4 5)
(Vector v2 1 3 10 15 5 2 4)
(Elements 4 5 3)
So, my problem is that I don't know how to match ALL the unordered elements in the vector, in order to fire the rule.
I want the rule to activate only when ALL the elements from Elements are present, not taking in consideration if they follow the same order.
I haven't been able to achieve it, so I ask for help.
Examples of rules not doing what I want:
(defrule Equal
(Elements $?x)
(Vector ?name $?y)
(test (member$ $?x $?y))
=>
(printout t ?name crlf)
)
*The problem of this one is that it fires when both are blank, and mainly when a single member of ?x is contained in ?y, but I want the rule to fire when ALL elements in ?x are in ?y.
I tried using this simplier one too:
(defrule Equal
(Elements $? $?x $?)
(Vector ?name $? $?y $?)
=>
(printout t ?name crlf)
)
But in this case the rule only activates when the elements are exactly the same and ordered in the same way, but I want to have the flexibility of elements not having to be ordered exactly as they appear in the vector.
Use the subsetp function rather than member$:
CLIPS (6.31 4/1/19)
CLIPS>
(defrule equal
(elements $?elements)
(test (> (length$ ?elements) 0))
(vector ?name $?values)
(test (subsetp ?elements ?values))
=>
(printout t ?name crlf))
CLIPS>
(assert (vector v1 3 4 5)
(vector v2 1 3 10 15 5 2 4)
(vector v3 4 5 7 2)
(elements 4 5 3))
<Fact-4>
CLIPS>
(agenda)
0 equal: f-4,f-2
0 equal: f-4,f-1
For a total of 2 activations.
CLIPS> (run)
v2
v1
CLIPS>
You can also do it this way without a function call:
CLIPS> (clear)
CLIPS>
(defrule equal
(elements ? $?)
(vector ?name $?list)
(forall (elements $? ?v $?)
(vector ?name $? ?v $?))
=>
(printout t ?name crlf))
CLIPS>
(assert (vector v1 3 4 5)
(vector v2 1 3 10 15 5 2 4)
(vector v3 4 5 7 2)
(elements 4 5 3))
<Fact-4>
CLIPS> (agenda)
0 equal: f-4,f-2,*
0 equal: f-4,f-1,*
For a total of 2 activations.
CLIPS> (run)
v2
v1
CLIPS>
Let's say I have some facts (I do not know how many there are) like this: lamp x is off. With a defrule I proggressively turn all lamps on so every fact will be: lamp x is on. How do I check every lamp that is on. I know that if there were three lamps I could write:
(defrule checkAllLamps
(lamp 1 is on)
(lamp 2 is on)
(lamp 3 is on)
=>
(printout t "All lamps are on now")
)
But for x lamps?
Thank you!
You can use fact-set query functions for that (chapter 12.9.12 of the Basic Programming Guide).
(deftemplate lamp
(slot id (type INTEGER))
(slot state (type SYMBOL)))
(defrule all-lamps-are-on
(lamp (state on))
(test (>= (length$ (find-all-facts ((?l lamp)) (eq ?l:state on))) 3))
=>
(printout t "All lamps are on" crlf))
Here's how you can check whether all of the lamps are on. The checkAllLamps rule treats the case where there are no lamps at all as all lamps being on, whereas the checkAllLampsAtLeastOne rule requires that there is at least one lamp that is on.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule checkAllLamps
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS>
(defrule checkAllLampsAtLeastOne
(exists (lamp ? is on))
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS> (agenda)
0 checkAllLamps: *
For a total of 1 activation.
CLIPS> (assert (lamp 1 is on))
<Fact-1>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS> (assert (lamp 2 is off))
<Fact-2>
CLIPS> (agenda)
CLIPS> (retract 2)
CLIPS> (assert (lamp 2 is on))
<Fact-3>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS>
I want to build an expert system in which in a case of emergency at a building with some floors (it needs to work for any amount of floors) the elevator should take the people into the ground.
The thing is, that the defrule to send the elevator at any floor never makes it in the agenda, so the system just does nothing. The correct action should be to fire the rule and then another rule that takes the people from the floor.
The code for the defrule is this:
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors))
)
The facts as they have been initialized from the user in another defrule above are these:
f-0 (initial-fact)
f-1 (elevator is_at 0 has 0 adults and 0 minors)
f-3 (capacity 4)
f-4 (floors 3)
f-5 (initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
f-6 (floor 3 has 2 adult and 1 minor people)
f-7 (floor 2 has 4 adult and 5 minor people)
f-8 (floor 1 has 1 adult and 2 minor people)
I can't seem to find the solution. Also, I'm using deffacts and not deftemplate as I have seen many people using on the internet.
You can use the matches command to see which patterns in a rule are matched.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors)))
CLIPS>
(deffacts initial
(elevator is_at 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (matches move_to_floor)
Matches for Pattern 1
None
Matches for Pattern 2
f-5
f-6
f-7
Partial matches for CEs 1 - 2
None
Activations
None
(3 0 0)
CLIPS>
In this case, the first pattern is not matched. That's because your pattern expects is_at floor ?x but your fact contains is_at 0 (the symbol floor is missing in your fact). If you correct this issue, the rule will be placed on the agenda.
CLIPS>
(deffacts initial
(elevator is_at floor 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
CLIPS>
If you issue a (run) command at this point, the rules will endlessly fire in a loop moving from floor to floor, so that's something you'll need to address next.
If you use deftemplate facts rather than ordered facts, you'll get an error if you misspell slot names, so it's better to use these if you have a fact with multiple attributes.
CLIPS> (clear)
CLIPS>
(deftemplate elevator
(slot at_floor (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deftemplate floor
(slot # (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deffacts initial
(elevator (at_floor 0))
(capacity 4)
(floors 3)
(initCanEnter 0)
(floor (# 3) (adults 2) (minors 1))
(floor (# 2) (adults 4) (minors 5))
(floor (# 1) (adults 1) (minors 2)))
CLIPS>
(defrule move_to_floor
?i <- (elevator (at_floor ?x))
(floor (# ?fl&~?x) (adults ?n) (minors ?m))
(test (> (+ ?n ?m) 0))
=>
(modify ?i (at_floor ?fl)))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (elevator (at_floor 0) (adults 0) (minors 0))
f-2 (capacity 4)
f-3 (floors 3)
f-4 (initCanEnter 0)
f-5 (floor (# 3) (adults 2) (minors 1))
f-6 (floor (# 2) (adults 4) (minors 5))
f-7 (floor (# 1) (adults 1) (minors 2))
For a total of 8 facts.
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
CLIPS>
I want to write a rule that says the following
if x > y => assert x
where x and y are variables and their values are given as facts.
How do I do it?
If x already exists as a fact, then asserting it again from the actions of the rule would be unnecessary, but if you want to assert a fact indicating that x is greater than y then you could do it this way:
CLIPS>
(defrule greater-than
(x ?x)
(y ?y)
(test (> ?x ?y))
=>
(assert (x-is-greater-than-y)))
CLIPS> (assert (x 4))
<Fact-1>
CLIPS> (assert (y 1))
<Fact-2>
CLIPS> (agenda)
0 greater-than: f-1,f-2
For a total of 1 activation.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (x 4)
f-2 (y 1)
f-3 (x-is-greater-than-y)
For a total of 4 facts.
CLIPS>