Search code examples
clips

How to sum integer in multislot fields trying all the possibile combinations in CLIPS


I have a situation like this:

(deftemplate trip
   (multislot place-sequence)
   (multislot days-distribution)
)

(deftemplate travel-banchmark
    (slot name)
    (slot value)
)

(trip (place-sequence milano roma venezia) (days-distribution 1 1 1))
(trip (place-sequence roma milano venezia) (days-distribution 1 1 1))
(travel-banchmark (name travel-duration) (value 5))

Now for every trip-fact I have to assert all the possible trip with different days-distribution (the sum of days-distribution needs to be the travel-duration (e.g., 5))

Example:

(trip (place-sequence milano roma venezia) (days-distribution 3 1 1))
(trip (place-sequence milano roma venezia) (days-distribution 1 3 1))
(trip (place-sequence milano roma venezia) (days-distribution 1 1 3))
(trip (place-sequence milano roma venezia) (days-distribution 2 2 1))
(trip (place-sequence milano roma venezia) (days-distribution 1 1 2))
...

Is it possible to do this using rules? I have some problem in understanding the best way to do this kind of things with a rule-based system

Edit: This is my way to calculate the sum inside the multislot but I still have a problem figuring out how to calculate the different days-distrubtion

(defrule test
   (travel-banchmark (name travel-duration) (value ?duration))
   ?p <- (trip
       (days-distribution $?d))
       (test (<= (+ 0 (expand$ ?d)) ?duration))
   =>
   ...
)

Solution

  • You don't have to use rules to do everything, particularly if there's an obvious algorithmic solution. For example, it doesn't make sense to do this:

    (defrule hello
       ?f <- (count ?c&:(> ?c 0))
       =>
       (printout t "Hello" crlf)
       (retract ?f)
       (assert (count (- ?c 1))))
    

    When you can do this:

    (deffunction hello (?count)
       (loop-for-count ?count (printout t "Hello" crlf)))
    

    Generating the distributions using a recursive function call is pretty straightforward and can do so from a single rule firing without having to incrementally build the solution and then remove the intermediate steps.

             CLIPS (6.31 6/12/19)
    CLIPS> 
    (deftemplate trip
       (multislot place-sequence)
       (multislot days-distribution))
    CLIPS> 
    (deftemplate travel-banchmark
        (slot name)
        (slot value))
    CLIPS> 
    (deffacts initial
       (travel-banchmark (name travel-duration) (value 5))
       (trip (place-sequence milano roma venezia) (days-distribution)))
    CLIPS> 
    (deffunction create-distributions (?cc ?cities ?days ?duration $?distribution)
       (bind ?max-alloc (- ?duration ?days (- ?cc 1)))   
       (if (= ?cc 1)
          then
          (assert (trip (place-sequence ?cities) (days-distribution ?distribution ?max-alloc)))
          (return))
       (loop-for-count (?a ?max-alloc)
          (create-distributions (- ?cc 1) ?cities (+ ?days ?a) ?duration ?distribution ?a)))
    CLIPS> 
    (defrule test
        (travel-banchmark (name travel-duration) (value ?duration))
        ?p <- (trip (place-sequence $?cities) (days-distribution))
        =>
        (bind ?city-count (length$ ?cities))
        (create-distributions ?city-count ?cities 0 ?duration)
        (retract ?p))
    CLIPS> (reset)
    CLIPS> (run)
    CLIPS> (facts)
    f-0     (initial-fact)
    f-1     (travel-banchmark (name travel-duration) (value 5))
    f-3     (trip (place-sequence milano roma venezia) (days-distribution 1 1 3))
    f-4     (trip (place-sequence milano roma venezia) (days-distribution 1 2 2))
    f-5     (trip (place-sequence milano roma venezia) (days-distribution 1 3 1))
    f-6     (trip (place-sequence milano roma venezia) (days-distribution 2 1 2))
    f-7     (trip (place-sequence milano roma venezia) (days-distribution 2 2 1))
    f-8     (trip (place-sequence milano roma venezia) (days-distribution 3 1 1))
    For a total of 8 facts.
    CLIPS>