aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2021-07-20 15:09:20 +0100
committerFederico Igne <git@federicoigne.com>2021-07-20 15:14:18 +0100
commit53646646f924887768688794aee46874ed194673 (patch)
tree50e468ed4ad50bcfb23355e66c0def2e3997fde4
parentf0d1bfe564853a63128ad139520c9838778a7b61 (diff)
downloadRSAComb-53646646f924887768688794aee46874ed194673.tar.gz
RSAComb-53646646f924887768688794aee46874ed194673.zip
Generalize dependency graph generation
The code to generate the dependency graph has been moved in the companion object of the generic OWL 2 ontology wrapper Ontology. This signals that we could potentially build a dependency graph for any ontology (and not only RSA ontology). Moreover, a dependency graph can be build for an Ontology object or an arbitrary TBox and Abox.
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala2
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala10
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala111
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/approximation/lowerbound.scala4
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala17
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala13
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala3
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala141
8 files changed, 182 insertions, 119 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala
index 6621f59..af6c463 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala
@@ -76,7 +76,7 @@ class CanonicalModel(val ontology: RSAOntology) {
76 val (facts, rules): (List[TupleTableAtom], List[Rule]) = { 76 val (facts, rules): (List[TupleTableAtom], List[Rule]) = {
77 // Compute rules from ontology axioms 77 // Compute rules from ontology axioms
78 val (facts, rules) = { 78 val (facts, rules) = {
79 val term = RSAOntology.genFreshVariable() 79 val term = RSAUtil.genFreshVariable()
80 val unsafe = ontology.unsafeRoles 80 val unsafe = ontology.unsafeRoles
81 ontology.axioms 81 ontology.axioms
82 .map(a => 82 .map(a =>
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
index b749401..82da9df 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
@@ -103,6 +103,16 @@ object RSAConfig {
103/** Main entry point to the program */ 103/** Main entry point to the program */
104object RSAComb extends App { 104object RSAComb extends App {
105 105
106 /*
107 * TODO: Aiming for this workflow:
108 *
109 * implicit val manager = new Manager(...)
110 * val original = manager.importFromFile("ontology.owl")
111 * val axioms = original.getAxioms.filter(isLogicalAxiom).normalize(normalizer)
112 * val ontology = new Ontology(axioms, data)
113 * val rsa = ontology.toRSA(approximator)
114 */
115
106 /* Command-line options */ 116 /* Command-line options */
107 val config = RSAConfig.parse(args.toList) 117 val config = RSAConfig.parse(args.toList)
108 118
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
index bbbbcf3..9902fcd 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
@@ -71,108 +71,10 @@ object RSAUtil {
71 // manager.createOntology(axioms.asJava) 71 // manager.createOntology(axioms.asJava)
72 // } 72 // }
73 73
74 /** Compute the RSA dependency graph for a set of axioms
75 *
76 * @return a tuple containing the dependency graph and a map between
77 * the newly introduced constants and the corresponding input axioms.
78 *
79 * @note no check on the ontology language is performed since the
80 * construction of the dependency graph is computed regardless. The
81 * input axioms are assumed to be normalized.
82 */
83 def dependencyGraph(
84 axioms: List[OWLLogicalAxiom],
85 datafiles: List[File]
86 ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = {
87 val unsafe = RSAOntology(axioms, datafiles).unsafeRoles
88 var nodemap = Map.empty[String, OWLAxiom]
89
90 object RSAConverter extends RDFoxConverter {
91
92 override def convert(
93 expr: OWLClassExpression,
94 term: Term,
95 unsafe: List[OWLObjectPropertyExpression],
96 skolem: SkolemStrategy,
97 suffix: RSASuffix
98 ): Shards =
99 (expr, skolem) match {
100
101 case (e: OWLObjectSomeValuesFrom, c: Constant) => {
102 nodemap.update(c.iri.getIRI, c.axiom)
103 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
104 if (unsafe contains e.getProperty)
105 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
106 else
107 (RSA.PE(term, c.iri) :: res, ext)
108 }
109
110 case (e: OWLDataSomeValuesFrom, c: Constant) => {
111 nodemap.update(c.iri.getIRI, c.axiom)
112 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
113 if (unsafe contains e.getProperty)
114 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
115 else
116 (RSA.PE(term, c.iri) :: res, ext)
117 }
118
119 case _ => super.convert(expr, term, unsafe, skolem, suffix)
120 }
121 }
122
123 /* Ontology convertion into LP rules */
124 val term = RSAOntology.genFreshVariable()
125 val result = axioms.map(a =>
126 RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)
127 )
128
129 val datalog = result.unzip
130 val facts = datalog._1.flatten
131 var rules = datalog._2.flatten
132
133 /* Open connection with RDFox */
134 val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph")
135
136 /* Add additional built-in rules */
137 val varX = Variable.create("X")
138 val varY = Variable.create("Y")
139 rules = Rule.create(
140 RSA.E(varX, varY),
141 RSA.PE(varX, varY),
142 RSA.U(varX),
143 RSA.U(varY)
144 ) :: rules
145 /* Load facts and rules from ontology */
146 RDFoxUtil.addFacts(data, facts)
147 RDFoxUtil.addRules(data, rules)
148 /* Load data files */
149 RDFoxUtil.addData(data, datafiles: _*)
150
151 /* Build the graph */
152 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }"
153 val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get
154 var edges: Seq[DiEdge[Resource]] =
155 answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 }
156 val graph = Graph(edges: _*)
157
158 /* Close connection to RDFox */
159 RDFoxUtil.closeConnection(server, data)
160
161 (graph, nodemap)
162 }
163}
164
165object RSAOntology {
166
167 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
168
169 /** Manager instance to interface with OWLAPI */ 74 /** Manager instance to interface with OWLAPI */
170 val manager = OWLManager.createOWLOntologyManager() 75 val manager = OWLManager.createOWLOntologyManager()
171 val factory = manager.getOWLDataFactory() 76 val factory = manager.getOWLDataFactory()
172 77
173 /** Name of the RDFox data store used for CQ answering */
174 private val DataStore = "answer_computation"
175
176 /** Simple fresh variable/class generator */ 78 /** Simple fresh variable/class generator */
177 private var counter = -1; 79 private var counter = -1;
178 def genFreshVariable(): Variable = { 80 def genFreshVariable(): Variable = {
@@ -184,6 +86,19 @@ object RSAOntology {
184 factory.getOWLClass(s"X$counter") 86 factory.getOWLClass(s"X$counter")
185 } 87 }
186 88
89}
90
91object RSAOntology {
92
93 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
94
95 /** Manager instance to interface with OWLAPI */
96 val manager = OWLManager.createOWLOntologyManager()
97 val factory = manager.getOWLDataFactory()
98
99 /** Name of the RDFox data store used for CQ answering */
100 private val DataStore = "answer_computation"
101
187 def apply( 102 def apply(
188 axioms: List[OWLLogicalAxiom], 103 axioms: List[OWLLogicalAxiom],
189 datafiles: List[File] 104 datafiles: List[File]
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 8a86d19..2750cca 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
@@ -129,10 +129,10 @@ class LowerBound extends Approximation {
129 sup match { 129 sup match {
130 case sup: OWLObjectUnionOf => { 130 case sup: OWLObjectUnionOf => {
131 val body = sub.asConjunctSet.map((atom) => 131 val body = sub.asConjunctSet.map((atom) =>
132 (atom, RSAOntology.getFreshOWLClass()) 132 (atom, RSAUtil.getFreshOWLClass())
133 ) 133 )
134 val head = sup.asDisjunctSet.map((atom) => 134 val head = sup.asDisjunctSet.map((atom) =>
135 (atom, RSAOntology.getFreshOWLClass()) 135 (atom, RSAUtil.getFreshOWLClass())
136 ) 136 )
137 137
138 val r1 = 138 val r1 =
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala
index 4b298f4..80dd222 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala
@@ -5,6 +5,7 @@ import org.semanticweb.owlapi.model._
5 5
6import uk.ac.ox.cs.rsacomb.util.Logger 6import uk.ac.ox.cs.rsacomb.util.Logger
7import uk.ac.ox.cs.rsacomb.RSAOntology 7import uk.ac.ox.cs.rsacomb.RSAOntology
8import uk.ac.ox.cs.rsacomb.RSAUtil
8 9
9object Normalizer { 10object Normalizer {
10 11
@@ -53,7 +54,7 @@ class Normalizer() {
53 * C c D -> { C c X, X c D } 54 * C c D -> { C c X, X c D }
54 */ 55 */
55 case _ if !sub.isOWLClass && !sup.isOWLClass => { 56 case _ if !sub.isOWLClass && !sup.isOWLClass => {
56 val cls = RSAOntology.getFreshOWLClass() 57 val cls = RSAUtil.getFreshOWLClass()
57 Seq( 58 Seq(
58 factory.getOWLSubClassOfAxiom(sub, cls), 59 factory.getOWLSubClassOfAxiom(sub, cls),
59 factory.getOWLSubClassOfAxiom(cls, sup) 60 factory.getOWLSubClassOfAxiom(cls, sup)
@@ -74,7 +75,7 @@ class Normalizer() {
74 if (conj.isOWLClass) 75 if (conj.isOWLClass)
75 (acc1 :+ conj, acc2) 76 (acc1 :+ conj, acc2)
76 else { 77 else {
77 val cls = RSAOntology.getFreshOWLClass() 78 val cls = RSAUtil.getFreshOWLClass()
78 ( 79 (
79 acc1 :+ cls, 80 acc1 :+ cls,
80 acc2 :+ factory.getOWLSubClassOfAxiom(conj, cls) 81 acc2 :+ factory.getOWLSubClassOfAxiom(conj, cls)
@@ -133,7 +134,7 @@ class Normalizer() {
133 */ 134 */
134 case (sub: OWLObjectSomeValuesFrom, _) 135 case (sub: OWLObjectSomeValuesFrom, _)
135 if !sub.getFiller.isOWLClass => { 136 if !sub.getFiller.isOWLClass => {
136 val cls = RSAOntology.getFreshOWLClass() 137 val cls = RSAUtil.getFreshOWLClass()
137 Seq( 138 Seq(
138 factory.getOWLSubClassOfAxiom(sub.getFiller, cls), 139 factory.getOWLSubClassOfAxiom(sub.getFiller, cls),
139 factory.getOWLSubClassOfAxiom( 140 factory.getOWLSubClassOfAxiom(
@@ -148,7 +149,7 @@ class Normalizer() {
148 */ 149 */
149 case (_, sup: OWLObjectSomeValuesFrom) 150 case (_, sup: OWLObjectSomeValuesFrom)
150 if !sup.getFiller.isOWLClass => { 151 if !sup.getFiller.isOWLClass => {
151 val cls = RSAOntology.getFreshOWLClass() 152 val cls = RSAUtil.getFreshOWLClass()
152 Seq( 153 Seq(
153 factory.getOWLSubClassOfAxiom(cls, sup.getFiller), 154 factory.getOWLSubClassOfAxiom(cls, sup.getFiller),
154 factory.getOWLSubClassOfAxiom( 155 factory.getOWLSubClassOfAxiom(
@@ -293,7 +294,7 @@ class Normalizer() {
293 ) 294 )
294 case (_, sup: OWLObjectMaxCardinality) 295 case (_, sup: OWLObjectMaxCardinality)
295 if sup.getCardinality == 1 && !sup.getFiller.isOWLClass => { 296 if sup.getCardinality == 1 && !sup.getFiller.isOWLClass => {
296 val cls = RSAOntology.getFreshOWLClass() 297 val cls = RSAUtil.getFreshOWLClass()
297 Seq( 298 Seq(
298 factory.getOWLSubClassOfAxiom(cls, sup.getFiller), 299 factory.getOWLSubClassOfAxiom(cls, sup.getFiller),
299 factory.getOWLSubClassOfAxiom( 300 factory.getOWLSubClassOfAxiom(
@@ -483,7 +484,7 @@ class Normalizer() {
483 * C(a) -> { X(a), X c C } 484 * C(a) -> { X(a), X c C }
484 */ 485 */
485 case a: OWLClassAssertionAxiom if !a.getClassExpression.isOWLClass => { 486 case a: OWLClassAssertionAxiom if !a.getClassExpression.isOWLClass => {
486 val cls = RSAOntology.getFreshOWLClass() 487 val cls = RSAUtil.getFreshOWLClass()
487 Seq( 488 Seq(
488 factory.getOWLClassAssertionAxiom(cls, a.getIndividual), 489 factory.getOWLClassAssertionAxiom(cls, a.getIndividual),
489 factory.getOWLSubClassOfAxiom(cls, a.getClassExpression) 490 factory.getOWLSubClassOfAxiom(cls, a.getClassExpression)
@@ -528,9 +529,9 @@ class Normalizer() {
528 sup: OWLObjectUnionOf 529 sup: OWLObjectUnionOf
529 ): Seq[OWLLogicalAxiom] = { 530 ): Seq[OWLLogicalAxiom] = {
530 val body = 531 val body =
531 sub.asConjunctSet.map((atom) => (atom, RSAOntology.getFreshOWLClass())) 532 sub.asConjunctSet.map((atom) => (atom, RSAUtil.getFreshOWLClass()))
532 val head = 533 val head =
533 sup.asDisjunctSet.map((atom) => (atom, RSAOntology.getFreshOWLClass())) 534 sup.asDisjunctSet.map((atom) => (atom, RSAUtil.getFreshOWLClass()))
534 535
535 /* Update statistics */ 536 /* Update statistics */
536 shifted += 1 537 shifted += 1
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala
index 3fac46f..1adf325 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala
@@ -11,6 +11,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{
11 TupleTableAtom 11 TupleTableAtom
12} 12}
13import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall} 13import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall}
14import uk.ac.ox.cs.rsacomb.RSAUtil
14import uk.ac.ox.cs.rsacomb.RSAOntology 15import uk.ac.ox.cs.rsacomb.RSAOntology
15import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix} 16import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix}
16import uk.ac.ox.cs.rsacomb.util.{RSA, RDFoxUtil} 17import uk.ac.ox.cs.rsacomb.util.{RSA, RDFoxUtil}
@@ -143,14 +144,14 @@ trait RDFoxConverter {
143 } 144 }
144 145
145 case a: OWLSubObjectPropertyOfAxiom => { 146 case a: OWLSubObjectPropertyOfAxiom => {
146 val term1 = RSAOntology.genFreshVariable() 147 val term1 = RSAUtil.genFreshVariable()
147 val body = convert(a.getSubProperty, term, term1, suffix) 148 val body = convert(a.getSubProperty, term, term1, suffix)
148 val head = convert(a.getSuperProperty, term, term1, suffix) 149 val head = convert(a.getSuperProperty, term, term1, suffix)
149 ResultR(List(Rule.create(head, body))) 150 ResultR(List(Rule.create(head, body)))
150 } 151 }
151 152
152 case a: OWLSubDataPropertyOfAxiom => { 153 case a: OWLSubDataPropertyOfAxiom => {
153 val term1 = RSAOntology.genFreshVariable() 154 val term1 = RSAUtil.genFreshVariable()
154 val body = convert(a.getSubProperty, term, term1, suffix) 155 val body = convert(a.getSubProperty, term, term1, suffix)
155 val head = convert(a.getSuperProperty, term, term1, suffix) 156 val head = convert(a.getSuperProperty, term, term1, suffix)
156 ResultR(List(Rule.create(head, body))) 157 ResultR(List(Rule.create(head, body)))
@@ -160,7 +161,7 @@ trait RDFoxConverter {
160 convert(a.asOWLSubClassOfAxiom, term, unsafe, skolem, suffix) 161 convert(a.asOWLSubClassOfAxiom, term, unsafe, skolem, suffix)
161 162
162 case a: OWLObjectPropertyRangeAxiom => { 163 case a: OWLObjectPropertyRangeAxiom => {
163 val term1 = RSAOntology.genFreshVariable() 164 val term1 = RSAUtil.genFreshVariable()
164 val (res, ext) = convert(a.getRange, term, unsafe, skolem, suffix) 165 val (res, ext) = convert(a.getRange, term, unsafe, skolem, suffix)
165 val prop = convert(a.getProperty, term1, term, suffix) 166 val prop = convert(a.getProperty, term1, term, suffix)
166 ResultR(List(Rule.create(res, prop :: ext))) 167 ResultR(List(Rule.create(res, prop :: ext)))
@@ -327,7 +328,7 @@ trait RDFoxConverter {
327 case e: OWLObjectSomeValuesFrom => { 328 case e: OWLObjectSomeValuesFrom => {
328 val cls = e.getFiller() 329 val cls = e.getFiller()
329 val role = e.getProperty() 330 val role = e.getProperty()
330 val varX = RSAOntology.genFreshVariable 331 val varX = RSAUtil.genFreshVariable
331 val (bind, term1) = skolem match { 332 val (bind, term1) = skolem match {
332 case NoSkolem => (None, varX) 333 case NoSkolem => (None, varX)
333 case c: Constant => (None, c.iri) 334 case c: Constant => (None, c.iri)
@@ -354,7 +355,7 @@ trait RDFoxConverter {
354 // Computes the result of rule skolemization. Depending on the used 355 // Computes the result of rule skolemization. Depending on the used
355 // technique it might involve the introduction of additional atoms, 356 // technique it might involve the introduction of additional atoms,
356 // and/or fresh constants and variables. 357 // and/or fresh constants and variables.
357 val varX = RSAOntology.genFreshVariable 358 val varX = RSAUtil.genFreshVariable
358 val (bind, term1) = skolem match { 359 val (bind, term1) = skolem match {
359 case NoSkolem => (None, varX) 360 case NoSkolem => (None, varX)
360 case c: Constant => (None, c.iri) 361 case c: Constant => (None, c.iri)
@@ -379,7 +380,7 @@ trait RDFoxConverter {
379 s"Class expression '$e' has cardinality restriction != 1." 380 s"Class expression '$e' has cardinality restriction != 1."
380 ) 381 )
381 val vars @ (y :: z :: _) = 382 val vars @ (y :: z :: _) =
382 Seq(RSAOntology.genFreshVariable(), RSAOntology.genFreshVariable()) 383 Seq(RSAUtil.genFreshVariable(), RSAUtil.genFreshVariable())
383 val cls = e.getFiller 384 val cls = e.getFiller
384 val role = e.getProperty 385 val role = e.getProperty
385 val (res, ext) = vars.map(convert(cls, _, unsafe, skolem, suffix)).unzip 386 val (res, ext) = vars.map(convert(cls, _, unsafe, skolem, suffix)).unzip
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala
index 9b04f0e..a0d1b5d 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/implicits/RSAAtom.scala
@@ -9,6 +9,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{
9} 9}
10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI} 10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI}
11 11
12import uk.ac.ox.cs.rsacomb.RSAUtil
12import uk.ac.ox.cs.rsacomb.RSAOntology 13import uk.ac.ox.cs.rsacomb.RSAOntology
13import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth} 14import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth}
14import uk.ac.ox.cs.rsacomb.util.RDFoxUtil 15import uk.ac.ox.cs.rsacomb.util.RDFoxUtil
@@ -78,7 +79,7 @@ object RSAAtom {
78 if (isRDF) { 79 if (isRDF) {
79 (None, List(atom)) 80 (None, List(atom))
80 } else { 81 } else {
81 val varS = RSAOntology.genFreshVariable() 82 val varS = RSAUtil.genFreshVariable()
82 val skolem = RDFoxUtil.skolem(name, (args :+ varS): _*) 83 val skolem = RDFoxUtil.skolem(name, (args :+ varS): _*)
83 val atom = TupleTableAtom.rdf(varS, IRI.RDF_TYPE, name) 84 val atom = TupleTableAtom.rdf(varS, IRI.RDF_TYPE, name)
84 val atoms = args.zipWithIndex 85 val atoms = args.zipWithIndex
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
index d7e57dd..7856b3a 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/ontologies/Ontology.scala
@@ -18,27 +18,158 @@ package uk.ac.ox.cs.rsacomb.ontology
18 18
19import java.io.File 19import java.io.File
20 20
21import scala.collection.mutable.Map
22import scala.collection.JavaConverters._
23import scalax.collection.Graph
24import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
25
21import org.semanticweb.owlapi.apibinding.OWLManager 26import org.semanticweb.owlapi.apibinding.OWLManager
22import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} 27import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom}
23import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression} 28import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression}
24import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory 29import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory
30import tech.oxfordsemantic.jrdfox.logic.expression.Resource
25 31
26object Ontology { 32object Ontology {
27 33
28 /** Manager instance to interface with OWLAPI */ 34 /** Type wrapper representing a dependency graph for the ontology.
35 *
36 * The graph is returned along with a map associating each node (IRI
37 * string of the resource), with the corresponding axiom in the
38 * original TBox.
39 */
40 type DependencyGraph = (Graph[Resource, DiEdge], Map[String, OWLAxiom])
41
42 /** Manager instance to interface with OWLAPI
43 *
44 * TODO: turn this into an implicit class parameter.
45 */
29 val manager = OWLManager.createOWLOntologyManager() 46 val manager = OWLManager.createOWLOntologyManager()
30 //val factory = manager.getOWLDataFactory() 47 //val factory = manager.getOWLDataFactory()
31 48
49 /** Compute the RSA dependency graph for a set of axioms
50 *
51 * @param axioms set of input axioms (TBox) to build the dependency
52 * graph.
53 * @param datafiles data (ABox) to build the dependency graph.
54 * @param unsafe list of unsafe roles in the TBox.
55 *
56 * @return a tuple containing the dependency graph and a map between
57 * the newly introduced constants and the corresponding input axioms.
58 *
59 * @note no check on the ontology language is performed since the
60 * construction of the dependency graph is computed regardless. The
61 * input axioms are assumed to be normalized.
62 */
63 def dependencyGraph(
64 axioms: List[OWLLogicalAxiom],
65 datafiles: List[File],
66 unsafe: List[OWLObjectPropertyExpression]
67 ): DependencyGraph = {
68
69 import org.semanticweb.owlapi.model.{
70 OWLClassExpression,
71 OWLObjectSomeValuesFrom,
72 OWLDataSomeValuesFrom
73 }
74 import tech.oxfordsemantic.jrdfox.logic.datalog.Rule
75 import tech.oxfordsemantic.jrdfox.logic.expression.{Term, Variable}
76 import uk.ac.ox.cs.rsacomb.suffix._
77 import uk.ac.ox.cs.rsacomb.converter._
78 import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
79 import uk.ac.ox.cs.rsacomb.RSAUtil
80
81 var nodemap = Map.empty[String, OWLAxiom]
82
83 object RSAConverter extends RDFoxConverter {
84
85 override def convert(
86 expr: OWLClassExpression,
87 term: Term,
88 unsafe: List[OWLObjectPropertyExpression],
89 skolem: SkolemStrategy,
90 suffix: RSASuffix
91 ): Shards =
92 (expr, skolem) match {
93
94 case (e: OWLObjectSomeValuesFrom, c: Constant) => {
95 nodemap.update(c.iri.getIRI, c.axiom)
96 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
97 if (unsafe contains e.getProperty)
98 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
99 else
100 (RSA.PE(term, c.iri) :: res, ext)
101 }
102
103 case (e: OWLDataSomeValuesFrom, c: Constant) => {
104 nodemap.update(c.iri.getIRI, c.axiom)
105 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
106 if (unsafe contains e.getProperty)
107 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
108 else
109 (RSA.PE(term, c.iri) :: res, ext)
110 }
111
112 case _ => super.convert(expr, term, unsafe, skolem, suffix)
113 }
114 }
115
116 /* Ontology convertion into LP rules */
117 val term = RSAUtil.genFreshVariable()
118 val result = axioms.map(a =>
119 RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)
120 )
121
122 val datalog = result.unzip
123 val facts = datalog._1.flatten
124 var rules = datalog._2.flatten
125
126 /* Open connection with RDFox */
127 val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph")
128
129 /* Add additional built-in rules */
130 val varX = Variable.create("X")
131 val varY = Variable.create("Y")
132 rules = Rule.create(
133 RSA.E(varX, varY),
134 RSA.PE(varX, varY),
135 RSA.U(varX),
136 RSA.U(varY)
137 ) :: rules
138 /* Load facts and rules from ontology */
139 RDFoxUtil.addFacts(data, facts)
140 RDFoxUtil.addRules(data, rules)
141 /* Load data files */
142 RDFoxUtil.addData(data, datafiles: _*)
143
144 /* Build the graph */
145 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }"
146 val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get
147 var edges: Seq[DiEdge[Resource]] =
148 answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 }
149 val graph = Graph(edges: _*)
150
151 /* Close connection to RDFox */
152 RDFoxUtil.closeConnection(server, data)
153
154 (graph, nodemap)
155 }
32} 156}
33 157
34/** A wrapper for 158/** A wrapper for a generic OWL2 ontology
35 */ 159 */
36class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) { 160class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) {
37 161
38 /** Extend OWLAxiom functionalities */ 162 /** Extend OWLAxiom functionalities */
39 import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ 163 import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._
40 164
41 /** OWLOntology based on input axioms */ 165 /** Simplify conversion between Java and Scala collections */
166 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
167
168 /** OWLOntology based on input axioms
169 *
170 * This is mainly used to instantiate a new reasoner to be used in
171 * the computation of unsafe roles.
172 */
42 private val ontology: OWLOntology = 173 private val ontology: OWLOntology =
43 Ontology.manager.createOntology((axioms: List[OWLAxiom]).asJava) 174 Ontology.manager.createOntology((axioms: List[OWLAxiom]).asJava)
44 175
@@ -87,4 +218,8 @@ class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) {
87 218
88 unsafe1 ++ unsafe2 219 unsafe1 ++ unsafe2
89 } 220 }
221
222 lazy val dependencyGraph: Ontology.DependencyGraph =
223 Ontology.dependencyGraph(axioms, datafiles, this.unsafe)
224
90} 225}