1
votes

I'm a newbie in expert systems and in CLIPS in particular (besides in English language too :-) ).

I'm trying to find a maximum (in some meaning) among facts using complicated comparsion predicate. A comparsion predicate is implemented as a function which takes two facts as an arguments. In simple words I need CLIPS to run over all facts, make comparsion using my predicate and give back one fact that is maximum.

One solution might be to evaluate next expression using CLIPS rules:

( Exists x) and not ((Exists y)(y != x) and (y>x))

where ">" stands for my predicate, x and y are facts, like that:

(deftemplate fact (slot name ... ) ... )
(deffunction my-predicate ""
(?fact1 ?fact2)
...
)
(defrule find-max ""
    ?fact1 <- (fact (name ?name1) )
    ( not (test (my-predicate ?fact1 (fact (name ?name2 )))))
=>
...
)

But this example isn't working because of error message about 2nd argument in my-predicate function call.

Another solution is to make intermediate facts that represents base facts with particular name and value of comparsion function for predicate. But this will need several iterations to accomplish final result.

I prefer 1st solution but i don't understand how to code it. I think I need something like in answer to this question. The difference is that my question is about comparsion of whole fact, not just one slot.

Is it possible to find maximum for one activation of rule? If possible, please explain how.

1

1 Answers

8
votes

Here's how you would typically do it with a simple comparison:

CLIPS> (clear)
CLIPS> 
(deftemplate fact (slot name) (slot value))
CLIPS> 
(deffacts test-data
   (fact (name data-1) (value 3))
   (fact (name data-2) (value 1))
   (fact (name data-3) (value 2))
   (fact (name data-4) (value 2))
   (fact (name data-5) (value 4))
   (fact (name data-6) (value 3)))
CLIPS> 
(defrule find-max-value
   (fact (name ?name1) (value ?value1))
   (not (fact (value ?value2&:(> ?value2 ?value1))))
   =>
   (printout t "Fact " ?name1 " is the maximum" crlf))
CLIPS> (reset)
CLIPS> (run)
Fact data-5 is the maximum
CLIPS> 

The same basic approach also works if you bind all of the slot values needed for comparison and pass them into a function:

CLIPS> (clear)
CLIPS> 
(deftemplate fact (slot name) (slot value))
CLIPS> 
(deffacts test-data
   (fact (name data-1) (value 3))
   (fact (name data-2) (value 1))
   (fact (name data-3) (value 2))
   (fact (name data-4) (value 2))
   (fact (name data-5) (value 4))
   (fact (name data-6) (value 3)))
CLIPS> 
(deffunction my-predicate (?value1 ?value2)
   (> ?value1 ?value2))
CLIPS>    
(defrule find-max-value
   (fact (name ?name1) (value ?value1))
   (not (fact (value ?value2&:(my-predicate ?value2 ?value1))))
   =>
   (printout t "Fact " ?name1 " is the maximum" crlf))
CLIPS> (reset)
CLIPS> (run)
Fact data-5 is the maximum
CLIPS> 

Passing in a fact to the function, however, is problematic because you can't bind the pattern to a fact address inside a not conditional element. If the fact has a slot that serves as a unique identifier, you can use this to retrieve a pointer to the fact (although I wouldn't recommend this approach):

CLIPS> (clear)
CLIPS> 
(deftemplate fact (slot name) (slot value))
CLIPS> 
(deffacts test-data
   (fact (name data-1) (value 3))
   (fact (name data-2) (value 1))
   (fact (name data-3) (value 2))
   (fact (name data-4) (value 2))
   (fact (name data-5) (value 4))
   (fact (name data-6) (value 3)))
CLIPS> 
(deffunction my-predicate (?fact1 ?fact2)
   (> (fact-slot-value ?fact1 value) (fact-slot-value ?fact2 value)))
CLIPS> 
(defrule find-max-value
   ?fact1 <- (fact (name ?name1))
   (not (fact (name ?name2&:(my-predicate (nth$ 1 (find-fact ((?fact2 fact)) 
                                                               (eq ?fact2:name ?name2))) 
                                            ?fact1))))
   =>
   (printout t "Fact " ?name1 " is the maximum" crlf))
CLIPS> (reset)
CLIPS> (run)
Fact data-5 is the maximum
CLIPS> 

What I would suggest doing is to use the fact query functions to iterate over the facts to find the maximum value:

CLIPS> (clear)
CLIPS> 
(deftemplate fact (slot name) (slot value))
CLIPS> 
(deffacts test-data
   (fact (name data-1) (value 3))
   (fact (name data-2) (value 1))
   (fact (name data-3) (value 2))
   (fact (name data-4) (value 2))
   (fact (name data-5) (value 4))
   (fact (name data-6) (value 3)))
CLIPS> 
(deffunction my-predicate (?fact1 ?fact2)
   (> (fact-slot-value ?fact1 value) (fact-slot-value ?fact2 value)))
CLIPS> 
(deffunction find-max (?template ?predicate)
   (bind ?max FALSE)
   (do-for-all-facts ((?f ?template)) TRUE
      (if (or (not ?max) (funcall ?predicate ?f ?max))
         then
         (bind ?max ?f)))
   (return ?max))
CLIPS>    
(reset)
CLIPS> (facts)
f-0     (initial-fact)
f-1     (fact (name data-1) (value 3))
f-2     (fact (name data-2) (value 1))
f-3     (fact (name data-3) (value 2))
f-4     (fact (name data-4) (value 2))
f-5     (fact (name data-5) (value 4))
f-6     (fact (name data-6) (value 3))
For a total of 7 facts.
CLIPS> (find-max fact my-predicate)
<Fact-5>
CLIPS> 
(defrule find-max
   =>
   (bind ?fact (find-max fact my-predicate))
   (if ?fact
      then
      (printout t "Fact " (fact-slot-value ?fact name) " is the maximum" crlf)))
CLIPS> (run)
Fact data-5 is the maximum
CLIPS> 

Although CLIPS does not, many languages do support accumulation conditional elements which makes it easier to perform this type of calculation. For example, in Jess you can do this:

Jess> 
(deftemplate fact (slot name) (slot value))
TRUE
Jess> 
(deffacts test-data
   (fact (name data-1) (value 3))
   (fact (name data-2) (value 1))
   (fact (name data-3) (value 2))
   (fact (name data-4) (value 2))
   (fact (name data-5) (value 4))
   (fact (name data-6) (value 3)))
TRUE
Jess> (if FALSE then 3 else 4)
4
Jess> 
(defrule find-max-value
   ?c <- (accumulate (bind ?max FALSE)
                     (if (or (not ?max) (> ?value ?max))
                         then (bind ?max ?value))
                     ?max
                     (fact (value ?value)))
   =>
   (printout t "The max is " ?c crlf))
TRUE
Jess> (reset)
TRUE
Jess> (run)
The max is 4
1
Jess>