aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/rsacomb/RSAOntology.scala
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-06 17:18:07 +0000
committerFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-06 17:18:07 +0000
commitb721703c349cebd3ffe888d9644f2b85d5a8eeb7 (patch)
treea3845fbf44827ff66fc956c90fe20c1d4be73115 /src/main/scala/rsacomb/RSAOntology.scala
parentc6babfd508f65e8b7596a96659214cb43881dadd (diff)
downloadRSAComb-b721703c349cebd3ffe888d9644f2b85d5a8eeb7.tar.gz
RSAComb-b721703c349cebd3ffe888d9644f2b85d5a8eeb7.zip
Rework canonical model computation
This is a first attempt to avoid a bug triggered by the nature of the class RSAOntology and CanonicalModel. An OWLOntology is converted implicitly to an RSAOntology object whenever it is needed. From within the RSAOntology class we used to create a CanonicalModel object (and pass the underling OWLOntology object as a parameter). Inside CanonicalModel we require RSAOntology functionalities from the OWLOntology, triggering a new conversion into RSAOntology (that would compute a new CanonicalModel and so on in a loop). While declaring the CanonicalModel as lazy in RSAOntology could solve the problem, it does not fix the underlying issue of having a class strictly related to RSAOntology as a top level class. As a first attempt we moved CanonicalModel as an object inside RSAOntology.
Diffstat (limited to 'src/main/scala/rsacomb/RSAOntology.scala')
-rw-r--r--src/main/scala/rsacomb/RSAOntology.scala331
1 files changed, 276 insertions, 55 deletions
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala
index ef1885b..60008a2 100644
--- a/src/main/scala/rsacomb/RSAOntology.scala
+++ b/src/main/scala/rsacomb/RSAOntology.scala
@@ -4,11 +4,13 @@ package rsacomb
4import java.util.HashMap 4import java.util.HashMap
5import java.util.stream.{Collectors, Stream} 5import java.util.stream.{Collectors, Stream}
6 6
7import org.semanticweb.owlapi.model.OWLOntology 7import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom}
8import org.semanticweb.owlapi.model.{ 8import org.semanticweb.owlapi.model.{
9 OWLClass, 9 OWLClass,
10 OWLObjectProperty, 10 OWLObjectProperty,
11 OWLSubObjectPropertyOfAxiom,
11 OWLObjectPropertyExpression, 12 OWLObjectPropertyExpression,
13 OWLObjectSomeValuesFrom,
12 OWLSubClassOfAxiom 14 OWLSubClassOfAxiom
13} 15}
14import org.semanticweb.owlapi.model.parameters.Imports 16import org.semanticweb.owlapi.model.parameters.Imports
@@ -45,24 +47,37 @@ trait RSAOntology {
45 */ 47 */
46 implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom { 48 implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom {
47 49
50 // Gather TBox+RBox from original ontology
51 lazy val tbox: List[OWLAxiom] =
52 ontology
53 .tboxAxioms(Imports.INCLUDED)
54 .collect(Collectors.toList())
55 .asScala
56 .toList
57
58 lazy val rbox: List[OWLAxiom] =
59 ontology
60 .rboxAxioms(Imports.INCLUDED)
61 .collect(Collectors.toList())
62 .asScala
63 .toList
64
65 lazy val axioms: List[OWLAxiom] = tbox ++ rbox
66
48 /* Retrieve individuals in the original ontology 67 /* Retrieve individuals in the original ontology
49 */ 68 */
50 lazy val individuals: List[IRI] = { 69 lazy val individuals: List[IRI] =
51 ontology 70 ontology
52 .getIndividualsInSignature() 71 .getIndividualsInSignature()
53 .asScala 72 .asScala
54 .map(_.getIRI) 73 .map(_.getIRI)
55 .map(RDFoxUtil.owlapi2rdfox) 74 .map(RDFoxUtil.owlapi2rdfox)
56 .toList 75 .toList
57 }
58 76
59 // private val roles: Set[OWLObjectPropertyExpression] = { 77 lazy val roles: List[OWLObjectPropertyExpression] =
60 // ontology 78 axioms
61 // .rboxAxioms(Imports.INCLUDED) 79 .flatMap(_.objectPropertyExpressionsInSignature)
62 // .collect(Collectors.toSet()) 80 .distinct
63 // .asScala
64 // .flatMap(_.objectPropertyExpressionsInSignature)
65 // }
66 81
67 // OWLAPI reasoner for same easier tasks 82 // OWLAPI reasoner for same easier tasks
68 private val reasoner = 83 private val reasoner =
@@ -79,13 +94,6 @@ trait RSAOntology {
79 */ 94 */
80 lazy val isRSA: Boolean = { 95 lazy val isRSA: Boolean = {
81 96
82 val tbox = ontology.tboxAxioms(Imports.INCLUDED)
83 val rbox = ontology.rboxAxioms(Imports.INCLUDED)
84 val axioms =
85 Stream
86 .concat(tbox, rbox)
87 .collect(Collectors.toList())
88 .asScala
89 val unsafe = this.unsafeRoles 97 val unsafe = this.unsafeRoles
90 98
91 /* DEBUG: print rules in DL syntax and unsafe roles */ 99 /* DEBUG: print rules in DL syntax and unsafe roles */
@@ -154,11 +162,6 @@ trait RSAOntology {
154 162
155 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { 163 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = {
156 164
157 val tbox = ontology
158 .tboxAxioms(Imports.INCLUDED)
159 .collect(Collectors.toSet())
160 .asScala
161
162 /* DEBUG: print rules in DL syntax */ 165 /* DEBUG: print rules in DL syntax */
163 //val renderer = new DLSyntaxObjectRenderer() 166 //val renderer = new DLSyntaxObjectRenderer()
164 167
@@ -210,37 +213,6 @@ trait RSAOntology {
210 (unsafe1 ++ unsafe2).toList 213 (unsafe1 ++ unsafe2).toList
211 } 214 }
212 215
213 lazy val canonicalModel: List[Rule] = {
214 // Compute program to generate canonical model
215 val tbox =
216 ontology
217 .tboxAxioms(Imports.INCLUDED)
218 .collect(Collectors.toList())
219 .asScala
220 .toList
221 val rbox =
222 ontology
223 .rboxAxioms(Imports.INCLUDED)
224 .collect(Collectors.toList())
225 .asScala
226 .toList
227 val axioms = tbox ++ rbox
228 val varX = Variable.create("X")
229 val visitor = ProgramGenerator(ontology, varX)
230 val facts = ProgramGenerator.NIs(individuals)
231 val rules1 = ProgramGenerator.generateRoleRules(
232 axioms
233 .flatMap(
234 _.objectPropertiesInSignature.collect(Collectors.toSet()).asScala
235 )
236 .toSet
237 )
238 val rules2 = axioms.flatMap(_.accept(visitor))
239
240 rules1 ++ rules2
241 // Call RDFox to generate the canonical model
242 }
243
244 private def rsaGraph( 216 private def rsaGraph(
245 data: DataStoreConnection 217 data: DataStoreConnection
246 ): Graph[Resource, UnDiEdge] = { 218 ): Graph[Resource, UnDiEdge] = {
@@ -256,8 +228,8 @@ trait RSAOntology {
256 Graph(edges: _*) 228 Graph(edges: _*)
257 } 229 }
258 230
259 def filteringProgram(query: SelectQuery): List[Rule] = 231 def filteringProgram(query: SelectQuery): FilteringProgram =
260 FilteringProgram(query, individuals).rules 232 FilteringProgram(query, individuals)
261 233
262 // TODO: the following functions needs testing 234 // TODO: the following functions needs testing
263 def confl( 235 def confl(
@@ -366,6 +338,255 @@ trait RSAOntology {
366 def unfold(axiom: OWLSubClassOfAxiom): Set[Term] = 338 def unfold(axiom: OWLSubClassOfAxiom): Set[Term] =
367 this.self(axiom) | this.cycle(axiom) 339 this.self(axiom) | this.cycle(axiom)
368 340
341 object canonicalModel {
342
343 import RDFoxUtil._
344
345 val NIs: List[Rule] =
346 individuals.map(a =>
347 Rule.create(TupleTableAtom.rdf(a, IRI.RDF_TYPE, RSA.internal("NI")))
348 )
349
350 val rolesAdditionalRules: List[Rule] = {
351 // Given a role (predicate) compute additional logic rules
352 def additional(pred: String): Seq[Rule] = {
353 val varX = Variable.create("X")
354 val varY = Variable.create("Y")
355 List(
356 Rule.create(
357 TupleTableAtom.rdf(varX, IRI.create(pred), varY),
358 TupleTableAtom
359 .rdf(
360 varX,
361 IRI.create(pred ++ RSASuffix.Forward.getSuffix),
362 varY
363 )
364 ),
365 Rule.create(
366 TupleTableAtom.rdf(varX, IRI.create(pred), varY),
367 TupleTableAtom
368 .rdf(
369 varX,
370 IRI.create(pred ++ RSASuffix.Backward.getSuffix),
371 varY
372 )
373 ),
374 Rule.create(
375 TupleTableAtom.rdf(
376 varY,
377 IRI.create(pred ++ RSASuffix.Backward.getSuffix ++ "_inv"),
378 varX
379 ),
380 TupleTableAtom
381 .rdf(
382 varX,
383 IRI.create(pred ++ RSASuffix.Forward.getSuffix),
384 varY
385 )
386 ),
387 Rule.create(
388 TupleTableAtom.rdf(
389 varY,
390 IRI.create(pred ++ RSASuffix.Forward.getSuffix ++ "_inv"),
391 varX
392 ),
393 TupleTableAtom.rdf(
394 varX,
395 IRI.create(pred ++ RSASuffix.Backward.getSuffix),
396 varY
397 )
398 )
399 )
400 }
401 // Compute additional rules per role
402 axioms
403 .flatMap(
404 _.objectPropertiesInSignature.collect(Collectors.toSet()).asScala
405 )
406 .distinct
407 .map(_.getIRI.getIRIString)
408 .flatMap(additional)
409 }
410
411 val rules: List[Rule] = {
412 // Compute rules from ontology axioms
413 val rules = axioms.flatMap(_.accept(this.ProgramGenerator))
414 // Return full set of rules
415 rules ++ rolesAdditionalRules ++ NIs
416 }
417
418 object ProgramGenerator
419 extends RDFoxAxiomConverter(
420 Variable.create("X"),
421 unsafeRoles,
422 SkolemStrategy.None,
423 RSASuffix.None
424 ) {
425
426 private def rules1(axiom: OWLSubClassOfAxiom): List[Rule] = {
427 val unfold = ontology.unfold(axiom).toList
428 // Fresh Variables
429 val v0 = IRI.create("v0_" ++ axiom.hashCode.toString)
430 val varX = Variable.create("X")
431 // Predicates
432 val atomA: TupleTableAtom = {
433 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
434 TupleTableAtom.rdf(varX, IRI.RDF_TYPE, cls)
435 }
436 def notIn(t: Term): TupleTableAtom = {
437 TupleTableAtom.rdf(
438 t,
439 RSA.internal("notIn"),
440 RSA.internal(unfold.hashCode.toString)
441 )
442 }
443 val roleRf: TupleTableAtom = {
444 val visitor =
445 new RDFoxPropertyExprConverter(varX, v0, RSASuffix.Forward)
446 axiom.getSuperClass
447 .asInstanceOf[OWLObjectSomeValuesFrom]
448 .getProperty
449 .accept(visitor)
450 .head
451 }
452 val atomB: TupleTableAtom = {
453 val cls = axiom.getSuperClass
454 .asInstanceOf[OWLObjectSomeValuesFrom]
455 .getFiller
456 .asInstanceOf[OWLClass]
457 .getIRI
458 TupleTableAtom.rdf(v0, IRI.RDF_TYPE, cls)
459 }
460 // TODO: To be consistent with the specifics of the visitor we are
461 // returning facts as `Rule`s with true body. While this is correct
462 // there is an easier way to import facts into RDFox. Are we able to
463 // do that?
464 val facts = unfold.map(x => Rule.create(notIn(x)))
465 val rules = List(
466 Rule.create(roleRf, atomA, notIn(varX)),
467 Rule.create(atomB, atomA, notIn(varX))
468 )
469 facts ++ rules
470 }
471
472 private def rules2(axiom: OWLSubClassOfAxiom): List[Rule] = {
473 val roleR =
474 axiom.getSuperClass
475 .asInstanceOf[OWLObjectSomeValuesFrom]
476 .getProperty
477 if (ontology.confl(roleR) contains roleR) {
478 // Fresh Variables
479 val v0 = IRI.create("v0_" ++ axiom.hashCode.toString)
480 val v1 = IRI.create("v1_" ++ axiom.hashCode.toString)
481 val v2 = IRI.create("v2_" ++ axiom.hashCode.toString)
482 // Predicates
483 def atomA(t: Term): TupleTableAtom = {
484 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
485 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
486 }
487 def roleRf(t1: Term, t2: Term): TupleTableAtom = {
488 val visitor =
489 new RDFoxPropertyExprConverter(t1, t2, RSASuffix.Forward)
490 roleR.accept(visitor).head
491 }
492 def atomB(t: Term): TupleTableAtom = {
493 val cls = axiom.getSuperClass
494 .asInstanceOf[OWLObjectSomeValuesFrom]
495 .getFiller
496 .asInstanceOf[OWLClass]
497 .getIRI
498 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
499 }
500 //Rules
501 List(
502 Rule.create(roleRf(v0, v1), atomA(v0)),
503 Rule.create(atomB(v1), atomA(v0)),
504 Rule.create(roleRf(v1, v2), atomA(v1)),
505 Rule.create(atomB(v2), atomA(v1))
506 )
507 } else {
508 List()
509 }
510 }
511
512 private def rules3(axiom: OWLSubClassOfAxiom): List[Rule] = {
513 val cycle = ontology.cycle(axiom).toList
514 val roleR =
515 axiom.getSuperClass
516 .asInstanceOf[OWLObjectSomeValuesFrom]
517 .getProperty
518 // Fresh Variables
519 val v1 = IRI.create("v1_" ++ axiom.hashCode.toString)
520 // Predicates
521 def atomA(t: Term): TupleTableAtom = {
522 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
523 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
524 }
525 def roleRf(t: Term): TupleTableAtom = {
526 val visitor =
527 new RDFoxPropertyExprConverter(t, v1, RSASuffix.Forward)
528 roleR.accept(visitor).head
529 }
530 val atomB: TupleTableAtom = {
531 val cls = axiom.getSuperClass
532 .asInstanceOf[OWLObjectSomeValuesFrom]
533 .getFiller
534 .asInstanceOf[OWLClass]
535 .getIRI
536 TupleTableAtom.rdf(v1, IRI.RDF_TYPE, cls)
537 }
538 cycle.flatMap { x =>
539 List(
540 Rule.create(roleRf(x), atomA(x)),
541 Rule.create(atomB, atomA(x))
542 )
543 }
544 }
545
546 override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = {
547 if (axiom.isT5) {
548 // TODO: get role in T5 axiom
549 // Assuming one role here
550 val role = axiom.objectPropertyExpressionsInSignature(0)
551 if (ontology.unsafeRoles.contains(role)) {
552 val visitor =
553 new RDFoxAxiomConverter(
554 Variable.create("X"),
555 ontology.unsafeRoles,
556 SkolemStrategy.Standard(axiom.toString),
557 RSASuffix.Forward
558 )
559 axiom.accept(visitor)
560 } else {
561 rules1(axiom) ++ rules2(axiom) ++ rules3(axiom)
562 }
563 } else {
564 // Fallback to standard OWL to LP translation
565 super.visit(axiom)
566 }
567 }
568
569 override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = {
570 val varX = Variable.create("X")
571 val varY = Variable.create("Y")
572 val visitorF = new RDFoxAxiomConverter(
573 Variable.create("X"),
574 ontology.unsafeRoles,
575 SkolemStrategy.None,
576 RSASuffix.Forward
577 )
578 val visitorB = new RDFoxAxiomConverter(
579 Variable.create("X"),
580 ontology.unsafeRoles,
581 SkolemStrategy.None,
582 RSASuffix.Backward
583 )
584 axiom.accept(visitorB) ++ axiom.accept(visitorF)
585 }
586
587 }
588
589 }
369 } // implicit class RSAOntology 590 } // implicit class RSAOntology
370 591
371} // trait RSAOntology 592} // trait RSAOntology