how to combine these two rules with CLIPS? - expert-system

I have two rules in CLIPS which I want to combine if they're both true...not sure how to do it though. I have an attribute which is called grant-eligible....I was thinking if I set it to TRUE then I could read the next rule, and then set the 'grant-eligible' to FALSE....but it seems that my code is getting into an infinite loop when I do this...
So here are my rules:
(defrule complete "rule for app completeness"
?f <- (application (transcript-received Yes) (app-complete FALSE)
(gpa
?v_gpa&:(
> ?v_gpa 0)))
=>
(modify ?f (app-complete TRUE)))
(defrule denied "rule for admission - DENIED"
?f <- (application (app-complete TRUE) (app-decision FALSE)
(gpa
?v_gpa&:(
< ?v_gpa 3.0))
(ssat
?v_ssat&:(
>= ?v_ssat 0.0))
)
=>
(modify ?f (app-decision DENIED))
)
(defrule accepted "rule for admission - ACCEPTED"
?f <- (application (app-complete TRUE) (app-decision FALSE)
(gpa
?v_gpa&:(
>= ?v_gpa 3.5))
(ssat
?v_ssat&:(
>= ?v_ssat 1500))
)
=>
(modify ?f (app-decision ACCEPTED))
)
This is the ones I am trying to implement now
(defrule female-finaid "rule for finaid applications for female students"
?f <- (application (app-decision ACCEPTED)
(gender F) (grade-entry Freshman) (country USA)
(grant-eligible TRUE)
(grant ?v_grant)
)
=>
(modify ?f
(grant (+ ?v_grant 5000))
(grant-eligible TRUE)
)
)
(defrule great-students-finaid "rule for finaid applications for female students"
?f <- (application (app-decision ACCEPTED)
(country USA)
(grant-eligible TRUE)
(grant ?v_grant)
(gpa
?v_gpa&:(
>= ?v_gpa 4.0))
)
=>
(modify ?f
(grant (+ ?v_grant 4500))
(grant-eligible FALSE)
)
)
If both of these rules are true, the grant awarded should be 9500, or it could be 5000 or it could be 4500...Any ideas?
The solution: (where ff-grant-eligible and es-grant-eligible are my control facts...they stand for ff=female finaid, and es=excellent student)
(defrule female-finaid "rule for finaid applications for female students"
?f <- (application (app-decision ACCEPTED) (ff-grant-eligible TRUE)
(gender F) (grade-entry Freshman) (country USA)
(grant ?v_grant)
)
=>
(modify ?f
(grant (+ ?v_grant 5000))
(ff-grant-eligible FALSE)
)
)
(defrule great-students-finaid "rule for finaid applications for female students"
?f <- (application (app-decision ACCEPTED) (es-grant-eligible TRUE)
(country USA)
(grant ?v_grant)
(gpa
?v_gpa&:(
>= ?v_gpa 4.0))
)
=>
(modify ?f
(grant (+ ?v_grant 4500))
(es-grant-eligible FALSE)
)
)

There are numerous ways you can control execution of your program (e.g., control facts, salience, modules). This answer will use control facts (with salience) for the stages of application processing. I am also going to assume that you have a unique id slot associated with each application.
Consider the following fact and rule:
(deffacts application-stages "ordered sequence of stages for an application"
(stages app-received app-accept-reject app-evaluate-grants
app-apply-grants app-complete))
(defrule go-to-next-stage "Advances to the next application stage"
?stage <- (app-stage ?id ?current-stage)
(stages $? ?current-stage ?next-stage $?)
=>
(retract ?stage)
(assert (app-stage ?id ?next-stage))
(printout t "Application " ?id " moved from stage " ?current-stage
" to " ?next-stage "." crlf))
The application-stages deffact defines the sequence of stages for an application and the go-to-next-stage rule advances the application stage. Since the rule has salience that is lower than the default (0), it will only be executed if there are no other rules corresponding to the current stage. So with no other rules in your program, you would get the following:
CLIPS> (reset)
CLIPS> (assert (app-stage app-001 app-received))
<Fact-2>
CLIPS> (run)
Application app-001 moved from stage app-received to app-accept-reject.
Application app-001 moved from stage app-accept-reject to app-evaluate-grants.
Application app-001 moved from stage app-evaluate-grants to app-apply-grants.
Application app-001 moved from stage app-apply-grants to app-complete.
CLIPS>
But if you have any rules associated with a particular stage, they will be executed first. So you can add rules to the app-evaluate-grants stage like this:
(defrule female-finaid "rule for finaid applications for female students"
(app-stage ?id app-evaluate-grants)
(application (app-decision ACCEPTED) (id ?id)
(gender F) (grade-entry Freshman) (country USA)
=>
(assert (grant ?id female 5000)))
And you would similarly add a great-student-finaid rule. Then, there is a single rule for the app-apply-grants stage:
(defrule apply-grant "Adds the amount of a grant to an application"
(app-stage ?id app-apply-grants)
?grant <- (grant ?id ? ?amount)
?app <- (application (id ?id) (grant ?v_grant))
=>
(retract ?grant)
(modify (?app (grant (+ ?v_grant ?amount))))
One of the benefits of modeling it this way is that you don't have to include control facts (e.g., grant-eligible) in the data for an application. That is, your control logic is separate from the data model. Note that you can achieve the same effect as what I've done here by using CLIPS modules (via defmodule) and that is often preferable but it would require a lengthier answer (and this one is already fairly long).

Related

Finding path between 2 vertices which are not directly connected

I have a connected graph Like this
user1|A,C,B
user2|A,E,B,A
user3|C,B,A,B,E
user4|A,C,B,E,B
where user are the property name and the path for that particular user is followed. For example for
user1 the path is A->C->B
user2: A->E->B->A
user3: C->B->A->B->E
user4: A->C->B->E->B
Now, I want to find all users who have reached from A to E. Output should be
user2, user3, user4(since all these users finally reached E from A, no matter how many hops they took). How can I write the motif for this.
This is what I tried.
val vertices=spark.createDataFrame(List(("A","Billing"),("B","Devices"),("C","Payment"),("D","Data"),("E","Help"))).toDF("id","desc")
val edges = spark.createDataFrame(List(("A","C","user1"),
("C","B","user1"),
("A","E","user2"),
("E","B","user2"),
("B","A","user2"),
("C","B","user3"),
("B","A","user3"),
("A","B","user3"),
("B","E","user3"),
("A","C","user4"),
("C","B","user4"),
("B","E","user4"),
("E","B","user4"))).toDF("src","dst","user")
val pathAnalysis=GraphFrame(vertices,edges)
pathAnalysis.find("(a)-[]->();()-[]->();()-[]->(d)").filter("a.id='A'").filter("d.id='E'").distinct().show()
But I am getting an exception like this
org.apache.spark.sql.AnalysisException: Detected implicit cartesian product for INNER join between logical plans
Join Inner
:- Project [a#355]
: +- Join Inner, (__tmp-4363599943432734077#353.src = a#355.id)
: :- LocalRelation [__tmp-4363599943432734077#353]
: +- Project [named_struct(id, _1#0, desc, _2#1) AS a#355]
: +- Filter (named_struct(id, _1#0, desc, _2#1).id = A)
: +- LocalRelation [_1#0, _2#1]
+- LocalRelation
and
LocalRelation [__tmp-1043886091038848698#371]
Join condition is missing or trivial.
Either: use the CROSS JOIN syntax to allow cartesian products between these
relations, or: enable implicit cartesian products by setting the configuration
variable spark.sql.crossJoin.enabled=true;
I am not sure if my condition is correct or how to set this property
spark.sql.crossJoin.enabled=true on a spark-shell
I invoked my spark-shell as follows
spark-shell --packages graphframes:graphframes:0.3.0-spark2.0-s_2.11
my suggested solution is kinda trivial, but it will work fine if the paths are relatively short, and the number of user (i.e., number of rows in the data set) is big. If this is not the case, please let me know, other implementations are possible.
case class UserPath(
userId: String,
path: List[String])
val dsUsers = Seq(
UserPath("user1", List("A", "B", "C")),
UserPath("user2", List("A", "E", "B", "A")))
.doDF.as[UserPath]
def pathExists(up: UserPath): Option[String] = {
val prefix = up.path.takeWhile(s => s != "A")
val len = up.path.length
if (up.path.takeRight(len - prefix.length).contains("E"))
Some(up.userId)
else
None
}
// Users with path from A -> E.
dsUsers.map(pathExists).filter(opt => !opt.isEmpty)
You can also use BFS algorithm for it: http://graphframes.github.io/graphframes/docs/_site/api/scala/index.html#org.graphframes.lib.BFS
With your data model you will have to iterate over users and run the BFS for each of them like this:
scala> pathAnalysis.bfs.fromExpr($"id" === "A").toExpr($"id" === "E").edgeFilter($"user" === "user3").run().show
+------------+-------------+------------+-------------+---------+
| from| e0| v1| e1| to|
+------------+-------------+------------+-------------+---------+
|[A, Billing]|[A, B, user3]|[B, Devices]|[B, E, user3]|[E, Help]|
+------------+-------------+------------+-------------+---------+

unable to get results while doing a DataFrame look in a Clojure Flambo api calls

I read a parquet file and and get data as RDD using Flambo api. we apply zipmap of column names and create a hash map/ Clojure map
let say my map has following values
[{:a 1 :b2}
{:a 2 :b 2}]
(:require [flambo.api :as f])
core.clj
I am using
(f/map rdd-records (f/fn[each-rdd]
(perform-calcs each-red)))
in perform-calcs function based on the input from map we do additional calculations, something like
cals.clj
(defn perform-calcs
[r]
(merge r {:c (+ (:a r) (:b r))}))
we had a new requirement is to perform another calculation based on another DataFrame from another file. we don't want to load the file for each record so kept the code to load DataFrame out side calc and defined in commons file. this DataFrame gets loaded as part of application and can be accessible across application.
commons.clj
(def another-csv-df
(load-file->df "file-name"))
calcs.clj
(defn df-lookup
[r df]
{:d (->
df (.filter (format "a = %d and b = %d" (:a r) (:b r) )
(.select (into [] (map #(Column. %) ["d"] )))
(first)
(.getString(0))})
by including this in perform-calcs function will change as follows.
(defn perform-calcs
[r]
(-> r
(merge {:c (+ (:a r) (:b r))})
(df-lookup commons/another-csv-df))
in real I see the values on the data frame... code is working as expected with out this external call of DF with this DF look up Code it was running for long... and never complete the process
Nested transformations like this one, are not allowed in Spark at all. You will have to rethink your approach, likely by converting RDD to Dataset and performing join between both.

How to write the below function in cognos FM(filter)

EXISTS (SELECT 1 FROM Common.Com_macss_Account a WHERE COM_MACSS_PREMISE.prem_nb = a.prem_nb AND a.ACCT_TYPE_CD IN ('E', 'EO') )
The above expression is in BO(Universe). Need to write it in cognos FM (Filetr). Tried CASE statement but didn't help.

Spotfire - advanced row level security

I'm working on row level security in Spotfire (6.5) report.
It should be implemented on 3 levels, lets call it L1, L2 and L3. There is additional mapping table that contains Userlogins and specified values on all levels where user has access. Additionaly if user is not in mapping table he is some kind of Root user so he has access to everything.
On DB side it looks like that:
CREATE TABLE SECURITY
(
USER_ID VARCHAR2(100 BYTE)
, L1 VARCHAR2(100 BYTE)
, L2 VARCHAR2(100 BYTE)
, L3 VARCHAR2(100 BYTE)
--, L1L2L3 VARCHAR2(100 BYTE) -- option there could be one column that contains lowest possible level
);
INSERT INTO SECURITY (USER_ID, L1) VALUES ('UNAME1','A');
INSERT INTO SECURITY (USER_ID, L2) VALUES ('UNAME2','BB');
INSERT INTO SECURITY (USER_ID, L3) VALUES ('UNAME3','CCC');
CREATE TABLE SECURED_DATA
(
L1 VARCHAR2(100 BYTE)
, L2 VARCHAR2(100 BYTE)
, L3 VARCHAR2(100 BYTE)
, V1 NUMBER
);
INSERT INTO SECURED_DATA (L1, V1) VALUES ('A',1);
INSERT INTO SECURED_DATA (L1, L2, V1) VALUES ('B','BB',2);
INSERT INTO SECURED_DATA (L1, L2, L3, V1) VALUES ('C','CC','CCC',3);
Finally I've made Information Link and then I've changed its' sql code to something like that:
SELECT
M.*
FROM
SECURITY S
INNER JOIN SECURED_DATA M
ON
(
M.L1 = S.L1
AND S.USER_ID = (%CURRENT_USER%)
)
UNION ALL
SELECT
M.*
FROM
SECURITY S
INNER JOIN SECURED_DATA M
ON
(
M.L2 = S.L2
AND S.USER_ID = (%CURRENT_USER%)
)
UNION ALL
SELECT
M.*
FROM
SECURITY S
INNER JOIN SECURED_DATA M
ON
(
M.L3 = S.L3
AND S.USER_ID = (%CURRENT_USER%)
)
UNION ALL
SELECT
M.*
FROM
SECURED_DATA M
WHERE
(
SELECT
COUNT(1)
FROM
SECURITY S
WHERE S.USER_ID = (%CURRENT_USER%)
)
=0
It works fine, but I'm worndering if there is more smart and more Spotfire way to get it?
Many thanks and regards,
Maciej
My guess on "more smart and more Spotfire way" is that you want to be able to cache a single data set and use it for multiple users, limiting it in the analytic rather than in the data pull. There is some danger to this, if we're doing it for security's sake, because the data will technically be in the analytic, and if they have permission to edit and add visualizations, you no longer control what they can and cannot see. If there's any authoring allowed in Web Player for the specific analytic, I recommend all securities be done DataBase-side.
If you want to do it in Spotfire anyways, here is my recommendation:
Have an Information Link (for example case, named IL_SecurityCheck) which is Select * from SECURITY WHERE S.USER_ID = (%CURRENT_USER%).
If they move from a cover page to the page with the data in it, you can put the code in the script to change pages; if not, you can use a method I explained here: Spotfire Current Date in input field with calendar popup to fire off a script on open.
Button Script required:
from Spotfire.Dxp.Data import *
crossSource = Document.Data.Tables["IL_SecurityCheck"]
rowCount = crossSource.RowCount
rowIndexSet = IndexSet(rowCount, True)
print rowCount
#rowCount = Document.Data.Tables["Managed Care UpDownStream"].RowCount
colCurs = DataValueCursor.CreateFormatted(crossSource.Columns["L1"])
colCurs2 = DataValueCursor.CreateFormatted(crossSource.Columns["L2"])
colCurs3 = DataValueCursor.CreateFormatted(crossSource.Columns["L3"])
x = ""
if rowIndexSet.IsEmpty != True:
for row in crossSource.GetRows(rowIndexSet, colCurs):
if colCurs.CurrentValue is not None:
x += "[L1] = '" + colCurs.CurrentValue + "' and "
for row in crossSource.GetRows(rowIndexSet, colCurs2):
if colCurs2.CurrentValue is not None:
x += "[L2] = '" + colCurs2.CurrentValue + "' and "
for row in crossSource.GetRows(rowIndexSet, colCurs3):
if colCurs3.CurrentValue is not None:
x += "[L3] = '" + colCurs3.CurrentValue + "' and "
x = x[:len(x) - 4]
else:
x = "1=1"
Document.Properties["SecurityLimits"] = x
Visualization Data Limited by Expression: ${SecurityLimits}

Lambda Expression: How to select from two non-related tables with one query

Would you help me to re-write this query syntax with lambda expressions?
(From Entity.Apple a in db.Context.Apples
From Entity.Bikini b in db.Context.Bikinis
Where a.Id== 10
Where b.Id== 15
Select new {NaturalColor: a.Color, FavoriteColor: b.Color }).FirstOrDefault();
I try to find specific records of two non-related tables, with one connection. In my example, next step may be this:
FindColorDiffrent(Naturalcolor, FavoriteColor){}
db.Context.Apples
.SelectMany(a => db.Context.Bikinis, (a, b) => new {a, b})
.Where(x => x.a.Id == 10)
.Where(x => x.b.Id == 15)
.Select(x => new {NaturalColor: x.a.Color, FavoriteColor: x.b.Color })
.FirstOrDefault();

Resources