diff options
author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2021-04-05 13:14:00 +0100 |
---|---|---|
committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2021-04-05 13:14:00 +0100 |
commit | b622e151905d9a3b8fff14748cc840696dc85f16 (patch) | |
tree | bed37dc1243d7f6926560ff7fed0a10d943a875a | |
parent | 4879515113b2bc107ee0e35ad164de42f6c0d059 (diff) | |
download | RSAComb-b622e151905d9a3b8fff14748cc840696dc85f16.tar.gz RSAComb-b622e151905d9a3b8fff14748cc840696dc85f16.zip |
Add shifting for disjunction in the rhs of an axiom
-rw-r--r-- | src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala | 58 | ||||
-rw-r--r-- | src/test/scala/uk/ac/ox/cs/rsacomb/converter/NormalizerSpec.scala | 21 |
2 files changed, 77 insertions, 2 deletions
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 a771e3d..fe81312 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 | |||
@@ -31,6 +31,10 @@ class Normalizer() { | |||
31 | factory.getOWLClass(s"X$counter") | 31 | factory.getOWLClass(s"X$counter") |
32 | } | 32 | } |
33 | 33 | ||
34 | /** Statistics */ | ||
35 | private var discarted = 0 | ||
36 | private var shifted = 0 | ||
37 | |||
34 | /** Normalizes a | 38 | /** Normalizes a |
35 | * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]] | 39 | * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]] |
36 | * | 40 | * |
@@ -122,8 +126,12 @@ class Normalizer() { | |||
122 | ) | 126 | ) |
123 | } | 127 | } |
124 | } | 128 | } |
125 | /** Disjunction on the rhs is not supported */ | 129 | /** Disjunction on the rhs is not supported directly |
126 | case (_, sup: OWLObjectUnionOf) => notInHornALCHOIQ(a) | 130 | * |
131 | * Instead we `shift` the rule to eliminate the disjunction. | ||
132 | */ | ||
133 | case (_, sup: OWLObjectUnionOf) => | ||
134 | shift(sub, sup) flatMap normalize | ||
127 | /** Complex class expression on existential restriction on the lhs | 135 | /** Complex class expression on existential restriction on the lhs |
128 | * | 136 | * |
129 | * exists R . C c D -> { C c X, exists R . X c D } | 137 | * exists R . C c D -> { C c X, exists R . X c D } |
@@ -515,6 +523,50 @@ class Normalizer() { | |||
515 | case a => Seq(a) | 523 | case a => Seq(a) |
516 | } | 524 | } |
517 | 525 | ||
526 | /** Shift an axiom with disjunction on the rhs */ | ||
527 | private def shift( | ||
528 | sub: OWLClassExpression, | ||
529 | sup: OWLObjectUnionOf | ||
530 | ): Seq[OWLLogicalAxiom] = { | ||
531 | val body = sub.asConjunctSet.map((atom) => (atom, freshOWLClass())) | ||
532 | val head = sup.asDisjunctSet.map((atom) => (atom, freshOWLClass())) | ||
533 | |||
534 | /* Update statistics */ | ||
535 | shifted += 1 | ||
536 | |||
537 | val r1 = | ||
538 | factory.getOWLSubClassOfAxiom( | ||
539 | factory.getOWLObjectIntersectionOf( | ||
540 | (body.map(_._1) ++ head.map(_._2)): _* | ||
541 | ), | ||
542 | factory.getOWLNothing | ||
543 | ) | ||
544 | |||
545 | val r2s = | ||
546 | for { | ||
547 | (a, na) <- head | ||
548 | hs = head.map(_._2).filterNot(_ equals na) | ||
549 | } yield factory.getOWLSubClassOfAxiom( | ||
550 | factory.getOWLObjectIntersectionOf( | ||
551 | (body.map(_._1) ++ hs): _* | ||
552 | ), | ||
553 | a | ||
554 | ) | ||
555 | |||
556 | val r3s = | ||
557 | for { | ||
558 | (a, na) <- body | ||
559 | bs = body.map(_._1).filterNot(_ equals a) | ||
560 | } yield factory.getOWLSubClassOfAxiom( | ||
561 | factory.getOWLObjectIntersectionOf( | ||
562 | (bs ++ head.map(_._2)): _* | ||
563 | ), | ||
564 | na | ||
565 | ) | ||
566 | |||
567 | Seq(r1) ++ r2s ++ r3s | ||
568 | } | ||
569 | |||
518 | /** Approximation function for axioms out of Horn-ALCHOIQ | 570 | /** Approximation function for axioms out of Horn-ALCHOIQ |
519 | * | 571 | * |
520 | * By default discards the axiom, which guarantees a lower bound | 572 | * By default discards the axiom, which guarantees a lower bound |
@@ -523,6 +575,8 @@ class Normalizer() { | |||
523 | protected def notInHornALCHOIQ( | 575 | protected def notInHornALCHOIQ( |
524 | axiom: OWLLogicalAxiom | 576 | axiom: OWLLogicalAxiom |
525 | ): Seq[OWLLogicalAxiom] = { | 577 | ): Seq[OWLLogicalAxiom] = { |
578 | /* Update statistics */ | ||
579 | discarted += 1 | ||
526 | Logger print s"'$axiom' has been ignored because it is not in Horn-ALCHOIQ" | 580 | Logger print s"'$axiom' has been ignored because it is not in Horn-ALCHOIQ" |
527 | Seq() | 581 | Seq() |
528 | } | 582 | } |
diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/converter/NormalizerSpec.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/converter/NormalizerSpec.scala index 3a686c0..7f0f720 100644 --- a/src/test/scala/uk/ac/ox/cs/rsacomb/converter/NormalizerSpec.scala +++ b/src/test/scala/uk/ac/ox/cs/rsacomb/converter/NormalizerSpec.scala | |||
@@ -73,6 +73,27 @@ class NormalizerSpec extends AnyFlatSpec with Matchers with LoneElement { | |||
73 | ).flatMap(normalizer.normalize) | 73 | ).flatMap(normalizer.normalize) |
74 | } | 74 | } |
75 | 75 | ||
76 | "Disjunction on the rhs" should "be shifted" in { | ||
77 | def cls(n: Int) = factory.getOWLClass(s"_:class$n") | ||
78 | val axiom1 = | ||
79 | factory.getOWLSubClassOfAxiom( | ||
80 | factory.getOWLObjectIntersectionOf(cls(1), cls(2), cls(3)), | ||
81 | factory.getOWLObjectUnionOf(cls(4), cls(5)) | ||
82 | ) | ||
83 | val axiom2 = | ||
84 | factory.getOWLSubClassOfAxiom( | ||
85 | cls(1), | ||
86 | factory.getOWLObjectUnionOf(cls(2), cls(3), cls(4)) | ||
87 | ) | ||
88 | val axiom3 = | ||
89 | factory.getOWLSubClassOfAxiom( | ||
90 | factory.getOWLObjectIntersectionOf(cls(1), cls(2), cls(3)), | ||
91 | factory.getOWLObjectUnionOf(cls(4)) | ||
92 | ) | ||
93 | normalizer.normalize(axiom1) should have length 5 | ||
94 | normalizer.normalize(axiom2) should have length 5 | ||
95 | normalizer.normalize(axiom3) should have length 4 | ||
96 | } | ||
76 | //"A class name" should "be converted into a single atom" in { | 97 | //"A class name" should "be converted into a single atom" in { |
77 | // val cls = factory.getOWLClass(iriString0) | 98 | // val cls = factory.getOWLClass(iriString0) |
78 | // val atom = TupleTableAtom.rdf(term0, IRI.RDF_TYPE, IRI.create(iriString0)) | 99 | // val atom = TupleTableAtom.rdf(term0, IRI.RDF_TYPE, IRI.create(iriString0)) |