Skip to content
This repository was archived by the owner on Oct 8, 2020. It is now read-only.

Commit d7bfc31

Browse files
General plan generator.
1 parent d4dda38 commit d7bfc31

10 files changed

Lines changed: 334 additions & 3 deletions

File tree

sansa-inference-common/src/main/resources/schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{
99
name: 'Triples',
1010
type: 'custom',
11-
factory: 'org.sansa.inference.rules.plan.TriplesTableFactory'
11+
factory: 'net.sansa_stack.inference.rules.plan.TriplesTableFactory'
1212
}
1313
]
1414
}

sansa-inference-common/src/main/scala/net/sansa_stack/inference/rules/plan/PlanGenerator.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class PlanGenerator extends Logging{
5757
val clusters: Array[RelOptCluster] = Array(null)
5858
val relOptSchemas: Array[RelOptSchema] = Array(null)
5959
val rootSchemas: Array[SchemaPlus] = Array(null)
60+
6061
Frameworks.withPlanner(new PlannerAction[Void] {
6162
override def apply(
6263
cluster: RelOptCluster,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package net.sansa_stack.inference.rules.plan
2+
3+
import org.apache.jena.reasoner.rulesys.Rule
4+
5+
/**
6+
* A SQL generator for rules.
7+
*
8+
* @author Lorenz Buehmann
9+
*/
10+
trait SQLGenerator {
11+
12+
/**
13+
* Generates a SQL query for the given rule.
14+
*
15+
* @param rule the rule
16+
* @return the SQL query
17+
*/
18+
def generateSQLQuery(rule: Rule): String
19+
20+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package net.sansa_stack.inference.rules.plan
2+
3+
import com.google.common.collect.ImmutableSet
4+
import org.apache.calcite.plan.{RelOptCluster, RelTraitSet}
5+
import org.apache.calcite.rel.RelNode
6+
import org.apache.calcite.rel.core.CorrelationId
7+
import org.apache.calcite.rel.logical.{LogicalFilter, LogicalJoin, LogicalProject}
8+
import org.apache.calcite.rex.RexNode
9+
10+
/**
11+
* @author Lorenz Buehmann
12+
*/
13+
class SimplePlanExecutor {
14+
15+
def execute(plan: RelNode): Unit = {
16+
// plan match {
17+
// case LogicalProject => plan.asInstanceOf[LogicalProject].
18+
// case LogicalJoin =>
19+
// case LogicalFilter(cluster,
20+
// traitSet,
21+
// child,
22+
// condition,
23+
// variablesSet) =>
24+
// }
25+
}
26+
27+
28+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package net.sansa_stack.inference.rules.plan
2+
3+
import java.io.PrintWriter
4+
5+
import org.apache.calcite.config.Lex
6+
import org.apache.calcite.plan.{Contexts, ConventionTraitDef, RelTrait, RelTraitDef}
7+
import org.apache.calcite.rel.{RelCollationTraitDef, RelNode}
8+
import org.apache.calcite.schema.SchemaPlus
9+
import org.apache.calcite.sql.parser.SqlParser
10+
import org.apache.calcite.tools.{FrameworkConfig, Frameworks, Planner, RuleSets}
11+
import collection.JavaConverters._
12+
import scala.util.Try
13+
14+
import org.apache.calcite.rel.`type`.RelDataTypeSystem
15+
import org.apache.calcite.rel.externalize.RelWriterImpl
16+
import org.apache.calcite.rel.rules.{FilterJoinRule, ProjectJoinTransposeRule}
17+
import org.apache.jena.reasoner.rulesys.Rule
18+
19+
import net.sansa_stack.inference.utils.RuleUtils
20+
21+
/**
22+
* @author Lorenz Buehmann
23+
*/
24+
class SimplePlanGenerator(schema: SchemaPlus) {
25+
26+
val traitDefs: List[RelTraitDef[_ <: RelTrait]] = List(ConventionTraitDef.INSTANCE, RelCollationTraitDef.INSTANCE)
27+
28+
val optRuleSet = RuleSets.ofList(
29+
FilterJoinRule.FILTER_ON_JOIN,// push a filter into a join
30+
FilterJoinRule.JOIN,// push filter into the children of a join
31+
ProjectJoinTransposeRule.INSTANCE// push a projection to the children of a join
32+
)
33+
34+
val calciteFrameworkConfig: FrameworkConfig =
35+
Frameworks.newConfigBuilder
36+
.parserConfig(
37+
SqlParser.configBuilder
38+
// Lexical configuration defines how identifiers are quoted, whether they are converted to upper or lower
39+
.setLex(Lex.MYSQL)
40+
.build)
41+
// Sets the schema to use by the planner
42+
.defaultSchema(schema)
43+
.traitDefs(traitDefs.asJava)
44+
// Context provides a way to store data within the planner session that can be accessed in planner rules.
45+
.context(Contexts.EMPTY_CONTEXT)
46+
// Rule sets to use in transformation phases. Each transformation phase can use a different set of rules.
47+
.ruleSets(optRuleSet)
48+
// Custom cost factory to use during optimization
49+
.costFactory(null)
50+
.typeSystem(RelDataTypeSystem.DEFAULT)
51+
.build()
52+
53+
lazy val planner: Planner = Frameworks.getPlanner(calciteFrameworkConfig)
54+
55+
val sqlGenerator = new SimpleSQLGenerator
56+
57+
/**
58+
* Generates a logical plan for the rule.
59+
*
60+
* @param rule the rule
61+
* @return the root node of the logical plan
62+
*/
63+
def generateLogicalPlan(rule: Rule): RelNode = {
64+
// generate SQL query
65+
val sqlQuery = sqlGenerator.generateSQLQuery(rule)
66+
67+
// parse to SQL node
68+
val sqlNode = Try(planner.parse(sqlQuery))
69+
70+
// validate the SQL node
71+
val validatedSqlNode = planner.validate(sqlNode.get)
72+
73+
// return the root node
74+
planner.rel(validatedSqlNode).project
75+
}
76+
77+
/**
78+
* Generates a logical plan for the rules.
79+
*
80+
* @param rules the rules
81+
* @return the root node of the logical plan
82+
*/
83+
def generateLogicalPlan(rules: Seq[Rule]): RelNode = {
84+
// generate SQL query
85+
val sqlQuery = rules.map(sqlGenerator.generateSQLQuery _).mkString(" UNION ")
86+
87+
// parse to SQL node
88+
val sqlNode = Try(planner.parse(sqlQuery))
89+
90+
// validate the SQL node
91+
val validatedSqlNode = planner.validate(sqlNode.get)
92+
93+
// return the root node
94+
planner.rel(validatedSqlNode).project
95+
}
96+
}
97+
98+
object SimplePlanGenerator {
99+
100+
def main(args: Array[String]): Unit = {
101+
102+
val planGenerator = new SimplePlanGenerator(TriplesSchema.get())
103+
104+
val rules = RuleUtils.load("rules/rdfs-simple.rules")
105+
106+
val plan = planGenerator.generateLogicalPlan(Seq(rules(0), rules(1)))
107+
108+
plan.explain(new RelWriterImpl(new PrintWriter(System.out)))
109+
}
110+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package net.sansa_stack.inference.rules.plan
2+
3+
import scala.collection.mutable
4+
5+
import org.apache.jena.graph.Node
6+
import org.apache.jena.reasoner.rulesys.Rule
7+
8+
import net.sansa_stack.inference.utils.RuleUtils.RuleExtension
9+
import net.sansa_stack.inference.utils.TripleUtils._
10+
import net.sansa_stack.inference.utils.{Logging, RuleUtils}
11+
12+
/**
13+
* @author Lorenz Buehmann
14+
*/
15+
class SimpleSQLGenerator extends SQLGenerator with Logging {
16+
17+
def generateSQLQuery(rule: Rule): String = {
18+
info(s"Rule:\n$rule")
19+
20+
val body = rule.bodyTriplePatterns().map(tp => tp.toTriple).toSet
21+
22+
val visited = mutable.Set[org.apache.jena.graph.Triple]()
23+
24+
// process(body.head, body, visited)
25+
26+
// group triple patterns by var
27+
val map = new mutable.HashMap[Node, collection.mutable.Set[org.apache.jena.graph.Triple]]() with mutable.MultiMap[Node, org.apache.jena.graph.Triple]
28+
body.foreach { tp =>
29+
val vars = RuleUtils.varsOf(tp)
30+
vars.foreach { v =>
31+
map.addBinding(v, tp)
32+
}
33+
}
34+
35+
val joins = new mutable.HashSet[Join]
36+
37+
map.foreach{e =>
38+
val v = e._1
39+
val tps = e._2.toList.sortBy(_.toString).combinations(2).foreach(c =>
40+
joins.add(new Join(c(0), c(1), v))
41+
)
42+
}
43+
44+
val sqlQuery = new Plan(body, rule.headTriplePatterns().toList.head.asTriple(), joins).toSQL
45+
info(s"SQL Query:\n$sqlQuery")
46+
47+
sqlQuery
48+
}
49+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package net.sansa_stack.inference.utils
2+
3+
import org.apache.jena.graph.NodeFactory
4+
import org.apache.jena.tdb.base.file.Location
5+
import org.apache.jena.tdb.setup.DatasetBuilderStd
6+
import org.apache.jena.tdb.store.DatasetGraphTDB
7+
import tdb.bulkloader2.CmdNodeTableBuilder
8+
import tdb.cmdline.CmdTDB
9+
10+
/**
11+
* @author Lorenz Buehmann
12+
*/
13+
object NodeTableBuilder {
14+
15+
def main(args: Array[String]): Unit = {
16+
CmdTDB.init()
17+
DatasetBuilderStd.setOptimizerWarningFlag(false)
18+
new CmdNodeTableBuilder(args: _*).mainRun()
19+
20+
val location = Location.create("/tmp/node_table")
21+
22+
// This formats the location correctly.
23+
// But we're not really interested in it all.
24+
val dsg = DatasetBuilderStd.create(location)
25+
26+
// so close indexes and the prefix table.
27+
val nodeTable = dsg.getTripleTable.getNodeTupleTable.getNodeTable
28+
val id = nodeTable.getNodeIdForNode(NodeFactory.createURI("http://www.Department0.University6.edu/FullProfessor0"))
29+
println(id)
30+
31+
val node = nodeTable.getNodeForNodeId(id)
32+
33+
println(node)
34+
}
35+
36+
}

sansa-inference-common/src/main/scala/net/sansa_stack/inference/utils/RuleUtils.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import scalax.collection.mutable.Graph
88
import org.apache.jena.graph.Node
99
import org.apache.jena.reasoner.TriplePattern
1010
import org.apache.jena.reasoner.rulesys.Rule
11+
import org.jgrapht.alg.cycle.TarjanSimpleCycles
1112

1213
import net.sansa_stack.inference.rules.RuleEntailmentType
1314
import net.sansa_stack.inference.rules.RuleEntailmentType._
15+
import net.sansa_stack.inference.utils.graph.LabeledEdge
1416

1517
/**
1618
* Utility class for rules.
@@ -318,6 +320,18 @@ object RuleUtils {
318320
bodyPredicates.intersect(headPredicates).nonEmpty
319321

320322
}
323+
324+
val g = GraphUtils.asJGraphtRuleGraph(asGraph(rule))
325+
326+
// get cycles of length > 2
327+
val cycleDetector = new TarjanSimpleCycles[Node, LabeledEdge[Node, String]](g)
328+
val cycles = cycleDetector.findSimpleCycles().asScala.filter(c => c.size() == 2)
329+
330+
cycles.foreach(c => {
331+
println(c)
332+
})
333+
334+
true
321335
}
322336

323337
/**

sansa-inference-common/src/test/scala/net/sansa_stack/inference/common/DependencyGraphTest.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.sansa_stack.inference.common
22

33
import net.sansa_stack.inference.rules._
4+
import net.sansa_stack.inference.rules.minimizer.DefaultRuleDependencyGraphMinimizer
45
import net.sansa_stack.inference.utils.GraphUtils._
56
import net.sansa_stack.inference.utils.RuleUtils
67

@@ -18,19 +19,21 @@ object DependencyGraphTest {
1819
// val names = Seq("rdfs5", "rdfs7", "rdfp3", "rdfp4") // property TC rule + some instance rules
1920

2021
// define the rules
21-
val rules = RuleSets.OWL_HORST.filter(r => names.contains(r.getName))
22+
val rules = RuleSets.OWL_HORST//.filter(r => names.contains(r.getName))
2223
val profile = ReasoningProfile.OWL_HORST
2324
// val rules = RuleSets.RDFS_SIMPLE
2425
// val profile = ReasoningProfile.RDFS_SIMPLE
2526

27+
val minimizer = new DefaultRuleDependencyGraphMinimizer()
28+
2629
// export graphs
2730
rules.foreach(rule => RuleUtils.asGraph(rule).export(s"${path}/rule-${rule.getName}.graphml"))
2831

2932
// generate the rule dependency graph
3033
var dependencyGraph = RuleDependencyGraphGenerator.generate(rules)
3134
dependencyGraph.export(s"${path}/rdg-${profile}.graphml")
3235

33-
dependencyGraph = RuleDependencyGraphGenerator.generate(rules, pruned = true)
36+
dependencyGraph = minimizer.execute(dependencyGraph) // RuleDependencyGraphGenerator.generate(rules, pruned = true)
3437
dependencyGraph.export(s"${path}/rdg-${profile}-pruned.graphml")
3538
// dependencyGraph.exportAsPDF(s"${path}/rdg-${profile}-pruned.pdf")
3639

@@ -51,6 +54,8 @@ object DependencyGraphTest {
5154

5255
layer._2.foreach{rdg =>
5356
println("Processing dependency graph " + rdg.printNodes())
57+
println(rdg.isCyclic)
58+
rdg.nodes.foreach(n => println(n findSuccessor (_.outDegree >= 1)))
5459
}
5560
}
5661
}

0 commit comments

Comments
 (0)