diff options
author | Federico Igne <git@federicoigne.com> | 2021-07-20 15:09:20 +0100 |
---|---|---|
committer | Federico Igne <git@federicoigne.com> | 2021-07-20 15:14:18 +0100 |
commit | 53646646f924887768688794aee46874ed194673 (patch) | |
tree | 50e468ed4ad50bcfb23355e66c0def2e3997fde4 | |
parent | f0d1bfe564853a63128ad139520c9838778a7b61 (diff) | |
download | RSAComb-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.
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 */ |
104 | object RSAComb extends App { | 104 | object 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 | |||
165 | object 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 | |||
91 | object 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 | ||
6 | import uk.ac.ox.cs.rsacomb.util.Logger | 6 | import uk.ac.ox.cs.rsacomb.util.Logger |
7 | import uk.ac.ox.cs.rsacomb.RSAOntology | 7 | import uk.ac.ox.cs.rsacomb.RSAOntology |
8 | import uk.ac.ox.cs.rsacomb.RSAUtil | ||
8 | 9 | ||
9 | object Normalizer { | 10 | object 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 | } |
13 | import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall} | 13 | import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, FunctionCall} |
14 | import uk.ac.ox.cs.rsacomb.RSAUtil | ||
14 | import uk.ac.ox.cs.rsacomb.RSAOntology | 15 | import uk.ac.ox.cs.rsacomb.RSAOntology |
15 | import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix} | 16 | import uk.ac.ox.cs.rsacomb.suffix.{Empty, Inverse, RSASuffix} |
16 | import uk.ac.ox.cs.rsacomb.util.{RSA, RDFoxUtil} | 17 | import 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 | } |
10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI} | 10 | import tech.oxfordsemantic.jrdfox.logic.expression.{IRI} |
11 | 11 | ||
12 | import uk.ac.ox.cs.rsacomb.RSAUtil | ||
12 | import uk.ac.ox.cs.rsacomb.RSAOntology | 13 | import uk.ac.ox.cs.rsacomb.RSAOntology |
13 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth} | 14 | import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Nth} |
14 | import uk.ac.ox.cs.rsacomb.util.RDFoxUtil | 15 | import 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 | ||
19 | import java.io.File | 19 | import java.io.File |
20 | 20 | ||
21 | import scala.collection.mutable.Map | ||
22 | import scala.collection.JavaConverters._ | ||
23 | import scalax.collection.Graph | ||
24 | import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ | ||
25 | |||
21 | import org.semanticweb.owlapi.apibinding.OWLManager | 26 | import org.semanticweb.owlapi.apibinding.OWLManager |
22 | import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} | 27 | import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} |
23 | import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression} | 28 | import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression} |
24 | import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory | 29 | import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory |
30 | import tech.oxfordsemantic.jrdfox.logic.expression.Resource | ||
25 | 31 | ||
26 | object Ontology { | 32 | object 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 | */ |
36 | class Ontology(val axioms: List[OWLLogicalAxiom], val datafiles: List[File]) { | 160 | class 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 | } |