Can we bind multislot values dynamically? - clips

I am trying to get values out of multislots and dynamically assign it to variables.
Suppose we have a template as follows:
(fact (slot name)
(multislot field)
(multislot value))
(fact2 (slot field)
(slot value))
Then can have a rule such as:
(rule
(fact (name ?name)
(field $?field)
(value $?value))
;if length of field and value is greater than 0
(fact2 (field ?field1)
(value ?value1))
;if length of field and value is greater than 1
(fact2 (field ?field2)
(value ?value2))
;if length of field and value is greater than 2
(fact2 (field ?field3)
(value ?value3))
;if length of field and value is greater than 3
(fact2 (field ?field4)
(value ?value4))
=>
(assert (all field:values found))
Here the fact can have just one field value pair or at max 4. I want a generic single rule which can be used to extract all the available values from multislot dynamically.
I guess in the above rule, we need not only to extract values but also implement a if length > 0, else-if kinda statements.
So if Input is:
(fact (name Employee)
(field Name Role Department Company)
(value Bob Admin Security ABC))
(fact2 (field Name)
(value Bob))
(fact2 (field Role)
(value Admin))
(fact2 (field Department)
(value Security))
(fact2 (field Company)
(value ABC))
Then expected output will be:
(all field:value pairs found)
while
(fact (name Employee)
(field Name Role)
(value Bob Admin))
(fact2 (field Name)
(value Bob))
(fact2 (field Role)
(value Admin))
While the same rule should also match the above facts. Is this possible? Or is there any alternative solution to the problem?
Thank you in advance.

You can essentially iterate over each field/value using the forall conditional element. If the name slot dose not contain a unique value and you have multiple fact facts, you'll need to add a slot containing a unique value if you want to reason about the fact in the actions of the rule so that the fact pattern outside and inside the forall conditional element match the same fact.
CLIPS (6.31 4/1/19)
CLIPS>
(deftemplate fact
(slot name)
(slot id (default-dynamic (gensym*)))
(multislot field)
(multislot value))
CLIPS>
(deftemplate fact2
(slot field)
(slot value))
CLIPS>
(deffacts initial
(fact (name Employee)
(field Name Role Department Company)
(value Bob Admin Security ABC))
(fact (name Employee)
(field Name Age)
(value Bob 38))
(fact2 (field Name)
(value Bob))
(fact2 (field Role)
(value Admin))
(fact2 (field Department)
(value Security))
(fact2 (field Company)
(value ABC)))
CLIPS>
(defrule reasoning
(fact (id ?id))
(forall
(fact (id ?id)
(field $?f ?field $?)
(value $?v&:(= (length$ ?f) (length$ ?v)) ?value $?))
(fact2 (field ?field)
(value ?value)))
=>
(assert (all field:values found)))
CLIPS> (reset)
CLIPS> (agenda)
0 reasoning: f-1,*
For a total of 1 activation.
CLIPS> (ppfact 1)
(fact
(name Employee)
(id gen1)
(field Name Role Department Company)
(value Bob Admin Security ABC))
CLIPS>

Related

Clips range find between 2 json inputs

I want to implemen a rule ;
Lets assume that ı have one input.json it consist a name value pair.Example;
{
"quality":"300"
}
I have another constant json ,Example
[{
"up":"100",
"down":"0",
"data":"xx"
},
{
"up":"200",
"down":"100",
"data":"yy"
},
,
{
"up":"300",
"down":"200",
"data":"zz"
}
]
I am trying to find propert value for data for which up and down range.
for this one ı, have to get zz because "quality":"300" is between 200-300.
how can ı success this one in clips rule.
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate pair
(slot name)
(slot value))
CLIPS>
(deffacts pairs
(pair (name "quality") (value 300)))
CLIPS>
(deftemplate data
(slot name)
(slot up)
(slot down))
CLIPS>
(deffacts data-values
(data (name "xx") (up 100) (down 0))
(data (name "yy") (up 200) (down 100))
(data (name "zz") (up 300) (down 200)))
CLIPS>
(defrule in-range
(pair (name "quality")
(value ?value))
(data (name ?name)
(up ?up)
(down ?down))
(test (and (> ?value ?down) (<= ?value ?up)))
=>
(printout t ?name " is in range." crlf))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (pair (name "quality") (value 300))
f-2 (data (name "xx") (up 100) (down 0))
f-3 (data (name "yy") (up 200) (down 100))
f-4 (data (name "zz") (up 300) (down 200))
For a total of 5 facts.
CLIPS> (agenda)
0 in-range: f-1,f-4
For a total of 1 activation.
CLIPS> (run)
zz is in range.
CLIPS>

How to retrieve a handle to facts inside a deffacts construct?

As described by the question, I would somehow try to get an handle to a fact within a deffacts construct. The problem arises because I don't want to redefine the same thing several times in WM (since set-fact-duplication is true) and because I use a structured deftemplate in which a field is a FACT_ADDRESS.
You can't bind a fact address within a deffacts construct. What I would suggest instead is to use a symbolic link between the facts. In your case, if the name of the tourism-type, tourism-resort, and hotel facts is unique among facts of each type, you could use that slot as the symbolic link:
(deftemplate tourism-type
(slot name)
(slot score))
(deftemplate hotel
(slot name)
(slot tr)
(slot stars)
(slot price-per-night))
(deftemplate tourism-resort
(slot name)
(slot region)
(multislot type))
(deffacts the-tourism-type-list
(tourism-type (name culturale) (score 3))
(tourism-type (name enogastronomico) (score 4)))
(deffacts the-tourism-resort-list
(tourism-resort
(name Venezia)
(region Veneto)
(type culturale enogastronomico)))
(deffacts the-hotels-list
(hotel
(name hotel1)
(tr Venezia)
(stars 3)
(price-per-night 100)))
In your rules, you can then use the symbolic link to retrieve the linked fact:
(defrule food-and-wine-hotels
(hotel (name ?hotel)
(tr ?tr-name))
(tourism-resort
(name ?tr-name)
(type $? enogastronomico $?))
=>
(printout t ?hotel crlf))

How to retrive facts asserted by a rule having particular name?

I have certain template defined as follows:
(deftemplate action
(slot name)
(slot field)
(slot value))
I have other rules which will use other facts to assert the action fact.
Now I want to retrieve only the fact with template action.
For now, I am using find-fact to retrieve, but here I have to use query which I do not want to provide.
(find-fact ((?fact action)) (= (str-compare ?fact:name 'Action1') 0))
I want all facts with template action and do not want to write a loop over all names with Action1, Action2 etc..
Thank you in advance.
CLIPS (6.31 4/1/19)
CLIPS>
(deftemplate action
(slot name)
(slot field)
(slot value))
CLIPS>
(deffacts actions
(action (name Action1) (field x) (value 3))
(action (name Action2) (field y) (value 4))
(action (name Action3) (field z) (value 5)))
CLIPS>
(defrule find-Action1
(action (name Action1))
=>)
CLIPS> (reset)
CLIPS> (agenda)
0 find-Action1: f-1
For a total of 1 activation.
CLIPS> (facts)
f-0 (initial-fact)
f-1 (action (name Action1) (field x) (value 3))
f-2 (action (name Action2) (field y) (value 4))
f-3 (action (name Action3) (field z) (value 5))
For a total of 4 facts.
CLIPS>

Suggestions for grammar for auditing system

I was wondering if you can point me in the right direction. I would like to write a rules-based system to audit a set of machines -- for example
Check configuration parameters to see if they were set correctly
Check state of machines to see if they are at acceptable ranges
The thing that I am struggling with is coming up with a grammar for the auditing rules so that I don't have to code defrules. I created some simple auditing rules but I am struggling with coming up with a generic form.
Here is my current attempt.
First, I am currently representing configuration parameters and running state with a single template (for a lack of imagination, I called it audit-fact) so that my auditing language can work for both configuration and state. I use the domain slot to specify configuration vs. state.
(deftemplate audit-fact
(slot domain (allowed-values config state)) ;; config vs. state
(slot machine) ;; machine to audit
(slot name) ;; parameter or state to check/audit
(slot value) ;; parameter value
(slot already-checked (default FALSE))
)
Next, I wrote a program to read in and assert the machines configuration and state as audit-facts. The following are sample configuration and state audit-facts
(assert (audit-fact
(domain config)
(machine drl-a-15_0)
(name os-version) (value 5.5))
)
(assert (audit-fact
(domain state)
(machine drl-a-15_0)
(name rpm) (value 5023))
)
Here is my current attempt at an auditing grammar/language ---
(deftemplate rule
(slot domain)
(slot name)
(slot constraint (default ?NONE) (allowed-values should-remain-at-default should-equal should-be-between ignore-if-ends-with))
(multislot values)
(multislot reasons)
(multislot references)
(slot criticality (allowed-values critical info suggestion warning))
(slot already-checked (default FALSE))
)
The following rule is used to check if a state or parameter is within a certain range
(assert (rule
(domain state)
(name rpm)
(constraint should-be-between)
(values 5000 5500)
(criticality critical)
(reasons "Low values could cause engine to stall. Prolonged high value could cause engine to heat up") )
)
Next, I used the following rule to check that a state or parameter is equal to a specific value
(assert (rule
(domain config)
(name os-version)
(constraint should-equal)
(values 5.5)
(criticality critical)
(reasons "OS version must be at 5.5 – no other levels are currently certified.") )
)
The following rules implements the equal and between checks
(defrule rule-should-eq
(rule (domain ?d) (name ?n) (constraint should-equal) (values ?cv) (reasons ?r))
?p <- (audit-fact (domain ?d) (name ?n) (value ?v) (already-checked FALSE))
=>
(if (eq ?v ?cv)
then
(printout t "checking " ?d ": " ?n ". Passed " crlf)
else
(printout t "checking " ?d “: “ ?n " should be set to " ?cv ". " ?r ". Warning" crlf)
)
(modify ?p (already-checked TRUE))
)
(defrule rule-should-be-between
(rule (domain ?d) (name ?n) (constraint should-be-between) (values ?cv-low ?cv-high) (reasons ?r))
?p <- (audit-fact (domain ?d) (name ?n) (value ?v) (already-checked FALSE))
=>
(if (and (>= ?v ?cv-low) (<= ?v ?cv-high))
then
(printout t "checking " ?n ". Passed " crlf)
else
(printout t "checking " ?n " should be between " ?cv-low " and " ?cv-high ". " ?r ". Warning" crlf)
)
(modify ?p (already-checked TRUE))
)
Using the above, I can implement simple checks -- e.g., must-be-set-to, must-be-between, must-not-equal, must-remain-at-default-value, must-be-higher-than, must-be-lower-than, etc.
But I can't think of an easy way to make the grammar handle multiple conditions with ands or ors, etc.
Question ---
is there a paper that describes a design pattern for auditing rules like above. I am currently studying the wine.clp in examples (CLIPS).
should I give up and just use the simple grammar for simple auditing rules and just use defrules for anything complicated -- e.g., if config-A is set to X, then check five other configs to make sure they are also set correctly, and then assert a check of the state. Once asserted, then check the state to be within a certain range.
Thanks in advance.
Bernie
Here's a method to evaluate arbitrary expressions from a generic rule:
CLIPS>
(deffunction str-replace (?str ?rpl ?fnd)
(if (eq ?fnd "") then (return ?str))
(bind ?rv "")
(bind ?i (str-index ?fnd ?str))
(while ?i
(bind ?rv (str-cat ?rv (sub-string 1 (- ?i 1) ?str) ?rpl))
(bind ?str (sub-string (+ ?i (str-length ?fnd)) (str-length ?str) ?str))
(bind ?i (str-index ?fnd ?str)))
(bind ?rv (str-cat ?rv ?str)))
CLIPS>
(deftemplate audit-fact
(slot domain)
(slot machine)
(slot name)
(slot value))
CLIPS>
(deftemplate rule
(slot domain)
(slot name)
(slot constraint)
(multislot reasons)
(multislot references)
(slot criticality))
CLIPS>
(deffacts initial
(audit-fact
(domain config)
(machine drl-a-15_0)
(name os-version)
(value 5.4))
(audit-fact
(domain state)
(machine drl-a-15_0)
(name rpm)
(value 5023))
(rule
(domain state)
(name rpm)
(constraint "(and (>= ?v 5000) (<= ?v 5500))")
(criticality critical)
(reasons "Low values could cause engine to stall. Prolonged high value could cause engine to heat up."))
(rule
(domain config)
(name os-version)
(constraint "(eq ?v 5.5)")
(criticality critical)
(reasons "OS version must be at 5.5 – no other levels are currently certified.")))
CLIPS>
(defrule rule-check
(rule (domain ?d) (name ?n) (constraint ?constraint) (reasons ?r))
?p <- (audit-fact (domain ?d) (name ?n) (value ?v))
=>
(bind ?constraint (str-replace ?constraint ?v "?v"))
(if (eval ?constraint)
then
(printout t "checking " ?d ": " ?n " Passed" crlf)
else
(printout t "checking " ?d ": " ?n " Warning : " ?r crlf)))
CLIPS> (reset)
CLIPS> (run)
checking config: os-version Warning : OS version must be at 5.5 – no other levels are currently certified.
checking state: rpm Passed
CLIPS>
1) If statements on the RHS are a code smell: don't!
2) You don't need the already-checked flag since you aren't modifying these facts.
Rewriting the check against one machine attribute:
?p <- (Configuration (machine ?m)(param ?p)(value ?v))
(ConfigCheckRange (param ?p){?v < loBound || $v > hiBound})
(You may need another variants, e.g., ConfigCheckEnum is permitted values aren't in one interval.)
Checking parameter value combinations is more difficult since you have chosen to represent every parameter value by a separate fact. You'll have to
?p1 <- (Configuration (machine ?m)(param ?p1)(value ?v1))
?p2 <- (Configuration (machine ?m)(param ?p2)(value ?v2)
{?p2 != ?p1))
(ConfigCheckCombi (param1 ?p1)(param2 ?p2)
{lowBound1 <= ?v1 && ?v2 <= lowBound2}
{?v2 < loBound2 || $v2 > hiBound2})
The right hand sides should merely register a violation in a (new) fact, let's call it Findings. One Findings contains the id to the machine and a list of (violated) ConfigCheck facts.
At the end (with a low-salience rule) you locate Findings facts and call the report method on it. (It may be more convenient to use some proper Java beans as facts.)

how to get the objects in clips in order on LHS side based on a particular slot in class

Is there any way to get the objects in clips in order on LHS side based on a particular slot in class?
(defclass SAMPLE
"all the information about students"
(is-a BASE_SAMPLE) (role concrete) (pattern-match reactive)
(slot ID (create-accessor read-write) (access initialize-only) (propagation inherit) (visibility public) (type INTEGER))
(slot NAME (create-accessor read-write) (access initialize-only) (propagation inherit) (visibility public) (type STRING))
)
if I have 100 SAMPLE objects, and I want all of them to come in ascending order based on the slot ID on the LHS of a rule, is this poosilbe in clips?
There's two ways you can sort the objects. You can do it on the LHS by adding some additional information to either the objects or a separate fact/instance to retain information on which objects have been processed:
CLIPS> (clear)
CLIPS>
(defclass STUDENT
(is-a USER)
(slot id)
(slot full-name)
(slot processed (default no)))
CLIPS>
(definstances people
(of STUDENT (id 102) (full-name "Fred Jones"))
(of STUDENT (id 438) (full-name "Sally Smith"))
(of STUDENT (id 391) (full-name "John Farmer")))
CLIPS>
(defrule list
?i <- (object (is-a STUDENT)
(id ?id1)
(processed no))
(not (object (is-a STUDENT)
(id ?id2&:(> ?id1 ?id2))
(processed no)))
=>
(modify-instance ?i (processed yes))
(printout t ?id1 " " (send ?i get-full-name) crlf))
CLIPS> (reset)
CLIPS> (run)
102 Fred Jones
391 John Farmer
438 Sally Smith
CLIPS>
Or you can sort the values on the RHS:
CLIPS> (clear)
CLIPS>
(defclass STUDENT
(is-a USER)
(slot id)
(slot full-name))
CLIPS>
(definstances students
(of STUDENT (id 102) (full-name "Fred Jones"))
(of STUDENT (id 438) (full-name "Sally Smith"))
(of STUDENT (id 391) (full-name "John Farmer")))
CLIPS>
(deffunction id-sort (?i1 ?i2)
(> (send ?i1 get-id) (send ?i2 get-id)))
CLIPS>
(defrule list
=>
(bind ?instances (find-all-instances ((?i STUDENT)) TRUE))
(bind ?instances (sort id-sort ?instances))
(progn$ (?i ?instances)
(printout t (send ?i get-id) " " (send ?i get-full-name) crlf)))
CLIPS> (reset)
CLIPS> (run)
102 Fred Jones
391 John Farmer
438 Sally Smith
CLIPS>

Resources