aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/rsacomb
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2020-08-06 18:46:23 +0100
committerFederico Igne <federico.igne@cs.ox.ac.uk>2020-08-06 18:46:23 +0100
commit378e56852d8b5875ce5b34e91799dd6e704f2d48 (patch)
tree1b502ff4b916e3c60100082c4619ce6b01b05839 /src/main/scala/rsacomb
parent3408515a868ca65ab907e21160f75c858ead8d46 (diff)
downloadRSAComb-378e56852d8b5875ce5b34e91799dd6e704f2d48.tar.gz
RSAComb-378e56852d8b5875ce5b34e91799dd6e704f2d48.zip
Add unsafety check for ontology roles
The current implementation is still a first attempt and by far not the most effective. It is still missing some corner cases and extensive testing.
Diffstat (limited to 'src/main/scala/rsacomb')
-rw-r--r--src/main/scala/rsacomb/RSAAxiom.scala154
-rw-r--r--src/main/scala/rsacomb/RSAOntology.scala120
2 files changed, 142 insertions, 132 deletions
diff --git a/src/main/scala/rsacomb/RSAAxiom.scala b/src/main/scala/rsacomb/RSAAxiom.scala
index 032b7f9..588c72a 100644
--- a/src/main/scala/rsacomb/RSAAxiom.scala
+++ b/src/main/scala/rsacomb/RSAAxiom.scala
@@ -1,76 +1,64 @@
1package rsacomb 1package rsacomb
2 2
3/* Java imports */ 3/* Java imports */
4// import java.io.File
5// import java.util.stream.{Collectors,Stream}
6
7// import org.semanticweb.owlapi.apibinding.OWLManager
8// import org.semanticweb.owlapi.model.{OWLOntologyManager,OWLOntology}
9// import org.semanticweb.owlapi.model.{OWLAxiom,OWLObjectPropertyExpression}
10import org.semanticweb.owlapi.model.{OWLAxiom,OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} 4import org.semanticweb.owlapi.model.{OWLAxiom,OWLSubClassOfAxiom, OWLEquivalentClassesAxiom}
11import org.semanticweb.owlapi.model.OWLAxiomVisitorEx 5import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression,OWLClass,OWLClassExpression,OWLObjectSomeValuesFrom,OWLObjectMaxCardinality}
12// import org.semanticweb.owlapi.model.parameters.Imports 6import org.semanticweb.owlapi.model.ClassExpressionType
13// import org.semanticweb.owlapi.reasoner.OWLReasoner 7import org.semanticweb.owlapi.model.{OWLAxiomVisitorEx,OWLClassExpressionVisitorEx}
14// import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory
15
16// import tech.oxfordsemantic.jrdfox.logic.Variable
17
18/* Scala imports */
19// import scala.collection.JavaConverters._
20
21/* Local imports */
22// import rsacomb.RSAAxiom
23
24/* Debug only */
25// import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer
26
27// import java.util.HashMap
28// import java.util.stream.{Stream,Collectors}
29
30// import org.semanticweb.owlapi.model.{AxiomType, ClassExpressionType, OWLObjectSomeValuesFrom}
31// import org.semanticweb.owlapi.model.OWLClassExpression
32// import org.semanticweb.owlapi.model.IRI
33// import org.semanticweb.owlapi.reasoner.{OWLReasonerFactory, OWLReasoner}
34// import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl
35
36// import tech.oxfordsemantic.jrdfox.Prefixes
37// import tech.oxfordsemantic.jrdfox.client.{ConnectionFactory, ServerConnection, DataStoreConnection}
38// import tech.oxfordsemantic.jrdfox.client.UpdateType
39// import tech.oxfordsemantic.jrdfox.logic.{Rule, Atom, Literal, Term, Variable}
40// import tech.oxfordsemantic.jrdfox.logic.{BuiltinFunctionCall, TupleTableName}
41// import tech.oxfordsemantic.jrdfox.logic.{LogicFormat}
42
43
44// import rsacomb.SkolemStrategy
45//import org.semanticweb.owlapi.model.{OWLAxiom,OWLObjectPropertyExpression}
46 8
9/* Wrapper trait for the implicit class `RSAAxiom`.
10 */
47trait RSAAxiom { 11trait RSAAxiom {
48 12
49 sealed trait RSAAxiomType 13 /* Identifies some of the axiom types in a Horn-ALCHOIQ ontology
50 object RSAAxiomType { 14 * in normal form. Refer to the paper for more details on the
51 case object T3 extends RSAAxiomType 15 * chosen names.
52 case object T4 extends RSAAxiomType 16 */
53 case object T5 extends RSAAxiomType 17 private sealed trait RSAAxiomType
18 private object RSAAxiomType {
19 case object T3 extends RSAAxiomType // ∃R.A ⊑ B
20 case object T4 extends RSAAxiomType // A ⊑ ≤1R.B
21 case object T5 extends RSAAxiomType // A ⊑ ∃R.B
54 } 22 }
55 23
24 /* Implements additional features on top of `OWLAxiom` from
25 * the OWLAPI.
26 */
56 implicit class RSAAxiom(axiom: OWLAxiom) { 27 implicit class RSAAxiom(axiom: OWLAxiom) {
57 28
29 /* Detecting axiom types:
30 *
31 * In order to reason about role unsafety in Horn-ALCHOIQ
32 * ontologies we need to detect and filter axioms by their
33 * "type".
34 *
35 * This is a simple implementation following the Visitor
36 * pattern imposed by the OWLAPI.
37 */
58 private class RSAAxiomTypeDetector(t: RSAAxiomType) 38 private class RSAAxiomTypeDetector(t: RSAAxiomType)
59 extends OWLAxiomVisitorEx[Boolean] 39 extends OWLAxiomVisitorEx[Boolean]
60 { 40 {
61
62 override 41 override
63 def visit(axiom: OWLSubClassOfAxiom): Boolean = { 42 def visit(axiom: OWLSubClassOfAxiom): Boolean = {
64 true 43 val sub = axiom.getSubClass().getClassExpressionType()
44 val sup = axiom.getSuperClass().getClassExpressionType()
45 t match {
46 case RSAAxiomType.T3 => // ∃R.A ⊑ B
47 sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS
48 case RSAAxiomType.T4 => // A ⊑ ≤1R.B
49 sub == ClassExpressionType.OWL_CLASS && sup == ClassExpressionType.OBJECT_MAX_CARDINALITY
50 case RSAAxiomType.T5 => // A ⊑ ∃R.B
51 sub == ClassExpressionType.OWL_CLASS && sup == ClassExpressionType.OBJECT_SOME_VALUES_FROM
52 }
65 } 53 }
66 54
67 override 55 override
68 def visit(axiom: OWLEquivalentClassesAxiom): Boolean = { 56 def visit(axiom: OWLEquivalentClassesAxiom): Boolean = {
69 true 57 // TODO
58 false
70 } 59 }
71 60
72 def doDefault(axiom : OWLAxiom): Boolean = false 61 def doDefault(axiom : OWLAxiom): Boolean = false
73
74 } 62 }
75 63
76 private def isOfType(t: RSAAxiomType): Boolean = { 64 private def isOfType(t: RSAAxiomType): Boolean = {
@@ -78,9 +66,73 @@ trait RSAAxiom {
78 axiom.accept(visitor) 66 axiom.accept(visitor)
79 } 67 }
80 68
69 /* Exposed methods */
81 def isT3: Boolean = isOfType(RSAAxiomType.T3) 70 def isT3: Boolean = isOfType(RSAAxiomType.T3)
82 def isT4: Boolean = isOfType(RSAAxiomType.T4) 71 def isT4: Boolean = isOfType(RSAAxiomType.T4)
83 def isT5: Boolean = isOfType(RSAAxiomType.T5) 72 def isT5: Boolean = isOfType(RSAAxiomType.T5)
73
74 /* Extracting ObjectPropertyExpressions from axioms
75 *
76 * This extracts all ObjectPropertyExpressions from a given
77 * axiom. While the implementation is generic we use it on axioms
78 * of specific types (see above).
79 *
80 * NOTE: it is not possible to use the `objectPropertyInSignature`
81 * method of `OWLAxiom` because it returns all "role names" involved
82 * in the signature of an axiom. In particular we won't get the inverse
83 * of a role if this appears in the axiom (but we will get the role
84 * itself instead).
85 */
86 private class RSAAxiomRoleExtractor()
87 extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]]
88 {
89
90 private class RSAExprRoleExtractor()
91 extends OWLClassExpressionVisitorEx[List[OWLObjectPropertyExpression]]
92 {
93 override
94 def visit(expr: OWLObjectSomeValuesFrom): List[OWLObjectPropertyExpression] =
95 List(expr.getProperty)
96
97 override
98 def visit(expr: OWLObjectMaxCardinality): List[OWLObjectPropertyExpression] =
99 List(expr.getProperty)
100
101 /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However
102 * if missing, the code throws a `NullPointerException`. It seems like, for some
103 * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as
104 * stated in the JavaDocs.
105 */
106 override
107 def visit(expr: OWLClass): List[OWLObjectPropertyExpression] =
108 List()
109
110 def doDefault(expr: OWLClassExpression): List[OWLObjectPropertyExpression] =
111 List()
112 }
113
114 override
115 def visit(axiom: OWLSubClassOfAxiom): List[OWLObjectPropertyExpression] = {
116 val visitor = new RSAExprRoleExtractor()
117 val sub = axiom.getSubClass.accept(visitor)
118 val sup = axiom.getSuperClass.accept(visitor)
119 sub ++ sup
120 }
121
122 override
123 def visit(axiom: OWLEquivalentClassesAxiom): List[OWLObjectPropertyExpression] = {
124 // TODO
125 List()
126 }
127
128 def doDefault(axiom : OWLAxiom): List[OWLObjectPropertyExpression] = List()
129 }
130
131 /* Exposed methods */
132 def objectPropertyExpressionsInSignature: List[OWLObjectPropertyExpression] = {
133 val visitor = new RSAAxiomRoleExtractor()
134 axiom.accept(visitor)
135 }
84 } 136 }
85 137
86} 138} // trait RSAAxiom \ No newline at end of file
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala
index 7fc8781..474d316 100644
--- a/src/main/scala/rsacomb/RSAOntology.scala
+++ b/src/main/scala/rsacomb/RSAOntology.scala
@@ -1,14 +1,11 @@
1package rsacomb 1package rsacomb
2 2
3/* Java imports */ 3/* Java imports */
4// import java.io.File
5import java.util.stream.{Collectors,Stream} 4import java.util.stream.{Collectors,Stream}
6 5
7import org.semanticweb.owlapi.model.OWLOntology 6import org.semanticweb.owlapi.model.OWLOntology
8import org.semanticweb.owlapi.model.OWLObjectPropertyExpression 7import org.semanticweb.owlapi.model.OWLObjectPropertyExpression
9// import org.semanticweb.owlapi.model.OWLAxiomVisitorEx
10import org.semanticweb.owlapi.model.parameters.Imports 8import org.semanticweb.owlapi.model.parameters.Imports
11// import org.semanticweb.owlapi.reasoner.OWLReasoner
12import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory 9import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory
13 10
14import tech.oxfordsemantic.jrdfox.logic.Variable 11import tech.oxfordsemantic.jrdfox.logic.Variable
@@ -16,32 +13,16 @@ import tech.oxfordsemantic.jrdfox.logic.Variable
16/* Scala imports */ 13/* Scala imports */
17import scala.collection.JavaConverters._ 14import scala.collection.JavaConverters._
18 15
19/* Local imports */
20// import rsacomb.RSAAxiom
21
22/* Debug only */ 16/* Debug only */
23import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer 17import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer
24 18
25// import java.util.HashMap 19/* Wrapper trait for the implicit class `RSAOntology`.
26// import java.util.stream.{Stream,Collectors} 20 */
27
28// import org.semanticweb.owlapi.model.{AxiomType, ClassExpressionType, OWLObjectSomeValuesFrom}
29// import org.semanticweb.owlapi.model.{OWLSubClassOfAxiom, OWLEquivalentClassesAxiom}
30// import org.semanticweb.owlapi.model.OWLClassExpression
31// import org.semanticweb.owlapi.model.IRI
32// import org.semanticweb.owlapi.reasoner.{OWLReasonerFactory, OWLReasoner}
33// import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl
34
35// import tech.oxfordsemantic.jrdfox.Prefixes
36// import tech.oxfordsemantic.jrdfox.client.{ConnectionFactory, ServerConnection, DataStoreConnection}
37// import tech.oxfordsemantic.jrdfox.client.UpdateType
38// import tech.oxfordsemantic.jrdfox.logic.{Rule, Atom, Literal, Term, Variable}
39// import tech.oxfordsemantic.jrdfox.logic.{BuiltinFunctionCall, TupleTableName}
40// import tech.oxfordsemantic.jrdfox.logic.{LogicFormat}
41// import rsacomb.SkolemStrategy
42
43trait RSAOntology { 21trait RSAOntology {
44 22
23 /* Implements additional features to reason about RSA ontologies
24 * on top of `OWLOntology` from the OWLAPI.
25 */
45 implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom { 26 implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom {
46 27
47 def isRSA: Boolean = { 28 def isRSA: Boolean = {
@@ -79,68 +60,45 @@ trait RSAOntology {
79 } 60 }
80 61
81 def getUnsafeRoles: List[OWLObjectPropertyExpression] = { 62 def getUnsafeRoles: List[OWLObjectPropertyExpression] = {
82 63 // The reasoner is used to check unsafety condition for the ontology roles
83 val factory = new StructuralReasonerFactory() 64 val factory = new StructuralReasonerFactory()
84 val reasoner = factory.createReasoner(ontology) 65 val reasoner = factory.createReasoner(ontology)
85 val tbox = ontology.tboxAxioms(Imports.INCLUDED).collect(Collectors.toList()).asScala
86 val rbox = ontology.rboxAxioms(Imports.INCLUDED).collect(Collectors.toList()).asScala
87 66
88 /* DEBUG: print rules in DL syntax */ 67 val tbox = ontology.tboxAxioms(Imports.INCLUDED).collect(Collectors.toSet()).asScala
89 val renderer = new DLSyntaxObjectRenderer()
90 println("\nT5 axioms:")
91 for {
92 axiom <- tbox
93 if axiom.isT5
94 } yield println(renderer.render(axiom))
95
96 // def isT3(axiom : OWLAxiom) : Boolean = {
97 // println(axiom)
98 // axiom.getAxiomType match {
99 // case AxiomType.SUBCLASS_OF =>
100 // val axiom1 = axiom.asInstanceOf[OWLSubClassOfAxiom]
101 // axiom1.getSubClass().getClassExpressionType() == ClassExpressionType.OBJECT_SOME_VALUES_FROM &&
102 // axiom1.getSuperClass().getClassExpressionType() == ClassExpressionType.OWL_CLASS
103 // case AxiomType.EQUIVALENT_CLASSES => false // TODO
104 // }
105 // }
106
107 // def isT4(axiom : OWLAxiom) : Boolean = {
108 // axiom.getAxiomType match {
109 // case AxiomType.SUBCLASS_OF =>
110 // val axiom1 = axiom.asInstanceOf[OWLSubClassOfAxiom]
111 // axiom1.getSubClass().getClassExpressionType() == ClassExpressionType.OWL_CLASS &&
112 // axiom1.getSuperClass().getClassExpressionType() == ClassExpressionType.OBJECT_MAX_CARDINALITY
113 // case AxiomType.EQUIVALENT_CLASSES => false // TODO
114 // }
115 // }
116
117 // def isT5(axiom : OWLAxiom) : Boolean = {
118 // axiom.getAxiomType match {
119 // case AxiomType.SUBCLASS_OF => {
120 // val axiom1 = axiom.asInstanceOf[OWLSubClassOfAxiom]
121 // axiom1.getSubClass().getClassExpressionType() == ClassExpressionType.OWL_CLASS &&
122 // axiom1.getSuperClass().getClassExpressionType() == ClassExpressionType.OBJECT_SOME_VALUES_FROM
123 // }
124 // case AxiomType.EQUIVALENT_CLASSES => false // TODO
125 // }
126 // }
127
128
129 // println("T3")
130 // for {
131 // axiom <- ontology.tboxAxioms(Imports.INCLUDED)
132 // //role <- axiom.objectPropertiesInSignature()
133 // } yield println(axiom)
134
135 // println("T4")
136 // for {
137 // axiom <- ontology.tboxAxioms(Imports.INCLUDED).filter(isT4)
138 // //role <- axiom.objectPropertiesInSignature()
139 // } yield println(axiom)
140 68
69 /* DEBUG: print rules in DL syntax */
70 //val renderer = new DLSyntaxObjectRenderer()
71
72 /* Checking for (1) unsafety condition:
73 *
74 * For all roles p1 appearing in an axiom of type T5, p1 is unsafe
75 * if there exists a role p2 (different from top) appearing in an axiom
76 * of type T3 and p1 is a subproperty of the inverse of p2.
77 *
78 * TODO: We are not checking whether the class expression on the right in T3
79 * is top. For now we can assume it is always the case.
80 */
81 val unsafe = for {
82 ax1 <- tbox
83 if ax1.isT5
84 p1 <- ax1.objectPropertyExpressionsInSignature
85 sup = p1.getInverseProperty +: reasoner.superObjectProperties(p1).map(_.getInverseProperty).collect(Collectors.toList()).asScala
86 ax2 <- tbox
87 if ax2.isT3
88 p2 <- ax2.objectPropertyExpressionsInSignature
89 if sup.contains(p2)
90 } yield p1
91
92 /* Checking for (2) unsafety condition:
93 *
94 * TODO
95 */
141 96
142 /* DEBUG */ 97 /* TODO: We should be able to avoid this last conversion to List.
143 List() 98 * Maybe we should just move everything to Sets instead of Lists, since
99 * they have a more straightforward conversion from Java collections.
100 */
101 unsafe.toList
144 } 102 }
145 103
146 } // implicit class RSAOntology 104 } // implicit class RSAOntology