ruleml2015 : hybrid relational and graph reasoning
TRANSCRIPT
Building A Hybrid Reactive Rule Engine
For Relational And Graph
ReasoningMark Proctor, Mario Fusco, István Ráth, Davide Sottara
The Problem
Student PlansString namePlan plan
Student
String codeStudent ownerList<Exam> exams
Plan
Plan planString courseString codeList<Grade> exams
ExamExam examint attemptString value
Grade
1
1
1
0..11 0..1
Student Planspublic class Student { private String name; private Plan plan; }
public class Plan { private String code;
private Student owner; private List<Exam> exams;
}
public class Exam { private String code;
private String course; private Plan plan; private List<Grade> grades; }
public class Grade { private String value; private int attempt;
private Exam exam; }
Student Planspublic static Student student(String studentName, String planCode, Exam... exams) { Student student = new Student(studentName); Plan plan = new Plan(student, planCode); student.setPlan( plan ); plan.setOwner( student ); for ( Exam exam : exams ) { exam.setPlan( plan ); plan.getExams().add ( exam ); } return student;} public static Exam exam( String course, String... grades) { Exam exam = new Exam( course ); int attempt = 1; for ( String letter : grades) { exam.getGrades().add(new Grade(exam, letter, attempt)); attempt++; } return exam;}
Courses •Communication •Creed - The Jedi Code•Force •Jedi Studies •Meditation •Personal •Situational Awareness•Spirituality - Jedi Mythology•Warrior
Student Plansprivate static final String A = "A"; private static final String B = "B"; private static final String C = "C"; private static final String D = "D"; private static final String E = "E"; private static final String F = "F";
Student Plansprivate static final String A = "A"; private static final String B = "B"; private static final String C = "C"; private static final String D = "D"; private static final String E = "E"; private static final String F = "F";
public void create(KieSession ksession) { Student darth = student( "Darth", "dp2015", exam( "dpe01", "Jedi Studies", A), exam( "dpe02", "Force", B ), exam( "dpe03", "Meditation", D, C) ); Student yoda = student( "Yoda", "yp2015", exam( "ype01", "Jedi Studies", A), exam( "ype02", "Force", A ), exam( "ype03", "Meditation", A) ); Student luke = student( "Luke", "lp2015", exam( "lpe01", "Jedi Studies", C, B), exam( "lpe02", "Force", B ), exam( "lpe03", "Meditation", F, C) );}
Student Plansprivate static final String A = "A"; private static final String B = "B"; private static final String C = "C"; private static final String D = "D"; private static final String E = "E"; private static final String F = "F";
public void create(KieSession ksession) { Student darth = student( "Darth", "dp2015", exam( "dpe01", "Jedi Studies", A), exam( "dpe02", "Force", B ), exam( "dpe03", "Meditation", D, C) ); Student yoda = student( "Yoda", "yp2015", exam( "ype01", "Jedi Studies", A), exam( "ype02", "Force", A ), exam( "ype03", "Meditation", A) ); Student luke = student( "Luke", "lp2015", exam( "lpe01", "Jedi Studies", C, B), exam( "lpe02", "Force", B ), exam( "lpe03", "Meditation", F, C) ); ksession.insert( darth ); ksession.insert( yoda ); ksession.insert( luke );}
Student Plans• Email all the “Big Data” people their grades.
The Problem• The information provided is Object Oriented which is a form
of graph and uses references.• Traditional Production Rule Systems are relational, they
cannot see or use references.• Users must then map their model to a relational one.
Student Plans
public class Student { private String name; private String plan;
}
public class Plan { private String code;
private String owner; }
Student Plans
public class Student { private String name; private String plan;
}
public class Plan { private String code;
private String owner; }
public class Student { private String name; private Plan plan; }
public class Plan { private String code;
private Student owner; private List<Exam> exams;
}
Student Plans
public class Grade { private String value; private int attempt;
private String exam; }
public class Exam { private String code;
private String course;
private String plan; }
Student Plans
public class Grade { private String value; private int attempt;
private String exam; }
public class Exam { private String code;
private String course;
private String plan; }
public class Exam { private String code;
private String course; private Plan plan; private List<Grade> grades; }
public class Grade { private String value; private int attempt;
private Exam exam; }
Student Plans• Email all the “Big Data” people their grades.
rule R1 when $student : Student () $plan : Plan ( owner == $student.name ) $exam : Exam( plan == $plan.code, course == ”Big Data” ) $grade : Grade( exam == $exam.code )
then
// RHS
end
Rete
AND
AND
OTN OTN OTN OTN
Root
GradeExamPlanStudent
ANDRTN
Retepublic abstract class Node { protected Node childNode; public abstract void propagateLeft(Tuple tuple); public abstract void propagateRight(Object object);}
Retepublic abstract class Node { protected Node childNode; public abstract void propagateLeft(Tuple tuple); public abstract void propagateRight(Object object);}
public class AndNode extends Node { private LeftMemory leftMemory; private RightMemory rightMemory; private CompiledExpression expr; public void propagateLeftInsert(Tuple tuple) { leftMemory.addTuple(tuple); for ( Object object : rightMemory.getObjects() ) { if ( expr.eval(tuple, object) ) { childNode.propagateLeft(new Tuple(tuple, object)); } } } public void propagateRightInsert(Object object) { rightMemory.addObject(object); for ( Tuple tuple : leftMemory.getTuples() ) { if ( expr.eval(tuple, object) ) { childNode.propagateLeft(new Tuple(tuple, object)); } } }}
Rete
AND
OTN
Root
Student [“dp2015”, “yp2015”, “lp2015”]
[“darth”, “yoda”, “luke”]
[“darth”, “yoda”, “luke”]
OTN
Plan
[“dp2015”, “yp2015”, “lp2015”]
AND
Exam
Rete
AND
OTN
Root
Student [“dp2015”, “yp2015”, “lp2015”]
[“darth”, “yoda”, “luke”]
[“darth”, “yoda”, “luke”]
OTN
Plan
[“dp2015”, “yp2015”, “lp2015”]
ANDPartial Match
[[“darth”, “dp2015”],[“yoda”, “yp2015”],
[“luke”, “yp2015”] ]
Exam
public class Tuple { private Tuple parent; private Object object; private List<Tuple> children; public Tuple(Tuple parent, Object object) { parent = parent; this.object = object; }}
Rete
AND
OTN
Root
Student [“dp2015”, “yp2015”, “lp2015”]
[“darth”, “yoda”, “luke”]
[“darth”, “yoda”, “luke”]
OTN
Plan
[“dp2015”, “yp2015”, “lp2015”]
ANDPartial Match
[[“darth”, “dp2015”],[“yoda”, “yp2015”],
[“luke”, “yp2015”] ]
OTN
Exam[“dpe01”, “dpe02”, “dpe03”, “ype01”, “ype02”, “ype03”,
“lpe01”, “lpe02”, “lpe03” ]
public class Tuple { private Tuple parent; private Object object; private List<Tuple> children; public Tuple(Tuple parent, Object object) { parent = parent; this.object = object; }}
Rete
AND
OTN
Root
Student [“dp2015”, “yp2015”, “lp2015”]
[“darth”, “yoda”, “luke”]
[“darth”, “yoda”, “luke”]
OTN
Plan
[“dp2015”, “yp2015”, “lp2015”]
AND
[[“darth”, “dp2015”, “dpe01”], [“darth”, “dp2015”,“dpe02”],[“darth”, “dp2015”,“dpe03”], [“yoda”, “yp2015”, “ype01”], [“yoda”, “yp2015”, “ype02”], [“yoda”, “yp2015”, “ype03”], [“luke”, “lp2015”, “ype01”], [“luke”, “lp2015”, “ype02”], [“luke”, “lp2015”, “ype03”]]
Partial Match
[[“darth”, “dp2015”],[“yoda”, “yp2015”],
[“luke”, “yp2015”] ]
OTN
Exam[“dpe01”, “dpe02”, “dpe03”, “ype01”, “ype02”, “ype03”,
“lpe01”, “lpe02”, “lpe03” ]
public class Tuple { private Tuple parent; private Object object; private List<Tuple> children; public Tuple(Tuple parent, Object object) { parent = parent; this.object = object; }}
Rete[[“darth”, “dp2015”, “dpe01”], [“darth”, “dp2015”,“dpe02”],[“darth”, “dp2015”,“dpe03”], [“yoda”, “yp2015”, “ype01”], [“yoda”, “yp2015”, “ype02”], [“yoda”, “yp2015”, “ype03”], [“luke”, “lp2015”, “ype01”], [“luke”, “lp2015”, “ype02”], [“luke”, “lp2015”, “ype03”]]
Darth
dp2015
dpe01 dpe02 dpe03
t1 = new Tuple( null, “Darth” )
t2 = new Tuple( t1, “dp205” )
t3 = new Tuple( t2, “dpe01” )
t4 = new Tuple( t2, “dpe02” )
t5 = new Tuple( t2, “dpe03” )
Exam
Plan
Student
[t1, t2, t3], [t1, t2, t4],[t1, t2, t5]
Requirements• Functionality
• Access child objects via references• React to child objects• List comprehension
• Iterate one to many relations• Support reactive and passive operations
• Implementation• Syntax Extensions• Rete Extensions• Object integration
From• Implementation
• Introduces one new keyword “from”.• Requires new Rete node.• Requires expression evaluation sub system.• Uses dot ‘.’ as reference accessor.
• with type safe javascript like syntax (MVEL).• Functionality
• Allows access to nested objects.• Dot ‘.’ accessor is access and return only.
• It does not provide list comprehension.• List comprehension is performed by the node on the return result.• Is passive only, no reactivity.
rule R2 when $student : Student ( $plan : plan ) $exam: Exam( course == ”Big Data” ) from $plan.exams $grade: Grade() from $exam.grades
then /∗ RHS ∗/ end
Fromrule R2 when $student : Student ( $plan : plan ) $exam: Exam( course == ”Big Data” ) from $plan.exams $grade: Grade() from $exam.grades
then /∗ RHS ∗/ end
From
From
OTN
Root
Student
RTN
$student : Student ( $plan : plan ) // “from” the Working Memory
$exam: Exam( course == ”Big Data” ) from $plan.exams
$grade: Grade() from $exam.grades
From public void propagateLeft(Tuple tuple) { leftMemory.addTuple(tuple); for ( Object object : rightMemory.getObjects() ) { if ( expr.eval(tuple, object) ) { childNode.propagateLeft(new Tuple(tuple, object)); } } }
From
public void propagateLeftInsert(Tuple tuple) { leftMemory.addTuple(tuple); Object result = expr.equals( tuple.get(index)); if ( result instanceof Collection) { for ( Object o : ((Collection)result)) { propagateLeftIfAllowed(tuple, o); } } else { propagateLeftIfAllowed(tuple, result); }} private void propagateLeftIfAllowed(Tuple tuple, Object o) { if ( isAllowed( tuple, o ) ) { childNode.propagateLeftInsert(new Tuple(tuple, o)); }}
public void propagateLeftInsert(Tuple tuple) { leftMemory.addTuple(tuple); for ( Object object : rightMemory.getObjects() ) { if ( expr.eval(tuple, object) ) { childNode.propagateLeft(new Tuple(tuple, object)); } } }
Frompublic class FromNode extends Node { private LeftMemory leftMemory; private CompiledExpression expr; private int index; public void propagateLeftInsert(Tuple tuple) { leftMemory.addTuple(tuple); Object result = expr.equals( tuple.get(index)); if ( result instanceof Collection) { for ( Object o : ((Collection)result)) { propagateLeftIfAllowed(tuple, o); } } else { propagateLeftIfAllowed(tuple, result); } } private void propagateLeftIfAllowed(Tuple tuple, Object o) { if ( isAllowed( tuple, o ) ) { childNode.propagateLeftInsert(new Tuple(tuple, o)); } } public void propagateRightInsert(Object object) { // From has no right propagation } }
Passive OOPath• Implementation
• Re-use ‘from’ node• Introduces no Rete changes.
• Requires expression evaluation sub system.• Uses forward slash ‘/’ as reference accessor.
• Introduce XPath inspired syntax• Syntax change must be added to Patterns
• Functionality• Allows access to nested objects.• forward slash ‘/’ accessor performs list comprehension
for each visited reference.• List comprehension is also performed by the node on the return result.• Is passive only, no reactivity.rule R3 when Student( $grade: /plan/exams{course == ”Big Data”}/grades )
then
/∗ RHS ∗/
end
R1, R2, R3rule R1 when $student : Student () $plan : Plan ( owner == $student.name ) $exam : Exam( plan == $plan.code, course == ”Big Data” ) $grade : Grade( exam == $exam.code )
then
// RHS
end
rule R2 when $student : Student ( $plan : plan ) $exam: Exam( course == ”Big Data” ) from $plan.exams $grade: Grade() from $exam.grades
then /∗ RHS ∗/ end
rule R3 when Student( $grade: /plan/exams{course == ”Big Data”}/grades )
then
/∗ RHS ∗/
end
OOPath Syntax• Access by index
• Inline cast for type safety
• Indexed back reference
• Variable back reference
• Back tracking
• Out of Pattern use
Student( $grade : /plan/exams[0]{ course == ”Big Data”}/grades )
Student( $grade : /plan/exams{ #PracticalExam, lab == ”hazard safe”, course == ”Material Explosions”}/grades )
A( $var: /b/c/d{ f1 == ../../f2}/e ) // the ../../ back references to the ‘b’ field access
A( $var: /$b : b/c/d{ f1 == $b.f2}/e ) // the $b is inline bound for later use
A( $var: /$b : b/c/d{ f1 == $b.f2}/$b/f2 ) // $var is bound to results of the f2 access
$student : Student() $grade : /$student/plan/exams{course == ”Big Data”}/grades;
Advanced OOPath Usage• Use existing Drools syntax and functionality
• Colon ‘:’ provides Pattern and field binding• Colon with equals ‘:=‘ provides unification• POSL-like support for position and slotted
• Arguments can be named or positional. • Positional arguments must come first and be delimited
with a semi colon ‘;’ at the end. • Positional arguments are always unified, compared to
named arguments which can be bound ’:’ or unified ’:=’.
Advanced OOPath Usage• Transitive closurequery isContainedIn ( Thing $x , Thing $y ) /$y/$x := children ; or /$y/$z := children; and isContainedIn($x, $z;) ) end
Advanced OOPath Usage• Transitive closure
• Negation over transitive closure
query isContainedIn ( Thing $x , Thing $y ) /$y/$x := children ; or /$y/$z := children; and isContainedIn($x, $z;) ) end
query isNotContainedIn ( Thing $x , Thing $y ) not( isContainedIn( $x, $y; ) ) end
Advanced OOPath Usage• Transitive closure
• Negation over transitive closure
• Accumulation
query isContainedIn ( Thing $x , Thing $y ) /$y/$x := children ; or /$y/$z := children; and isContainedIn($x, $z;) ) end
query isNotContainedIn ( Thing $x , Thing $y ) not( isContainedIn( $x, $y; ) ) end
query countItems ( Thing $y) acc( isContainedIn( $x, $y; ); count( $x ); ) end
Advanced OOPath Usage• Transitive closure
• Negation over transitive closure
• Accumulation
• Structural Control
query isContainedIn ( Thing $x , Thing $y ) /$y/$x := children ; or /$y/$z := children; and isContainedIn($x, $z;) ) end
query isNotContainedIn ( Thing $x , Thing $y ) not( isContainedIn( $x, $y; ) ) end
query countItems ( Thing $y) acc( isContainedIn( $x, $y; ); count( $x ); ) end
query childrenOrderedByEdgeCount( Parent $x, Child $c0, int index ) /$x/$c1 : children[index]{children.size <= $c0.children. size }; childrenOrderedByEdgeCount ( $x , $c1 , index + 1; ) end
Advanced OOPath Usage• Transitive closure
• Negation over transitive closure
• Accumulation
• Structural Control
• Combined Graph and Relational
query isContainedIn ( Thing $x , Thing $y ) /$y/$x := children ; or /$y/$z := children; and isContainedIn($x, $z;) ) end
query isNotContainedIn ( Thing $x , Thing $y ) not( isContainedIn( $x, $y; ) ) end
query countItems ( Thing $y) acc( isContainedIn( $x, $y; ); count( $x ); ) end
query childrenOrderedByEdgeCount( Parent $x, Child $c0, int index ) /$x/$c1 : children[index]{children.size <= $c0.children. size }; childrenOrderedByEdgeCount ( $x , $c1 , index + 1; ) end
query findChildrenWithMatchingEdgeCounts( Parent $x, Child $c0, int index ) /$x/$c := children[index]; // relational search exists( Thing( children . size == $c. children . size ) ) findChildrenWithMatchingEdgeCounts( $x , $c1 , index + 1;) end
Reactive OOPath• Implementation
• New reactive ‘from’ node.• ReactiveObject Object integration.• List integration.
• Functionality• Uses the OOPath syntax, but now all ‘/‘ are reactive.
Reactive OOPathpublic class ReactiveFromNode extends Node { private LeftMemory leftMemory; private CompiledExpression expr; private int index; public void propagateLeftInsert(Tuple tuple) { leftMemory.addTuple(tuple); Object result = expr.equals( tuple.get(index) ); if ( result instanceof ObservableList) { ObservableList list = (ObservableList) result; ListObserver observer = new ListObserver(tuple); tuple.setObserver(observer); list.addObserver( observer ); for ( Object o : list ) { propagateLeftIfAllowed(tuple, o); } } else { propagateLeftIfAllowed(tuple, result); } } public void propagateLeftIfAllowed(Tuple tuple, Object o) { ReactiveObject r = getReactiveObject(o); r.addTuple(tuple); if ( isAllowed( tuple, o ) ) { childNode.propagateLeftInsert(new Tuple(tuple, o)); } } public void propagateChildLeftTupleDelete(Tuple tuple) { tuple.unlink(); childNode.propagateLeftDelete(tuple); }
ReactiveObjectpublic class ReactiveObject { private List<Tuple> tuples = new ArrayList<Tuple>(); private Object object; public ReactiveObject(Object object) { this.object = object; } public void addTuple(Tuple tuple) { tuples.add(tuple); } public void removeTuple(Tuple tuple) { tuples.remove(tuple); } protected void notifyUpdate() { for ( Tuple tuple : tuples ) { ReactiveFromNode node = (ReactiveFromNode) tuple.getNode(); for ( Tuple childTuple : tuple.getChildren() ) { if ( childTuple.getObject() == object ) { node.propagateChildLeftTupleDelete(childTuple); node.propagateLeftIfAllowed(tuple, object); break; } } } }}
ReactiveObjectpublic class MyClass { private String name; private ReactiveObject delegate = new ReactiveObject(this); protected void setName(String name) { this.name = name; delegate.notifyUpdate() }}
public class MyClass extends ReactiveObject { private String name;
public void MyClass() { super(name); } protected void setName(String name) { this.name = name; notifyUpdate() }}
Benchmarkrule R1 when $student : Student () $plan : Plan ( owner == $student.name ) $exam : Exam( plan == $plan.code, course == ”Big Data” ) $grade : Grade( exam == $exam.code )
then
// RHS
end
rule R2 when $student : Student ( $plan : plan ) $exam: Exam( course == ”Big Data” ) from $plan.exams $grade: Grade() from $exam.grades
then /∗ RHS ∗/ end
rule R3 when Student( $grade: /plan/exams{course == ”Big Data”}/grades )
then
/∗ RHS ∗/
end
Benchmark
Benchmark• Visiting a tree with a relational strategy
• Visiting a tree with an Object-Oriented strategyquery findNodesWithValue( Node $from, int $value , List list ) Edge( $n : to, $v : to.value ) from $from.outEdges eval( $v != $value || ( $v == $value && list .add( $n ) ) ) findNodesWithValue ( $n , $value , list; ) end
query findNodesWithValue( int $id , int $value , List list ) $n: Node( id == $id, $v : value ) eval( $v != $value || ( $v == $value && list .add( $n ) ) ) Edge( fromId == $id , $toId : toId ) findNodesWithValue ( $toId , $value , list; ) end
Related Work• EMF-IncQuery• XPath• SPARQL• Gremlin• JXPath
Conclusion and Future Work• Integrated bytecode weavers for ReactiveObject• Externalise ReactiveObject
• Use PropertyChangeListener• Make work with PropertyReactive