aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/uk/ac/ox/cs/rsacomb/approximation
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/approximation')
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala38
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala178
2 files changed, 199 insertions, 17 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala
index 60a88fb..e261bce 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Lowerbound.scala
@@ -13,10 +13,10 @@ import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
13import scalax.collection.GraphTraversal._ 13import scalax.collection.GraphTraversal._
14 14
15import uk.ac.ox.cs.rsacomb.RSAOntology 15import uk.ac.ox.cs.rsacomb.RSAOntology
16import uk.ac.ox.cs.rsacomb.RSAUtil
17import uk.ac.ox.cs.rsacomb.ontology.Ontology 16import uk.ac.ox.cs.rsacomb.ontology.Ontology
17import uk.ac.ox.cs.rsacomb.util.DataFactory
18 18
19object LowerBound { 19object Lowerbound {
20 20
21 private val manager = OWLManager.createOWLOntologyManager() 21 private val manager = OWLManager.createOWLOntologyManager()
22 private val factory = manager.getOWLDataFactory() 22 private val factory = manager.getOWLDataFactory()
@@ -38,7 +38,8 @@ object LowerBound {
38 * 38 *
39 * @see [[uk.ac.ox.cs.rsacomb.converter.Normalizer]] 39 * @see [[uk.ac.ox.cs.rsacomb.converter.Normalizer]]
40 */ 40 */
41class LowerBound extends Approximation[RSAOntology] { 41class Lowerbound(implicit fresh: DataFactory)
42 extends Approximation[RSAOntology] {
42 43
43 /** Simplify conversion between Java and Scala collections */ 44 /** Simplify conversion between Java and Scala collections */
44 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ 45 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
@@ -50,6 +51,7 @@ class LowerBound extends Approximation[RSAOntology] {
50 def approximate(ontology: Ontology): RSAOntology = 51 def approximate(ontology: Ontology): RSAOntology =
51 toRSA( 52 toRSA(
52 new Ontology( 53 new Ontology(
54 ontology.origin,
53 ontology.axioms filter inALCHOIQ flatMap shift, 55 ontology.axioms filter inALCHOIQ flatMap shift,
54 ontology.datafiles 56 ontology.datafiles
55 ) 57 )
@@ -122,27 +124,25 @@ class LowerBound extends Approximation[RSAOntology] {
122 val sup = a.getSuperClass.getNNF 124 val sup = a.getSuperClass.getNNF
123 sup match { 125 sup match {
124 case sup: OWLObjectUnionOf => { 126 case sup: OWLObjectUnionOf => {
125 val body = sub.asConjunctSet.map((atom) => 127 val body =
126 (atom, RSAUtil.getFreshOWLClass()) 128 sub.asConjunctSet.map((atom) => (atom, fresh.getOWLClass))
127 ) 129 val head =
128 val head = sup.asDisjunctSet.map((atom) => 130 sup.asDisjunctSet.map((atom) => (atom, fresh.getOWLClass))
129 (atom, RSAUtil.getFreshOWLClass())
130 )
131 131
132 val r1 = 132 val r1 =
133 LowerBound.factory.getOWLSubClassOfAxiom( 133 Lowerbound.factory.getOWLSubClassOfAxiom(
134 LowerBound.factory.getOWLObjectIntersectionOf( 134 Lowerbound.factory.getOWLObjectIntersectionOf(
135 (body.map(_._1) ++ head.map(_._2)): _* 135 (body.map(_._1) ++ head.map(_._2)): _*
136 ), 136 ),
137 LowerBound.factory.getOWLNothing 137 Lowerbound.factory.getOWLNothing
138 ) 138 )
139 139
140 val r2s = 140 val r2s =
141 for { 141 for {
142 (a, na) <- head 142 (a, na) <- head
143 hs = head.map(_._2).filterNot(_ equals na) 143 hs = head.map(_._2).filterNot(_ equals na)
144 } yield LowerBound.factory.getOWLSubClassOfAxiom( 144 } yield Lowerbound.factory.getOWLSubClassOfAxiom(
145 LowerBound.factory.getOWLObjectIntersectionOf( 145 Lowerbound.factory.getOWLObjectIntersectionOf(
146 (body.map(_._1) ++ hs): _* 146 (body.map(_._1) ++ hs): _*
147 ), 147 ),
148 a 148 a
@@ -152,8 +152,8 @@ class LowerBound extends Approximation[RSAOntology] {
152 for { 152 for {
153 (a, na) <- body 153 (a, na) <- body
154 bs = body.map(_._1).filterNot(_ equals a) 154 bs = body.map(_._1).filterNot(_ equals a)
155 } yield LowerBound.factory.getOWLSubClassOfAxiom( 155 } yield Lowerbound.factory.getOWLSubClassOfAxiom(
156 LowerBound.factory.getOWLObjectIntersectionOf( 156 Lowerbound.factory.getOWLObjectIntersectionOf(
157 (bs ++ head.map(_._2)): _* 157 (bs ++ head.map(_._2)): _*
158 ), 158 ),
159 na 159 na
@@ -219,7 +219,11 @@ class LowerBound extends Approximation[RSAOntology] {
219 }.toList 219 }.toList
220 220
221 /* Remove axioms from approximated ontology */ 221 /* Remove axioms from approximated ontology */
222 RSAOntology(ontology.axioms diff toDelete, ontology.datafiles) 222 RSAOntology(
223 ontology.origin,
224 ontology.axioms diff toDelete,
225 ontology.datafiles
226 )
223 } 227 }
224 228
225 // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> 229 // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~>
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala
new file mode 100644
index 0000000..469d774
--- /dev/null
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/approximation/Upperbound.scala
@@ -0,0 +1,178 @@
1package uk.ac.ox.cs.rsacomb.approximation
2
3// import java.io.File
4
5import org.semanticweb.owlapi.apibinding.OWLManager
6import org.semanticweb.owlapi.model.{IRI => _, _}
7
8import tech.oxfordsemantic.jrdfox.logic.expression.{Resource, IRI}
9
10import scala.collection.mutable.Map
11import scalax.collection.Graph
12import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
13import scalax.collection.GraphTraversal._
14
15import uk.ac.ox.cs.rsacomb.RSAOntology
16import uk.ac.ox.cs.rsacomb.ontology.Ontology
17import uk.ac.ox.cs.rsacomb.util.DataFactory
18
19object Upperbound {
20
21 private val manager = OWLManager.createOWLOntologyManager()
22 private val factory = manager.getOWLDataFactory()
23
24}
25
26/** Approximation algorithm that mantains completeness for CQ answering.
27 *
28 * The input OWL 2 ontology is assumed to be normalized and the output
29 * ontology is guaranteed to be in RSA.
30 *
31 * The algorithm is performed in three steps:
32 * 1. the ontology is reduced to ALCHOIQ by discarding any axiom
33 * that is not in the language;
34 * 2. the ontology is further reduced to Horn-ALCHOIQ by shifting
35 * axioms with disjunction on the rhs;
36 * 3. the ontology is approximated to RSA by manipulating its
37 * dependency graph.
38 *
39 * @see [[uk.ac.ox.cs.rsacomb.converter.Normalizer]]
40 */
41class Upperbound(implicit fresh: DataFactory)
42 extends Approximation[RSAOntology] {
43
44 /** Simplify conversion between Java and Scala collections */
45 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
46
47 /** Simplify conversion between OWLAPI and RDFox concepts */
48 // import uk.ac.ox.cs.rsacomb.implicits.RDFox._
49
50 /** Main entry point for the approximation algorithm */
51 def approximate(ontology: Ontology): RSAOntology =
52 toRSA(
53 new Ontology(
54 ontology.origin,
55 ontology.axioms flatMap toConjuncts,
56 ontology.datafiles
57 )
58 )
59
60 /** Turn disjuncts into conjuncts
61 *
62 * This is a very naïve way of getting rid of disjunction preserving
63 * completeness of CQ answering.
64 *
65 * @todo implement a choice function that decides which disjunct to
66 * keep instead of keeping all of them. Note that PAGOdA is currently
67 * doing something similar.
68 */
69 private def toConjuncts(axiom: OWLLogicalAxiom): List[OWLLogicalAxiom] =
70 axiom match {
71 case a: OWLSubClassOfAxiom => {
72 val sub = a.getSubClass.getNNF
73 val sup = a.getSuperClass.getNNF
74 sup match {
75 case sup: OWLObjectUnionOf =>
76 sup.asDisjunctSet.map(
77 Upperbound.factory.getOWLSubClassOfAxiom(sub, _)
78 )
79 case _ => List(axiom)
80 }
81 }
82 case _ => List(axiom)
83 }
84
85 /** Approximate a Horn-ALCHOIQ ontology to RSA
86 *
87 * This is done by gathering those existential axioms that prevent
88 * the ontology dependency graph from being tree-shaped and constant
89 * skolemize them.
90 *
91 * @param ontology the set of axioms to approximate.
92 * @return the approximated RSA ontology
93 */
94 private def toRSA(ontology: Ontology): RSAOntology = {
95 /* Compute the dependency graph for the ontology */
96 val (graph, nodemap) = ontology.dependencyGraph
97
98 /* Define node colors for the graph visit */
99 sealed trait NodeColor
100 case object Unvisited extends NodeColor
101 case object Visited extends NodeColor
102 case object ToSkolem extends NodeColor
103
104 /* Keep track of node colors during graph visit */
105 var color = Map.from[Resource, NodeColor](
106 graph.nodes.toOuter.map(k => (k, Unvisited))
107 )
108
109 for {
110 component <- graph.componentTraverser().map(_ to Graph)
111 edge <- component
112 .outerEdgeTraverser(component.nodes.head)
113 .withKind(BreadthFirst)
114 } yield {
115 val source = edge._1
116 val target = edge._2
117 color(source) match {
118 case Unvisited | Visited => {
119 color(target) match {
120 case Unvisited =>
121 color(source) = Visited;
122 color(target) = Visited
123 case Visited =>
124 color(source) = ToSkolem
125 case ToSkolem =>
126 color(source) = Visited
127 }
128 }
129 case ToSkolem => {}
130 }
131 }
132
133 val toSkolem = color.collect { case (resource: IRI, ToSkolem) =>
134 nodemap(resource.getIRI)
135 }.toList
136
137 // Force constant skolemization by introducing a fresh individual
138 // (singleton class).
139 val skolemized = toSkolem flatMap { (axiom) =>
140 import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._
141 axiom.toTriple match {
142 case Some((subclass, role, filler)) => {
143 val skolem = Upperbound.factory.getOWLNamedIndividual(
144 s"i_${axiom.toString.hashCode}"
145 )
146 val cls = fresh.getOWLClass
147 List(
148 Upperbound.factory.getOWLSubClassOfAxiom(
149 subclass,
150 Upperbound.factory.getOWLObjectSomeValuesFrom(role, cls)
151 ),
152 Upperbound.factory.getOWLSubClassOfAxiom(
153 cls,
154 Upperbound.factory.getOWLObjectOneOf(skolem)
155 ),
156 Upperbound.factory.getOWLClassAssertionAxiom(filler, skolem)
157 )
158 }
159 case None => List()
160 }
161 }
162
163 /* Substitute selected axioms with their "skolemized" version */
164 RSAOntology(
165 ontology.origin,
166 ontology.axioms diff toSkolem concat skolemized,
167 ontology.datafiles
168 )
169 }
170
171 // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~>
172 // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F,
173 // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D)
174 // val edges2 = Seq('I ~> 'M, 'I ~> 'L, 'L ~> 'N, 'M ~> 'N)
175 // val edges3 = Seq('P ~> 'O)
176 // val graph = Graph.from(edges = edges1 ++ edges2 ++ edges3)
177
178}