Tuesday, January 7, 2014

Pattern Matching Constants in CLIPS

In CLIPS, there are three basic templates for matching a constant in a rule pattern. The constant, in this case 387, can be placed directly in the pattern as a literal constraint:
(defrule rule-387
   (data (value 387))
   =>)
A predicate constraint can be used to evaluate an expression testing for equality:
(defrule rule-387
   (data (value ?x&:(eq ?x 387)))
   =>)
A test conditional element can be used to evaluate an expression testing for equality:
(defrule rule-387
   (data (value ?x))
   (test (eq ?x 387))
   =>)
An empirical analysis can be performed to determine which template is most efficient as the number of rules and distinct constants increase. A common set of constructs will be used with each group of rules to trigger pattern matching as a single fact is repeatedly modified:
(deftemplate data
   (slot index)
   (slot value))

(deffacts start
   (data (index 1000000) (value 100000)))

(defrule loop
   (declare (salience 1))
   ?f <- (data (index ?i&~0))
   =>
   (modify ?f (index (- ?i 1)) (value (- ?i 1))))
Using CLIPS 6.24 to run the common rules in conjunction with groups of 1, 100, 400, 700, and 1000 rules using each of the three templates produces the following results.



The literal constraint is the most efficient, which is what you’d intuitively expect after an examination of the code. It directly compares a value in a fact to a value stored in the pattern network.

The predicate constraint and test conditional element are less efficient as they require a function evaluation. The predicate constraint is slightly more efficient because it’s evaluated at an earlier stage of pattern matching than the test conditional element. If all of the referenced variables in a predicate constraint are bound within the pattern, then the evaluation can be performed in the pattern network. Test conditional elements and expressions containing variables bound in other patterns are evaluated in the join network, which is primarily used to unify variable bindings across multiple patterns.

CLIPS 6.30 provides significantly better performance for literal constraints:



In situations where multiple patterns satisfy the criteria for sharing, literal constraints are not evaluated one at a time. Instead the next node in the pattern network is determined using a hashing algorithm which requires just a single computation regardless of the number of literal constraints. So for this particular benchmark, the execution time does not increase for literal constraints as the number of rules and distinct literals is increased.

Bottom line: regardless of which version of CLIPS you’re using, use literal constraints rather than predicate constraints or test conditional elements.

The code for these benchmarks is available here.

No comments:

Post a Comment