aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2021-02-04 10:38:33 +0000
committerFederico Igne <federico.igne@cs.ox.ac.uk>2021-02-04 10:38:33 +0000
commitb9fe66ed1b48d21f3fe6cb960c8fbe8f22f4a39c (patch)
tree3c696ab6e635383d5277114a591a0d9a02bdecd8 /src/main/scala
parent26f01c74b83cf79f5af425692421380dd3eb6bea (diff)
downloadRSAComb-b9fe66ed1b48d21f3fe6cb960c8fbe8f22f4a39c.tar.gz
RSAComb-b9fe66ed1b48d21f3fe6cb960c8fbe8f22f4a39c.zip
Add ontology normalizer
This also allows to define ontology approximations to RSA in a simple way.
Diffstat (limited to 'src/main/scala')
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala4
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala7
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala532
3 files changed, 541 insertions, 2 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 3777c6b..3da6c8a 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala
@@ -79,7 +79,9 @@ class CanonicalModel(val ontology: RSAOntology) {
79 val term = RSAOntology.genFreshVariable() 79 val term = RSAOntology.genFreshVariable()
80 val unsafe = ontology.unsafeRoles 80 val unsafe = ontology.unsafeRoles
81 ontology.axioms 81 ontology.axioms
82 .map(CanonicalModelConverter.convert(_, term, unsafe, NoSkolem, Empty)) 82 .map(a =>
83 CanonicalModelConverter.convert(a, term, unsafe, Constant(a), Empty)
84 )
83 .unzip 85 .unzip
84 } 86 }
85 ( 87 (
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 0f1552a..42a5b87 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
@@ -99,6 +99,8 @@ class RSAOntology(_ontology: File, val datafiles: File*) {
99 private val reasoner = 99 private val reasoner =
100 (new StructuralReasonerFactory()).createReasoner(ontology) 100 (new StructuralReasonerFactory()).createReasoner(ontology)
101 101
102 private val normalizer = new Normalizer()
103
102 /** Imported knowledge base. */ 104 /** Imported knowledge base. */
103 //lazy val kbase: OWLOntology = { 105 //lazy val kbase: OWLOntology = {
104 // val merger = new OWLOntologyMerger(manager) 106 // val merger = new OWLOntologyMerger(manager)
@@ -112,6 +114,7 @@ class RSAOntology(_ontology: File, val datafiles: File*) {
112 .tboxAxioms(Imports.INCLUDED) 114 .tboxAxioms(Imports.INCLUDED)
113 .collect(Collectors.toList()) 115 .collect(Collectors.toList())
114 .collect { case a: OWLLogicalAxiom => a } 116 .collect { case a: OWLLogicalAxiom => a }
117 .flatMap(normalizer.normalize)
115 Logger.print(s"Original TBox: ${tbox.length}", Logger.DEBUG) 118 Logger.print(s"Original TBox: ${tbox.length}", Logger.DEBUG)
116 119
117 /** RBox axioms */ 120 /** RBox axioms */
@@ -120,6 +123,7 @@ class RSAOntology(_ontology: File, val datafiles: File*) {
120 .rboxAxioms(Imports.INCLUDED) 123 .rboxAxioms(Imports.INCLUDED)
121 .collect(Collectors.toList()) 124 .collect(Collectors.toList())
122 .collect { case a: OWLLogicalAxiom => a } 125 .collect { case a: OWLLogicalAxiom => a }
126 .flatMap(normalizer.normalize)
123 Logger.print(s"Original RBox: ${rbox.length}", Logger.DEBUG) 127 Logger.print(s"Original RBox: ${rbox.length}", Logger.DEBUG)
124 128
125 /** ABox axioms 129 /** ABox axioms
@@ -134,7 +138,8 @@ class RSAOntology(_ontology: File, val datafiles: File*) {
134 .aboxAxioms(Imports.INCLUDED) 138 .aboxAxioms(Imports.INCLUDED)
135 .collect(Collectors.toList()) 139 .collect(Collectors.toList())
136 .collect { case a: OWLLogicalAxiom => a } 140 .collect { case a: OWLLogicalAxiom => a }
137 Logger.print(s"Original RBox: ${abox.length}", Logger.DEBUG) 141 .flatMap(normalizer.normalize)
142 Logger.print(s"Original ABox: ${abox.length}", Logger.DEBUG)
138 143
139 /** Collection of logical axioms in the input ontology */ 144 /** Collection of logical axioms in the input ontology */
140 lazy val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox 145 lazy val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox
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
new file mode 100644
index 0000000..3c4dd5c
--- /dev/null
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/Normalizer.scala
@@ -0,0 +1,532 @@
1package uk.ac.ox.cs.rsacomb.converter
2
3import org.semanticweb.owlapi.apibinding.OWLManager
4import org.semanticweb.owlapi.model._
5
6import uk.ac.ox.cs.rsacomb.util.Logger
7
8object Normalizer {
9
10 /** Factory used for converting expressions when needed.
11 *
12 * @note most of the time this is used to perform some kind of
13 * normalization on axioms. In later versions it might be worth
14 * moving the normalization code in its own independent step.
15 */
16 private val manager = OWLManager.createOWLOntologyManager()
17 val factory = manager.getOWLDataFactory()
18
19}
20
21class Normalizer() {
22
23 import Normalizer._
24
25 /** Simplify conversion between Java and Scala collections */
26 import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._
27
28 private var counter = -1
29 def freshOWLClass(): OWLClass = factory.getOWLClass(s"X${counter += 1}")
30
31 /** Normalizes a
32 * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]]
33 *
34 * @note not all possible axioms are supported. Following is a list
35 * of all unhandled class expressions:
36 * - [[org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom OWLAsymmetricObjectPropertyAxiom]]
37 * - [[org.semanticweb.owlapi.model.OWLDatatypeDefinitionAxiom OWLDatatypeDefinitionAxiom]]
38 * - [[org.semanticweb.owlapi.model.OWLDisjointDataPropertiesAxiom OWLDisjointDataPropertiesAxiom]]
39 * - [[org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom OWLDisjointObjectPropertiesAxiom]]
40 * - [[org.semanticweb.owlapi.model.OWLHasKeyAxiom OWLHasKeyAxiom]]
41 * - [[org.semanticweb.owlapi.model.SWRLRule SWRLRule]]
42 */
43 def normalize(axiom: OWLLogicalAxiom): Seq[OWLLogicalAxiom] =
44 axiom match {
45 case a: OWLSubClassOfAxiom => {
46 val sub = a.getSubClass.getNNF
47 val sup = a.getSuperClass.getNNF
48 (sub, sup) match {
49 /** Split complex subclass axioms
50 *
51 * C c D -> { C c X, X c D }
52 */
53 case _ if !sub.isOWLClass && !sup.isOWLClass => {
54 val cls = freshOWLClass()
55 Seq(
56 factory.getOWLSubClassOfAxiom(sub, cls),
57 factory.getOWLSubClassOfAxiom(cls, sup)
58 ).flatMap(normalize)
59 }
60 /** Conjunction on the lhs
61 *
62 * A1 n ... n C n ... n An c D -> { C c X, A1 n ... n X n ... n An c D }
63 */
64 case (sub: OWLObjectIntersectionOf, _)
65 if sub.asConjunctSet.exists(c => !c.isOWLClass) => {
66 var additional = Seq()
67 val conjuncts = sub.asConjunctSet
68 if (conjuncts.length > 0) {
69 val acc = (Seq[OWLClassExpression](), Seq[OWLLogicalAxiom]())
70 val (acc1, acc2) = conjuncts.foldLeft(acc)(
71 { case ((acc1, acc2), conj) =>
72 if (conj.isOWLClass)
73 (acc1 :+ conj, acc2)
74 else {
75 val cls = freshOWLClass()
76 (
77 acc1 :+ cls,
78 acc2 :+ factory.getOWLSubClassOfAxiom(conj, cls)
79 )
80 }
81 }
82 )
83 (acc2 :+ factory.getOWLSubClassOfAxiom(
84 factory.getOWLObjectIntersectionOf(acc1: _*),
85 sup
86 ))
87 .flatMap(normalize)
88 } else {
89 normalize(factory.getOWLSubClassOfAxiom(factory.getOWLThing, sup))
90 }
91 }
92 /** Conjunction on the rhs
93 *
94 * D c A1 n ... n An -> { D c A1, ... , D c An }
95 */
96 case (_, sup: OWLObjectIntersectionOf) => {
97 val conjuncts = sup.asConjunctSet
98 if (conjuncts.length > 0) {
99 conjuncts
100 .map(cls => factory.getOWLSubClassOfAxiom(sub, cls))
101 .flatMap(normalize)
102 } else {
103 normalize(factory.getOWLSubClassOfAxiom(sub, factory.getOWLThing))
104 }
105 }
106 /** Disjunction on the lhs
107 *
108 * A1 u ... u An c D -> { A1 c D, ... , An c D }
109 */
110 case (sub: OWLObjectUnionOf, _) => {
111 val disjuncts = sub.asDisjunctSet
112 if (disjuncts.length > 0) {
113 disjuncts
114 .map(cls => factory.getOWLSubClassOfAxiom(cls, sup))
115 .flatMap(normalize)
116 } else {
117 normalize(
118 factory.getOWLSubClassOfAxiom(factory.getOWLNothing, sup)
119 )
120 }
121 }
122 /** Disjunction on the rhs is not supported */
123 case (_, sup: OWLObjectUnionOf) => notInHornALCHOIQ(a)
124 /** Complex class expression on existential restriction on the lhs
125 *
126 * exists R . C c D -> { C c X, exists R . X c D }
127 */
128 case (sub: OWLObjectSomeValuesFrom, _)
129 if !sub.getFiller.isOWLClass => {
130 val cls = freshOWLClass()
131 Seq(
132 factory.getOWLSubClassOfAxiom(sub.getFiller, cls),
133 factory.getOWLSubClassOfAxiom(
134 factory.getOWLObjectSomeValuesFrom(sub.getProperty, cls),
135 sup
136 )
137 ).flatMap(normalize)
138 }
139 /** Complex class expression on existential restriction on the rhs
140 *
141 * C c exists R . D -> { X c D, C c exists R . X }
142 */
143 case (_, sup: OWLObjectSomeValuesFrom)
144 if !sup.getFiller.isOWLClass => {
145 val cls = freshOWLClass()
146 Seq(
147 factory.getOWLSubClassOfAxiom(cls, sup.getFiller),
148 factory.getOWLSubClassOfAxiom(
149 sub,
150 factory.getOWLObjectSomeValuesFrom(sup.getProperty, cls)
151 )
152 ).flatMap(normalize)
153 }
154 /** Object/Data universal quantification on the lhs not supported */
155 case (sub: OWLObjectAllValuesFrom, _) => notInHornALCHOIQ(a)
156 case (sub: OWLDataAllValuesFrom, _) => notInHornALCHOIQ(a)
157 /** Object universal quantification on the rhs
158 *
159 * C c forall R . D -> exists R- . C c D
160 */
161 case (_, sup: OWLObjectAllValuesFrom) =>
162 normalize(
163 factory.getOWLSubClassOfAxiom(
164 factory
165 .getOWLObjectSomeValuesFrom(
166 sup.getProperty.getInverseProperty,
167 sub
168 ),
169 sup.getFiller
170 )
171 )
172 /** Object universal quantification on the rhs not supported */
173 case (_, sup: OWLDataAllValuesFrom) => notInHornALCHOIQ(a)
174 /** Exact object/data cardinality restriction on the lhs/rhs
175 *
176 * = i R . C -> <= i R . C n >= i R . X
177 */
178 case (sub: OWLObjectExactCardinality, _) =>
179 normalize(
180 factory.getOWLSubClassOfAxiom(sub.asIntersectionOfMinMax, sup)
181 )
182 case (sub: OWLDataExactCardinality, _) =>
183 normalize(
184 factory.getOWLSubClassOfAxiom(sub.asIntersectionOfMinMax, sup)
185 )
186 case (_, sup: OWLObjectExactCardinality) =>
187 normalize(
188 factory.getOWLSubClassOfAxiom(sub, sup.asIntersectionOfMinMax)
189 )
190 case (_, sup: OWLDataExactCardinality) =>
191 normalize(
192 factory.getOWLSubClassOfAxiom(sub, sup.asIntersectionOfMinMax)
193 )
194 /** Min object/data cardinality restriction on the lhs/rhs
195 *
196 * >= 0 R . C -> top
197 * >= 1 R . C -> exists R . C
198 *
199 * @note not supported when restriction >= 2.
200 */
201 case (sub: OWLObjectMinCardinality, _) =>
202 sub.getCardinality match {
203 case 0 =>
204 normalize(
205 factory.getOWLSubClassOfAxiom(factory.getOWLThing, sup)
206 )
207 case 1 =>
208 normalize(
209 factory.getOWLSubClassOfAxiom(
210 factory.getOWLObjectSomeValuesFrom(
211 sub.getProperty,
212 sub.getFiller
213 ),
214 sup
215 )
216 )
217 case _ => notInHornALCHOIQ(a)
218 }
219 case (sub: OWLDataMinCardinality, _) =>
220 sub.getCardinality match {
221 case 0 =>
222 normalize(
223 factory.getOWLSubClassOfAxiom(factory.getOWLThing, sup)
224 )
225 case 1 =>
226 normalize(
227 factory.getOWLSubClassOfAxiom(
228 factory.getOWLDataSomeValuesFrom(
229 sub.getProperty,
230 sub.getFiller
231 ),
232 sup
233 )
234 )
235 case _ => notInHornALCHOIQ(a)
236 }
237 case (_, sup: OWLObjectMinCardinality) =>
238 sup.getCardinality match {
239 case 0 => Seq()
240 case 1 =>
241 normalize(
242 factory.getOWLSubClassOfAxiom(
243 sub,
244 factory.getOWLObjectSomeValuesFrom(
245 sup.getProperty,
246 sup.getFiller
247 )
248 )
249 )
250 case _ => notInHornALCHOIQ(a)
251 }
252 case (_, sup: OWLDataMinCardinality) =>
253 sup.getCardinality match {
254 case 0 => Seq()
255 case 1 =>
256 normalize(
257 factory.getOWLSubClassOfAxiom(
258 sub,
259 factory.getOWLDataSomeValuesFrom(
260 sup.getProperty,
261 sup.getFiller
262 )
263 )
264 )
265 case _ => notInHornALCHOIQ(a)
266 }
267 /** Max object/data cardinality restriction on the lhs not supported */
268 case (sub: OWLObjectMaxCardinality, _) => notInHornALCHOIQ(a)
269 case (sub: OWLDataMaxCardinality, _) => notInHornALCHOIQ(a)
270 /** Max object/data cardinality restriction on the rhs
271 *
272 * C c <= 0 R . D -> C n exists R . D -> bot
273 * C c <= 1 R . D -> { X c D, C c <= 1 R . D }
274 *
275 * @note not supported when restriction >= 2.
276 */
277 case (_, sup: OWLObjectMaxCardinality) if sup.getCardinality == 0 =>
278 normalize(
279 factory.getOWLSubClassOfAxiom(
280 factory.getOWLObjectIntersectionOf(
281 sub,
282 factory
283 .getOWLObjectSomeValuesFrom(sup.getProperty, sup.getFiller)
284 ),
285 factory.getOWLNothing
286 )
287 )
288 case (_, sup: OWLObjectMaxCardinality)
289 if sup.getCardinality == 1 && !sup.getFiller.isOWLClass => {
290 val cls = freshOWLClass()
291 Seq(
292 factory.getOWLSubClassOfAxiom(cls, sup.getFiller),
293 factory.getOWLSubClassOfAxiom(
294 sub,
295 factory.getOWLObjectMaxCardinality(1, sup.getProperty, cls)
296 )
297 ).flatMap(normalize)
298 }
299 case (_, sup: OWLObjectMaxCardinality) if sup.getCardinality >= 2 =>
300 notInHornALCHOIQ(a)
301 case (_, sup: OWLDataMaxCardinality) if sup.getCardinality == 0 =>
302 normalize(
303 factory.getOWLSubClassOfAxiom(
304 factory.getOWLObjectIntersectionOf(
305 sub,
306 factory
307 .getOWLDataSomeValuesFrom(sup.getProperty, sup.getFiller)
308 ),
309 factory.getOWLNothing
310 )
311 )
312 case (_, sup: OWLDataMaxCardinality) if sup.getCardinality >= 1 =>
313 notInHornALCHOIQ(a)
314 /** HasValue expression on the lhs/rhs
315 *
316 * HasValue(R, a) -> exists R . {a}
317 */
318 case (sub: OWLObjectHasValue, _) =>
319 normalize(
320 factory.getOWLSubClassOfAxiom(
321 factory.getOWLObjectSomeValuesFrom(
322 sub.getProperty,
323 factory.getOWLObjectOneOf(sub.getFiller)
324 ),
325 sup
326 )
327 )
328 case (sub: OWLDataHasValue, _) =>
329 normalize(
330 factory.getOWLSubClassOfAxiom(
331 factory.getOWLDataSomeValuesFrom(
332 sub.getProperty,
333 factory.getOWLDataOneOf(sub.getFiller)
334 ),
335 sup
336 )
337 )
338 case (_, sup: OWLObjectHasValue) =>
339 normalize(
340 factory.getOWLSubClassOfAxiom(
341 sub,
342 factory.getOWLObjectSomeValuesFrom(
343 sup.getProperty,
344 factory.getOWLObjectOneOf(sup.getFiller)
345 )
346 )
347 )
348 case (_, sup: OWLDataHasValue) =>
349 normalize(
350 factory.getOWLSubClassOfAxiom(
351 sub,
352 factory.getOWLDataSomeValuesFrom(
353 sup.getProperty,
354 factory.getOWLDataOneOf(sup.getFiller)
355 )
356 )
357 )
358 /** Enumeration of individuals on the lhs
359 *
360 * {a1, ... ,an} c D -> { D(a1), ..., D(an) }
361 */
362 case (sub: OWLObjectOneOf, _) =>
363 sub.getIndividuals.map(factory.getOWLClassAssertionAxiom(sup, _))
364 /** Enumeration of individuals on the rhs
365 * It's supported only when of cardinality < 2.
366 */
367 case (_, sup: OWLObjectOneOf) if sup.getIndividuals.length == 0 =>
368 normalize(factory.getOWLSubClassOfAxiom(sub, factory.getOWLNothing))
369 case (_, sup: OWLObjectOneOf) if sup.getIndividuals.length > 2 =>
370 notInHornALCHOIQ(a)
371 /** Class complement on the lhs
372 *
373 * ~C c D -> top c C n D
374 */
375 case (sub: OWLObjectComplementOf, _) =>
376 normalize(
377 factory.getOWLSubClassOfAxiom(
378 factory.getOWLThing,
379 factory.getOWLObjectIntersectionOf(sub.getComplementNNF, sup)
380 )
381 )
382 /** Class complement on the rhs
383 *
384 * C c ~D -> C n D c bot
385 */
386 case (_, sup: OWLObjectComplementOf) =>
387 normalize(
388 factory.getOWLSubClassOfAxiom(
389 factory.getOWLObjectIntersectionOf(sup.getComplementNNF, sub),
390 factory.getOWLNothing
391 )
392 )
393 /** Axiom is already normalized */
394 case _ => Seq(a)
395 }
396 }
397
398 case a: OWLEquivalentClassesAxiom => {
399 a.getAxiomWithoutAnnotations.asOWLSubClassOfAxioms.flatMap(normalize)
400 }
401
402 case a: OWLEquivalentObjectPropertiesAxiom => {
403 a.getAxiomWithoutAnnotations.asSubObjectPropertyOfAxioms.flatMap(
404 normalize
405 )
406 }
407
408 case a: OWLEquivalentDataPropertiesAxiom => {
409 a.getAxiomWithoutAnnotations.asSubDataPropertyOfAxioms.flatMap(
410 normalize
411 )
412 }
413
414 case a: OWLObjectPropertyDomainAxiom =>
415 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
416
417 case a: OWLObjectPropertyRangeAxiom =>
418 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
419
420 case a: OWLDataPropertyDomainAxiom =>
421 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
422
423 case a: OWLDataPropertyRangeAxiom =>
424 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
425
426 case a: OWLDisjointClassesAxiom =>
427 a.asPairwiseAxioms.map((a) => {
428 val classes = a.getAxiomWithoutAnnotations.getClassExpressions
429 factory.getOWLSubClassOfAxiom(
430 factory.getOWLObjectIntersectionOf(classes),
431 factory.getOWLNothing
432 )
433 })
434
435 case a: OWLInverseObjectPropertiesAxiom =>
436 a.getAxiomWithoutAnnotations.asSubObjectPropertyOfAxioms.flatMap(
437 normalize
438 )
439
440 case a: OWLFunctionalObjectPropertyAxiom =>
441 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
442
443 case a: OWLFunctionalDataPropertyAxiom =>
444 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
445
446 case a: OWLInverseFunctionalObjectPropertyAxiom =>
447 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
448
449 case a: OWLSymmetricObjectPropertyAxiom =>
450 a.getAxiomWithoutAnnotations.asSubPropertyAxioms.flatMap(normalize)
451
452 case a: OWLDifferentIndividualsAxiom =>
453 a.asPairwiseAxioms.map((a) => {
454 val classes = a.getIndividuals.map(factory.getOWLObjectOneOf(_))
455 factory.getOWLSubClassOfAxiom(
456 factory.getOWLObjectIntersectionOf(classes),
457 factory.getOWLNothing
458 )
459 })
460
461 case a: OWLIrreflexiveObjectPropertyAxiom =>
462 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
463
464 case a: OWLSameIndividualAxiom =>
465 a.getAxiomWithoutAnnotations.asOWLSubClassOfAxioms.flatMap(normalize)
466
467 case a: OWLDisjointUnionAxiom =>
468 Seq(a.getOWLDisjointClassesAxiom, a.getOWLEquivalentClassesAxiom)
469 .flatMap(normalize)
470
471 /** Complex class assertion
472 *
473 * C(a) -> { X(a), X c C }
474 */
475 case a: OWLClassAssertionAxiom if !a.getClassExpression.isOWLClass => {
476 val cls = freshOWLClass()
477 Seq(
478 factory.getOWLClassAssertionAxiom(cls, a.getIndividual),
479 factory.getOWLSubClassOfAxiom(cls, a.getClassExpression)
480 ).flatMap(normalize)
481 }
482
483 case a: OWLNegativeObjectPropertyAssertionAxiom =>
484 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
485
486 case a: OWLNegativeDataPropertyAssertionAxiom =>
487 normalize(a.getAxiomWithoutAnnotations.asOWLSubClassOfAxiom)
488
489 /** Not in Horn-ALCHOIQ */
490
491 case a: OWLTransitiveObjectPropertyAxiom => notInHornALCHOIQ(a)
492
493 case a: OWLReflexiveObjectPropertyAxiom => notInHornALCHOIQ(a)
494
495 case a: OWLSubPropertyChainOfAxiom => notInHornALCHOIQ(a)
496
497 /** Unsupported */
498
499 case a: OWLAsymmetricObjectPropertyAxiom => notSupported(a)
500
501 case a: OWLDatatypeDefinitionAxiom => notSupported(a)
502
503 case a: OWLDisjointDataPropertiesAxiom => notSupported(a)
504
505 case a: OWLDisjointObjectPropertiesAxiom => notSupported(a)
506
507 case a: OWLHasKeyAxiom => notSupported(a)
508
509 case a: SWRLRule => notSupported(a)
510
511 /** Axiom is already normalized */
512 case a => Seq(a)
513 }
514
515 /** Approximation function for axioms out of Horn-ALCHOIQ
516 *
517 * By default discards the axiom, which guarantees a lower bound
518 * ontology w.r.t. CQ answering.
519 */
520 protected def notInHornALCHOIQ(
521 axiom: OWLLogicalAxiom
522 ): Seq[OWLLogicalAxiom] = {
523 Logger print s"'$axiom' has been ignored because it is not in Horn-ALCHOIQ"
524 Seq()
525 }
526
527 /** Non supported axioms */
528 private def notSupported(axiom: OWLLogicalAxiom): Seq[OWLLogicalAxiom] =
529 throw new RuntimeException(
530 s"'$axiom' is not currently supported."
531 )
532}