In my program I have the following situation:
Template:
(deftemplate MAIN::travel-banchmark
(slot name)
(slot value)
)
Facts:
(travel-banchmark (name location) (value torino))
(travel-banchmark (name location) (value roma))
(travel-banchmark (name location) (value milano))
(travel-banchmark (name location) (value venezia))
I have to create (with a rule) all the possible subset of, as an example, k=3 (n.b., k is a variable) location (k-combination) and for every subset I have to permute the elements inside it (and assert every permutation).
Example combination:
torino roma milano
torino roma venezia
roma milano venezia
venezia milano torino
Example permutation:
torino roma milano -> roma torino milano -> torino milano roma ...
I'm wondering if there is something that can I do in the LHS of the rule in order to avoid writing all the logic in the RHS?
Any suggestion of useful syntax that can I use?
Here's one way to do it using rules:
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate travel-banchmark
(slot name)
(slot value))
CLIPS>
(deftemplate permutation
(multislot values))
CLIPS>
(deffacts initial
(k-combination 3)
(travel-banchmark (name location) (value torino))
(travel-banchmark (name location) (value roma))
(travel-banchmark (name location) (value milano))
(travel-banchmark (name location) (value venezia)))
CLIPS>
(defrule first-in-permutation
(k-combination ~0)
(travel-banchmark (name location) (value ?city))
=>
(assert (permutation (values ?city))))
CLIPS>
(defrule next-in-permutation
(k-combination ?k)
?p <- (permutation (values $?cities))
(test (< (length$ ?cities) ?k))
(travel-banchmark (name location) (value ?city))
(test (not (member$ ?city ?cities)))
=>
(assert (permutation (values ?cities ?city))))
CLIPS>
(defrule cleanup
(declare (salience -5))
(k-combination ?k)
?p <- (permutation (values $?cities))
(test (< (length$ ?cities) ?k))
=>
(retract ?p))
CLIPS> (reset)
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (k-combination 3)
f-2 (travel-banchmark (name location) (value torino))
f-3 (travel-banchmark (name location) (value roma))
f-4 (travel-banchmark (name location) (value milano))
f-5 (travel-banchmark (name location) (value venezia))
f-8 (permutation (values venezia milano roma))
f-9 (permutation (values venezia milano torino))
f-11 (permutation (values venezia roma milano))
f-12 (permutation (values venezia roma torino))
f-14 (permutation (values venezia torino milano))
f-15 (permutation (values venezia torino roma))
f-18 (permutation (values milano venezia roma))
f-19 (permutation (values milano venezia torino))
f-21 (permutation (values milano roma venezia))
f-22 (permutation (values milano roma torino))
f-24 (permutation (values milano torino venezia))
f-25 (permutation (values milano torino roma))
f-28 (permutation (values roma venezia milano))
f-29 (permutation (values roma venezia torino))
f-31 (permutation (values roma milano venezia))
f-32 (permutation (values roma milano torino))
f-34 (permutation (values roma torino venezia))
f-35 (permutation (values roma torino milano))
f-38 (permutation (values torino venezia milano))
f-39 (permutation (values torino venezia roma))
f-41 (permutation (values torino milano venezia))
f-42 (permutation (values torino milano roma))
f-44 (permutation (values torino roma venezia))
f-45 (permutation (values torino roma milano))
For a total of 30 facts.
CLIPS>
For comparison, generating the permutations using a function:
CLIPS> (clear)
CLIPS>
(deftemplate travel-banchmark
(slot name)
(slot value))
CLIPS>
(deftemplate permutation
(multislot values))
CLIPS>
(deffacts initial
(k-combination 3)
(travel-banchmark (name location) (value torino))
(travel-banchmark (name location) (value roma))
(travel-banchmark (name location) (value milano))
(travel-banchmark (name location) (value venezia)))
CLIPS>
(deffunction gen-permutation (?k ?cities $?result)
(if (= ?k 0)
then
(assert (permutation (values ?result)))
(return))
(foreach ?c ?cities
(gen-permutation (- ?k 1) (delete-member$ ?cities ?c) ?result ?c)))
CLIPS>
(defrule generate
(k-combination ?k)
=>
(bind ?cities (create$))
(do-for-all-facts ((?tb travel-banchmark)) (eq ?tb:name location)
(bind ?cities (create$ ?cities ?tb:value)))
(gen-permutation ?k ?cities))
CLIPS> (reset)
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (k-combination 3)
f-2 (travel-banchmark (name location) (value torino))
f-3 (travel-banchmark (name location) (value roma))
f-4 (travel-banchmark (name location) (value milano))
f-5 (travel-banchmark (name location) (value venezia))
f-6 (permutation (values torino roma milano))
f-7 (permutation (values torino roma venezia))
f-8 (permutation (values torino milano roma))
f-9 (permutation (values torino milano venezia))
f-10 (permutation (values torino venezia roma))
f-11 (permutation (values torino venezia milano))
f-12 (permutation (values roma torino milano))
f-13 (permutation (values roma torino venezia))
f-14 (permutation (values roma milano torino))
f-15 (permutation (values roma milano venezia))
f-16 (permutation (values roma venezia torino))
f-17 (permutation (values roma venezia milano))
f-18 (permutation (values milano torino roma))
f-19 (permutation (values milano torino venezia))
f-20 (permutation (values milano roma torino))
f-21 (permutation (values milano roma venezia))
f-22 (permutation (values milano venezia torino))
f-23 (permutation (values milano venezia roma))
f-24 (permutation (values venezia torino roma))
f-25 (permutation (values venezia torino milano))
f-26 (permutation (values venezia roma torino))
f-27 (permutation (values venezia roma milano))
f-28 (permutation (values venezia milano torino))
f-29 (permutation (values venezia milano roma))
For a total of 30 facts.
CLIPS>
Related
How could i make a rule that test if all facts from a deftemplate but one matches an specific condition?
Example: with
(deftemplate person (field name)(field hair-color))
having several blonde people, only one is not
get
(printout t "Only " ?name-not-blond " is not blonde" crlf)
CLIPS (6.4 2/9/21)
CLIPS>
(deftemplate person
(slot name)
(slot hair-color))
CLIPS>
(defrule only-one-not-blonde
(person (name ?name-not-blonde)
(hair-color ~blonde))
(not (person (name ~?name-not-blonde)
(hair-color ~blonde)))
=>
(println "Only " ?name-not-blonde " is not blonde."))
CLIPS>
(deffacts initial
(person (name Sue) (hair-color blonde))
(person (name Frank) (hair-color blonde))
(person (name Josh) (hair-color brown)))
CLIPS> (reset)
CLIPS> (facts)
f-1 (person (name Sue) (hair-color blonde))
f-2 (person (name Frank) (hair-color blonde))
f-3 (person (name Josh) (hair-color brown))
For a total of 3 facts.
CLIPS> (agenda)
0 only-one-not-blonde: f-3,*
For a total of 1 activation.
CLIPS> (assert (person (name Anne) (hair-color red)))
<Fact-4>
CLIPS> (agenda)
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))
=>
...
)
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>
Ok, I have found an answer to my question:
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 1 1 1))
)
CLIPS>
(defrule test
(travel-banchmark (name travel-duration) (value ?duration))
?p <- (trip
(place-sequence $?cities)
(days-distribution $?days-distribution))
(test (< (+ 0 (expand$ ?days-distribution)) ?duration))
=>
(retract ?p)
(loop-for-count (?cnt1 1 (length$ ?days-distribution)) do
(bind ?new-days-distribution (replace$ ?days-distribution ?cnt1 ?cnt1 (+ (nth$ ?cnt1 ?days-distribution) 1)))
(assert (trip
(place-sequence ?cities)
(days-distribution ?new-days-distribution))
)
)
)
CLIPS>
(defrule clean
(declare (salience -5))
?p <- (trip
(place-sequence $?cities)
(days-distribution $?days-distribution))
?p2 <- (trip
(place-sequence $?cities)
(days-distribution $?days-distribution))
(test (neq ?p ?p2))
=>
(retract ?p)
)
CLIPS> (reset)
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (travel-banchmark (name travel-duration) (value 5))
f-6 (trip (place-sequence milano roma venezia) (days-distribution 2 1 2))
f-7 (trip (place-sequence milano roma venezia) (days-distribution 1 2 2))
f-8 (trip (place-sequence milano roma venezia) (days-distribution 1 1 3))
f-9 (trip (place-sequence milano roma venezia) (days-distribution 2 2 1))
f-10 (trip (place-sequence milano roma venezia) (days-distribution 1 3 1))
f-12 (trip (place-sequence milano roma venezia) (days-distribution 3 1 1))
For a total of 8 facts.
CLIPS>
I have a template:
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
Can I have rule which will
Check whether there are any claims with name same as the one in drule.
Check whether claim:field has value 'EmpName', if found then bind it to ?name.
Currently I have something as follows:
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
(claim (name ?name2)
(field $?pf2 'EmpName' $?)
(value $?pv2&:(= (length$ ?pf2) (length$ ?pv2)) ?name $?))
...
=>
(assert (success ?name))
But it will bind only if all the selected claims have a field EmpName. I only want to check those claims with name
mentioned in drule. And if any of those claims have field 'EmpName' then it should be bound. If not just continue. My
rule fails if some claims do not have EmpName field at all. Assumption is that some claim will have EmpName field
and value.
Desired input and output:
(assert
(claim (name 'Employee')
(field 'Company')
(value 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
Output:
(success 'Bob')
Sorry if it is very stupid, I tried using test$ and member$ on field. But could not figure it out. I am self-learning CLIPS in my free time.
CLIPS>
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
CLIPS>
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
CLIPS>
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1))
(claim (name ?name2))
(claim (name ?name1 | ?name2)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
=>
(assert (success ?name)))
CLIPS>
(deffacts initial
(claim (name 'Employee')
(field 'Company')
(value 'ABC'))
(claim (name 'Event')
(field 'EmpName' 'EventName' 'Company')
(value 'Bob' 'Conference' 'ABC'))
(drule (id '001')
(name1 'Employee')
(field1 'Company')
(value1 'ABC')
(name2 'Event')
(field2 'EventName')
(value2 'Conference')))
CLIPS> (reset)
CLIPS> (agenda)
0 drule: f-3,f-1,f-2,f-2
For a total of 1 activation.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (claim (name 'Employee') (field 'Company') (value 'ABC'))
f-2 (claim (name 'Event') (field 'EmpName' 'EventName' 'Company') (value 'Bob' 'Conference' 'ABC'))
f-3 (drule (name1 'Employee') (id '001') (field1 'Company') (value1 'ABC') (name2 'Event') (field2 'EventName') (value2 'Conference'))
f-4 (success 'Bob')
For a total of 5 facts.
CLIPS>
Let's consider the next trivial template:
(deftemplate person (ssn ?s))
I want to check that, if a person "is registered", there are no other person with same ssn, however, I've tried with something like:
(defrule repeated-person
(person (ssn ?s1))
(person (ssn ?s2))
(test (= ?s1 ?s2))
=>
(printout t "No, no, no..." clrf))
or even,
(defrule repeated-person
(person (ssn ?s))
(person (ssn ?s))
=>
(printout t "No, no, no..." clrf))
but it didn't work.
How can I accomplish something like that?
By default, you can't create duplicates of facts:
CLIPS (6.31 2/3/18)
CLIPS>
(deftemplate person
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS> (assert (person (SSN 123-45-6789)))
FALSE
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS>
You can change this behavior using the set-fact-duplication function:
CLIPS> (set-fact-duplication TRUE)
FALSE
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
f-2 (person (SSN 123-45-6789))
For a total of 3 facts.
CLIPS>
You can then write a rule which checks to see if there are two different facts with the same SSN:
CLIPS>
(defrule repeated-person
?f1 <- (person (SSN ?ss))
?f2 <- (person (SSN ?ss))
(test (< (fact-index ?f1) (fact-index ?f2)))
=>
(printout t "Duplicated SSN " ?ss crlf))
CLIPS> (agenda)
0 repeated-person: f-1,f-2
For a total of 1 activation.
CLIPS>
Since each fact has a unique fact index, the comparison in the test conditional element ensures that the facts matching the first and second patterns are not the same.
If we add another person with an identical SSN, we'll get multiple activations of the rule:
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (agenda)
0 repeated-person: f-1,f-3
0 repeated-person: f-2,f-3
0 repeated-person: f-1,f-2
For a total of 3 activations.
CLIPS>
We can dynamically assign a unique id to each created fact which allows to create "duplicate" facts even when facts duplication is disabled:
CLIPS> (clear)
CLIPS> (set-fact-duplication FALSE)
TRUE
CLIPS>
(deftemplate person
(slot id (default-dynamic (gensym*)))
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (id gen1) (SSN 123-45-6789))
f-2 (person (id gen2) (SSN 123-45-6789))
f-3 (person (id gen3) (SSN 123-45-6789))
For a total of 4 facts.
CLIPS>
We can then create a rule which prints a single message regardless of the number of people with the same SSN:
CLIPS>
(defrule repeated-person
(person (id ?id) (SSN ?ssn))
(not (person (id ?id2&:(< (str-compare ?id2 ?id) 0)) (SSN ?ssn)))
(exists (person (id ~?id) (SSN ?ssn)))
=>
(printout t "Duplicated SSN " ?ssn crlf))
CLIPS> (agenda)
0 repeated-person: f-1,*,*
For a total of 1 activation.
CLIPS> (run)
Duplicated SSN 123-45-6789
CLIPS>
I am using CLIPS for a project.
I am using this template A which has an attribute model and another template B which has an attribute model as well.
So what I want to achieve is based on the attribute model, return those facts of template A which has the same attribute model value as of facts from template B.
I tried using this format
(find-all-facts((?a template_A)(?b template_B))
(and
//condition to be met
)
)
it does give me the results, but it is giving me both the results for A and B which are duplicates.. How do I make it in a way it returns non duplicate values, either A or B?
CLIPS>
(deftemplate template_A
(slot model))
CLIPS>
(deftemplate template_B
(slot model))
CLIPS>
(deffacts start
(template_A (model 1))
(template_A (model 2))
(template_A (model 3))
(template_B (model 2))
(template_B (model 3))
(template_B (model 4)))
CLIPS>
(deffunction extract-every-nth-value (?values ?start ?increment)
(bind ?rv (create$))
(while (<= ?start (length$ ?values))
(bind ?rv (create$ ?rv (nth$ ?start ?values)))
(bind ?start (+ ?start ?increment)))
(return ?rv))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (template_A (model 1))
f-2 (template_A (model 2))
f-3 (template_A (model 3))
f-4 (template_B (model 2))
f-5 (template_B (model 3))
f-6 (template_B (model 4))
For a total of 7 facts.
CLIPS>
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
(<Fact-2> <Fact-4> <Fact-3> <Fact-5>)
CLIPS>
(extract-every-nth-value
(find-all-facts ((?a template_A)(?b template_B))
(eq ?a:model ?b:model))
1 2)
(<Fact-2> <Fact-3>)
CLIPS>