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 | } |
