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

Commit 6b0b9be

Browse files
RDG cycle detection.
1 parent 13cd8cf commit 6b0b9be

1 file changed

Lines changed: 91 additions & 17 deletions

File tree

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

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package net.sansa_stack.inference.rules
22

3+
import java.util.stream.Collectors
4+
35
import scala.collection.JavaConverters._
46
import scala.language.{existentials, implicitConversions}
57
import scalax.collection.GraphPredef._
@@ -13,6 +15,7 @@ import org.apache.jena.graph.Node
1315
import org.apache.jena.reasoner.TriplePattern
1416
import org.apache.jena.reasoner.rulesys.Rule
1517
import org.apache.jena.vocabulary.RDFS
18+
import org.jgrapht.alg.CycleDetector
1619
import org.jgrapht.alg.cycle.TarjanSimpleCycles
1720

1821
import net.sansa_stack.inference.utils.RuleUtils._
@@ -82,13 +85,24 @@ object RuleDependencyGraphGenerator extends Logging {
8285
}
8386

8487
// 3. pruning
85-
if (pruned) {
86-
g = removeEdgesWithPredicateAlreadyTC(g)
87-
g = removeCyclesIfPredicateIsTC(g)
88-
g = removeEdgesWithCycleOverTCNode(g)
89-
// g = prune(g)
90-
// g = prune1(g)
88+
val pruningRules = List(
89+
removeLoops _,
90+
removeEdgesWithPredicateAlreadyTC _,
91+
removeCyclesIfPredicateIsTC _,
92+
removeEdgesWithCycleOverTCNode _
93+
,removeCycles _
94+
// ,prune _
95+
)
96+
97+
for((rule, i) <- pruningRules.zipWithIndex) g = {
98+
println(s"$i." + "*" * 40)
99+
rule.apply(g)
91100
}
101+
var cnt = 1
102+
pruningRules.foreach(rule => {
103+
104+
105+
})
92106

93107
g
94108
}
@@ -164,21 +178,81 @@ object RuleDependencyGraphGenerator extends Logging {
164178
ret
165179
}
166180

181+
def removeLoops(graph: RuleDependencyGraph): RuleDependencyGraph = {
182+
debug("removing non-TC loops")
183+
var edges2Remove = Seq[Graph[Rule, LDiEdge]#EdgeT]()
184+
185+
graph.nodes.toSeq.foreach(node => {
186+
187+
val loopEdge = node.outgoing.find(_.target == node)
188+
189+
if(loopEdge.isDefined) {
190+
191+
val edge = loopEdge.get
192+
193+
val rule = node.value
194+
195+
val isTC = RuleUtils.isTransitiveClosure(rule, edge.label.asInstanceOf[TriplePattern].getPredicate)
196+
197+
if(!isTC) {
198+
edges2Remove :+= edge
199+
debug(s"loop of node $node")
200+
}
201+
}
202+
})
203+
204+
val newNodes = graph.nodes.map(node => node.value)
205+
val newEdges = graph.edges.clone().filterNot(e => edges2Remove.contains(e)).map(edge => edge.toOuter)
206+
207+
new RuleDependencyGraph(newNodes, newEdges)
208+
}
209+
210+
def removeCycles(graph: RuleDependencyGraph): RuleDependencyGraph = {
211+
debug("removing redundant cycles")
212+
var edges2Remove = Seq[Graph[Rule, LDiEdge]#EdgeT]()
213+
214+
215+
// convert to JGraphT graph for algorithms not contained in Scala Graph API
216+
val g = GraphUtils.asJGraphtRuleSetGraph(graph)
217+
218+
219+
val cycleDetector = new CycleDetector[Rule, LabeledEdge[Rule, TriplePattern]](g)
220+
221+
val cycleDetector2 = new TarjanSimpleCycles[Rule, LabeledEdge[Rule, TriplePattern]](g)
222+
val allCycles = cycleDetector2.findSimpleCycles()
223+
224+
graph.nodes.toSeq.foreach(node => {
225+
debug(s"NODE ${node.value}")
226+
227+
// get cycles of length 3
228+
val cycles = cycleDetector.findCyclesContainingVertex(node.value)
229+
debug(cycles.asScala.mkString(","))
230+
231+
debug(allCycles.asScala.filter(cycle => cycle.contains(node.value)).map(cycle => cycle.asScala.map(rule => rule.getName)).mkString("\n"))
232+
233+
})
234+
235+
val newNodes = graph.nodes.map(node => node.value)
236+
val newEdges = graph.edges.clone().filterNot(e => edges2Remove.contains(e)).map(edge => edge.toOuter)
237+
238+
new RuleDependencyGraph(newNodes, newEdges)
239+
}
240+
167241
def prune(graph: RuleDependencyGraph): RuleDependencyGraph = {
168242
var redundantEdges = Seq[Graph[Rule, LDiEdge]#EdgeT]()
169243

170244
// graph.outerNodeTraverser.foreach(n => println(n))
171245

172246
// for each node n in G
173-
graph.nodes.foreach(node => {
247+
graph.nodes.toSeq.foreach(node => {
174248
debug("#" * 20)
175249
debug(s"NODE:${node.value.getName}")
176250

177251
// get all direct successors
178252
var successors = node.innerNodeTraverser.withParameters(Parameters(maxDepth = 1)).toList
179253
// remove node itself, if it's a cyclic node
180254
successors = successors.filterNot(_.equals(node))
181-
debug(s"SUCCESSORS:${successors.map(n => n.value.getName)}")
255+
debug(s"DIRECT SUCCESSORS:${successors.map(n => n.value.getName).mkString(", ")}")
182256

183257
if (successors.size > 1) {
184258
// get pairs of successors
@@ -188,22 +262,22 @@ object RuleDependencyGraphGenerator extends Logging {
188262
debug(s"PAIR:${pair._1.value.getName},${pair._2.value.getName}")
189263
val n1 = pair._1
190264
val edge1 = node.innerEdgeTraverser.filter(e => e.source == node && e.target == n1).head
265+
191266
val n2 = pair._2
192267
val edge2 = node.innerEdgeTraverser.filter(e => e.source == node && e.target == n2).head
193268

194269
// n --p--> n1
195270
val path1 = node.withSubgraph(edges = !_.equals(edge2)) pathTo n2
196271
if (path1.isDefined) {
197-
debug(s"PATH TO:${n2.value.getName}")
198-
debug(s"PATH:${path1.get.edges.toList.map(edge => asString(edge))}")
272+
debug(s"PATH TO ${n2.value.getName}: ${path1.get.edges.toList.map(edge => asString(edge))}")
199273
val edges = path1.get.edges.toList
200274
edges.foreach(edge => {
201275
debug(s"EDGE:${asString(edge)}")
202276
})
203277
val last = edges.last.value
204278

205279
if (last.label == edge2.label) {
206-
debug("redundant")
280+
debug(s"redundant edge $edge2")
207281
redundantEdges :+= edge2
208282
}
209283
} else {
@@ -221,7 +295,7 @@ object RuleDependencyGraphGenerator extends Logging {
221295
val last = edges.last.value
222296

223297
if (last.label == edge1.label) {
224-
debug("redundant")
298+
debug(s"redundant edge $edge1")
225299
redundantEdges :+= edge1
226300
}
227301
} else {
@@ -243,7 +317,7 @@ object RuleDependencyGraphGenerator extends Logging {
243317
var redundantEdges = Seq[Graph[Rule, LDiEdge]#EdgeT]()
244318

245319
// for each node n in G
246-
graph.nodes.foreach(node => {
320+
graph.nodes.toSeq.foreach(node => {
247321
debug("#" * 20)
248322
debug(s"NODE:${node.value.getName}")
249323

@@ -265,7 +339,7 @@ object RuleDependencyGraphGenerator extends Logging {
265339
var redundantEdges = Seq[Graph[Rule, LDiEdge]#EdgeT]()
266340

267341
// for each node n in G
268-
graph.nodes.foreach(node => {
342+
graph.nodes.toSeq.foreach(node => {
269343
debug("#" * 20)
270344
debug(s"NODE:${node.value.getName}")
271345

@@ -359,7 +433,7 @@ object RuleDependencyGraphGenerator extends Logging {
359433
var redundantEdges = Seq[Graph[Rule, LDiEdge]#EdgeT]()
360434

361435
// for each node n in G
362-
graph.nodes.foreach(node => {
436+
graph.nodes.toSeq.foreach(node => {
363437
debug("#" * 20)
364438
debug(s"NODE:${node.value.getName}")
365439

@@ -370,7 +444,7 @@ object RuleDependencyGraphGenerator extends Logging {
370444
debug(s"SUCCESSORS:${successors.map(n => n.value.getName)}")
371445

372446
// check for nodes that do compute the TC
373-
successors.foreach(n => {
447+
successors.toSeq.foreach(n => {
374448
debug(s"successor:${n.value.getName}")
375449
val rule = n.value
376450
val edges = node.innerEdgeTraverser.filter(e => e.target == n && e.source != n)
@@ -415,7 +489,7 @@ object RuleDependencyGraphGenerator extends Logging {
415489
var redundantEdges = Seq[Graph[Rule, LDiEdge]#EdgeT]()
416490

417491
// for each node n in G
418-
graph.nodes.foreach(node => {
492+
graph.nodes.toSeq.foreach(node => {
419493
val rule = node.value
420494

421495
// we only handle cyclic rules

0 commit comments

Comments
 (0)