diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/rsacomb/FilteringProgram.scala | 295 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/Main.scala | 18 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RDFTriple.scala | 60 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RDFoxAxiomConverter.scala | 54 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RDFoxClassExprConverter.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala | 2 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RDFoxUtil.scala | 6 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RSA.scala | 37 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RSAAxiom.scala | 92 | ||||
| -rw-r--r-- | src/main/scala/rsacomb/RSAOntology.scala | 105 |
10 files changed, 598 insertions, 83 deletions
diff --git a/src/main/scala/rsacomb/FilteringProgram.scala b/src/main/scala/rsacomb/FilteringProgram.scala new file mode 100644 index 0000000..91c98d8 --- /dev/null +++ b/src/main/scala/rsacomb/FilteringProgram.scala | |||
| @@ -0,0 +1,295 @@ | |||
| 1 | package rsacomb | ||
| 2 | |||
| 3 | import tech.oxfordsemantic.jrdfox.logic._ | ||
| 4 | import scala.collection.JavaConverters._ | ||
| 5 | |||
| 6 | class FilteringProgram(query: Query, constants: List[Term]) extends RDFTriple { | ||
| 7 | |||
| 8 | private val bounded: List[Term] = this.getBoundedVariables | ||
| 9 | private val answer: List[Term] = query.getAnswerVariables.asScala.toList | ||
| 10 | |||
| 11 | val facts: List[Atom] = constants.map(named) | ||
| 12 | val rules: List[Rule] = this.generateFilteringProgram() | ||
| 13 | |||
| 14 | private def named(t: Term): Atom = | ||
| 15 | Atom.rdf(t, IRI.RDF_TYPE, RSA.internal("NAMED")) | ||
| 16 | |||
| 17 | private def getBoundedVariables: List[Variable] = List() | ||
| 18 | |||
| 19 | private def generateFilteringProgram(): List[Rule] = { | ||
| 20 | // Query formula as a rule body | ||
| 21 | val body = queryToBody(query.getQueryFormula) | ||
| 22 | // Auxiliar predicates/helpers | ||
| 23 | def not(atom: Atom): BodyFormula = Negation.create(atom) | ||
| 24 | val predQM = | ||
| 25 | Atom.create( | ||
| 26 | TupleTableName.create(RSA.internal("QM").getIRI), | ||
| 27 | (answer ++ bounded): _* | ||
| 28 | ) | ||
| 29 | def predID(t1: Term, t2: Term) = | ||
| 30 | Atom.create( | ||
| 31 | TupleTableName.create(RSA.internal("ID").getIRI), | ||
| 32 | (answer ++ bounded).appended(t1).appended(t2): _* | ||
| 33 | ) | ||
| 34 | def predNI(t1: Term): Atom = | ||
| 35 | Atom.rdf(t1, IRI.RDF_TYPE, RSA.internal("NI")) | ||
| 36 | def predTQ(sx: String, t1: Term, t2: Term) = | ||
| 37 | Atom.create( | ||
| 38 | TupleTableName.create(RSA.internal(s"TQ_$sx").getIRI), | ||
| 39 | (answer ++ bounded).appended(t1).appended(t2): _* | ||
| 40 | ) | ||
| 41 | def predAQ(sx: String, t1: Term, t2: Term) = | ||
| 42 | Atom.create( | ||
| 43 | TupleTableName.create(RSA.internal(s"AQ_$sx").getIRI), | ||
| 44 | (answer ++ bounded).appended(t1).appended(t2): _* | ||
| 45 | ) | ||
| 46 | val predFK = | ||
| 47 | Atom.create( | ||
| 48 | TupleTableName.create(RSA.internal("FK").getIRI), | ||
| 49 | (answer ++ bounded): _* | ||
| 50 | ) | ||
| 51 | val predSP = | ||
| 52 | Atom.create( | ||
| 53 | TupleTableName.create(RSA.internal("SP").getIRI), | ||
| 54 | (answer ++ bounded): _* | ||
| 55 | ) | ||
| 56 | val predANS = | ||
| 57 | Atom.create( | ||
| 58 | TupleTableName.create(RSA.internal("ANS").getIRI), | ||
| 59 | answer: _* | ||
| 60 | ) | ||
| 61 | |||
| 62 | /* Rule 1 */ | ||
| 63 | val r1 = Rule.create(predQM, body: _*) | ||
| 64 | |||
| 65 | /* Rules 3x */ | ||
| 66 | val r3a = | ||
| 67 | for ((v, i) <- bounded.zipWithIndex) | ||
| 68 | yield Rule.create( | ||
| 69 | predID(RSA.internal(i), RSA.internal(i)), | ||
| 70 | predQM, | ||
| 71 | not(predNI(v)) | ||
| 72 | ) | ||
| 73 | val r3b = Rule.create( | ||
| 74 | predID(Variable.create("V"), Variable.create("U")), | ||
| 75 | predID(Variable.create("U"), Variable.create("V")) | ||
| 76 | ) | ||
| 77 | val r3c = Rule.create( | ||
| 78 | predID(Variable.create("U"), Variable.create("W")), | ||
| 79 | predID(Variable.create("U"), Variable.create("V")), | ||
| 80 | predID(Variable.create("V"), Variable.create("W")) | ||
| 81 | ) | ||
| 82 | |||
| 83 | /* Rules 4x */ | ||
| 84 | val r4a = for { | ||
| 85 | role1 <- body.filter(_.isRoleAssertion) | ||
| 86 | if bounded contains (role1 getArgument 2) | ||
| 87 | role2 <- body.filter(_.isRoleAssertion) | ||
| 88 | if bounded contains (role2 getArgument 2) | ||
| 89 | } yield Rule.create( | ||
| 90 | predFK, | ||
| 91 | role1 suffix "_f", | ||
| 92 | role2 suffix "_f", | ||
| 93 | predID( | ||
| 94 | RSA.internal(bounded.indexOf(role1 getArgument 2)), | ||
| 95 | RSA.internal(bounded.indexOf(role2 getArgument 2)) | ||
| 96 | ), | ||
| 97 | not(Atom.rdf(role1 getArgument 0, IRI.SAME_AS, role2 getArgument 0)) | ||
| 98 | ) | ||
| 99 | val r4b = for { | ||
| 100 | role1 <- body.filter(_.isRoleAssertion) | ||
| 101 | if bounded contains (role1 getArgument 2) | ||
| 102 | role2 <- body.filter(_.isRoleAssertion) | ||
| 103 | if bounded contains (role2 getArgument 0) | ||
| 104 | } yield Rule.create( | ||
| 105 | predFK, | ||
| 106 | role1 suffix "_f", | ||
| 107 | role2 suffix "_b", | ||
| 108 | predID( | ||
| 109 | RSA.internal(bounded.indexOf(role1 getArgument 2)), | ||
| 110 | RSA.internal(bounded.indexOf(role2 getArgument 0)) | ||
| 111 | ), | ||
| 112 | not(Atom.rdf(role1 getArgument 0, IRI.SAME_AS, role2 getArgument 2)) | ||
| 113 | ) | ||
| 114 | val r4c = for { | ||
| 115 | role1 <- body.filter(_.isRoleAssertion) | ||
| 116 | if bounded contains (role1 getArgument 0) | ||
| 117 | role2 <- body.filter(_.isRoleAssertion) | ||
| 118 | if bounded contains (role2 getArgument 0) | ||
| 119 | } yield Rule.create( | ||
| 120 | predFK, | ||
| 121 | role1 suffix "_b", | ||
| 122 | role2 suffix "_b", | ||
| 123 | predID( | ||
| 124 | RSA.internal(bounded.indexOf(role1 getArgument 0)), | ||
| 125 | RSA.internal(bounded.indexOf(role2 getArgument 0)) | ||
| 126 | ), | ||
| 127 | not(Atom.rdf(role1 getArgument 2, IRI.SAME_AS, role2 getArgument 2)) | ||
| 128 | ) | ||
| 129 | |||
| 130 | /* Rules 5x */ | ||
| 131 | val r5a = for { | ||
| 132 | role1 <- body.filter(_.isRoleAssertion) | ||
| 133 | role1arg0 = role1 getArgument 0 | ||
| 134 | role1arg2 = role1 getArgument 2 | ||
| 135 | if bounded contains role1arg0 | ||
| 136 | if bounded contains role1arg2 | ||
| 137 | role2 <- body.filter(_.isRoleAssertion) | ||
| 138 | role2arg0 = role2 getArgument 0 | ||
| 139 | role2arg2 = role2 getArgument 2 | ||
| 140 | if bounded contains role2arg0 | ||
| 141 | if bounded contains role2arg2 | ||
| 142 | } yield Rule.create( | ||
| 143 | predID( | ||
| 144 | RSA.internal(bounded indexOf role1arg0), | ||
| 145 | RSA.internal(bounded indexOf role2arg0) | ||
| 146 | ), | ||
| 147 | role1 suffix "_f", | ||
| 148 | role2 suffix "_f", | ||
| 149 | predID( | ||
| 150 | RSA.internal(bounded indexOf role1arg2), | ||
| 151 | RSA.internal(bounded indexOf role2arg2) | ||
| 152 | ), | ||
| 153 | Atom.rdf(role1arg0, IRI.SAME_AS, role2arg0), | ||
| 154 | not(predNI(role1arg0)) | ||
| 155 | ) | ||
| 156 | val r5b = for { | ||
| 157 | role1 <- body.filter(_.isRoleAssertion) | ||
| 158 | role1arg0 = role1 getArgument 0 | ||
| 159 | role1arg2 = role1 getArgument 2 | ||
| 160 | if bounded contains role1arg0 | ||
| 161 | if bounded contains role1arg2 | ||
| 162 | role2 <- body.filter(_.isRoleAssertion) | ||
| 163 | role2arg0 = role2 getArgument 0 | ||
| 164 | role2arg2 = role2 getArgument 2 | ||
| 165 | if bounded contains role2arg0 | ||
| 166 | if bounded contains role2arg2 | ||
| 167 | } yield Rule.create( | ||
| 168 | predID( | ||
| 169 | RSA.internal(bounded indexOf role1arg0), | ||
| 170 | RSA.internal(bounded indexOf role2arg2) | ||
| 171 | ), | ||
| 172 | role1 suffix "_f", | ||
| 173 | role2 suffix "_b", | ||
| 174 | predID( | ||
| 175 | RSA.internal(bounded indexOf role1arg2), | ||
| 176 | RSA.internal(bounded indexOf role2arg0) | ||
| 177 | ), | ||
| 178 | Atom.rdf(role1arg0, IRI.SAME_AS, role2arg2), | ||
| 179 | not(predNI(role1arg0)) | ||
| 180 | ) | ||
| 181 | val r5c = for { | ||
| 182 | role1 <- body.filter(_.isRoleAssertion) | ||
| 183 | role1arg0 = role1 getArgument 0 | ||
| 184 | role1arg2 = role1 getArgument 2 | ||
| 185 | if bounded contains role1arg0 | ||
| 186 | if bounded contains role1arg2 | ||
| 187 | role2 <- body.filter(_.isRoleAssertion) | ||
| 188 | role2arg0 = role2 getArgument 0 | ||
| 189 | role2arg2 = role2 getArgument 2 | ||
| 190 | if bounded contains role2arg0 | ||
| 191 | if bounded contains role2arg2 | ||
| 192 | } yield Rule.create( | ||
| 193 | predID( | ||
| 194 | RSA.internal(bounded indexOf role1arg2), | ||
| 195 | RSA.internal(bounded indexOf role2arg2) | ||
| 196 | ), | ||
| 197 | role1 suffix "_b", | ||
| 198 | role2 suffix "_b", | ||
| 199 | predID( | ||
| 200 | RSA.internal(bounded indexOf role1arg0), | ||
| 201 | RSA.internal(bounded indexOf role2arg0) | ||
| 202 | ), | ||
| 203 | Atom.rdf(role1arg2, IRI.SAME_AS, role2arg2), | ||
| 204 | not(predNI(role1arg2)) | ||
| 205 | ) | ||
| 206 | |||
| 207 | /* Rules 6 */ | ||
| 208 | val r6 = for { | ||
| 209 | role <- body.filter(_.isRoleAssertion) | ||
| 210 | arg0 = role getArgument 0 | ||
| 211 | arg2 = role getArgument 2 | ||
| 212 | if bounded contains arg0 | ||
| 213 | if bounded contains arg2 | ||
| 214 | sx <- List("_f", "_b") | ||
| 215 | } yield Rule.create( | ||
| 216 | predAQ(sx, Variable.create("V"), Variable.create("W")), | ||
| 217 | role suffix sx, | ||
| 218 | predID( | ||
| 219 | RSA.internal(bounded indexOf arg0), | ||
| 220 | RSA.internal(Variable.create("V")) | ||
| 221 | ), | ||
| 222 | predID( | ||
| 223 | RSA.internal(bounded indexOf arg2), | ||
| 224 | RSA.internal(Variable.create("W")) | ||
| 225 | ) | ||
| 226 | ) | ||
| 227 | |||
| 228 | /* Rules 7x */ | ||
| 229 | val r7a = | ||
| 230 | for (sx <- List("f", "b")) | ||
| 231 | yield Rule.create( | ||
| 232 | predTQ(sx, Variable.create("U"), Variable.create("V")), | ||
| 233 | predAQ(sx, Variable.create("U"), Variable.create("V")) | ||
| 234 | ) | ||
| 235 | val r7b = | ||
| 236 | for (r <- List("f", "b")) | ||
| 237 | yield Rule.create( | ||
| 238 | predTQ(r, Variable.create("U"), Variable.create("W")), | ||
| 239 | predAQ(r, Variable.create("U"), Variable.create("V")), | ||
| 240 | predTQ(r, Variable.create("V"), Variable.create("W")) | ||
| 241 | ) | ||
| 242 | |||
| 243 | /* Rules 8x */ | ||
| 244 | val r8a = | ||
| 245 | for (v <- answer) yield Rule.create(predSP, predQM, not(named(v))) | ||
| 246 | val r8b = | ||
| 247 | Rule.create(predSP, predFK) | ||
| 248 | val r8c = | ||
| 249 | for (sx <- List("_f", "_b")) | ||
| 250 | yield Rule.create( | ||
| 251 | predSP, | ||
| 252 | predTQ(sx, Variable.create("V"), Variable.create("V")) | ||
| 253 | ) | ||
| 254 | |||
| 255 | /* Rule 9 */ | ||
| 256 | val r9 = Rule.create(predANS, predQM, not(predSP)) | ||
| 257 | |||
| 258 | List.empty | ||
| 259 | .prepended(r9) | ||
| 260 | .prependedAll(r8c) | ||
| 261 | .prepended(r8b) | ||
| 262 | .prependedAll(r8a) | ||
| 263 | .prependedAll(r7b) | ||
| 264 | .prependedAll(r7a) | ||
| 265 | .prependedAll(r6) | ||
| 266 | .prependedAll(r5c) | ||
| 267 | .prependedAll(r5b) | ||
| 268 | .prependedAll(r5a) | ||
| 269 | .prependedAll(r4c) | ||
| 270 | .prependedAll(r4b) | ||
| 271 | .prependedAll(r4a) | ||
| 272 | .prepended(r3c) | ||
| 273 | .prepended(r3b) | ||
| 274 | .prependedAll(r3a) | ||
| 275 | .prepended(r1) | ||
| 276 | } | ||
| 277 | |||
| 278 | /* NOTE: we are restricting to queries that contain conjunctions of | ||
| 279 | * atoms for the time being. This might need to be reviewed in the | ||
| 280 | * future. | ||
| 281 | */ | ||
| 282 | private def queryToBody(body: Formula): List[Atom] = | ||
| 283 | body match { | ||
| 284 | case a: Atom => List(a); | ||
| 285 | case a: Conjunction => | ||
| 286 | a.getConjuncts.asScala.toList.flatMap(queryToBody); | ||
| 287 | case _ => List() | ||
| 288 | } | ||
| 289 | |||
| 290 | } // class FilteringProgram | ||
| 291 | |||
| 292 | object FilteringProgram { | ||
| 293 | def apply(query: Query, constants: List[Term]): FilteringProgram = | ||
| 294 | new FilteringProgram(query, constants) | ||
| 295 | } | ||
diff --git a/src/main/scala/rsacomb/Main.scala b/src/main/scala/rsacomb/Main.scala index 9cd6680..d2bc2a8 100644 --- a/src/main/scala/rsacomb/Main.scala +++ b/src/main/scala/rsacomb/Main.scala | |||
| @@ -48,18 +48,20 @@ object RSAComb { | |||
| 48 | */ | 48 | */ |
| 49 | 49 | ||
| 50 | val ontology = RSA.loadOntology(ontoPath) | 50 | val ontology = RSA.loadOntology(ontoPath) |
| 51 | ontology.isRSA | 51 | if (ontology.isRSA) { |
| 52 | 52 | ||
| 53 | /* Build canonical model */ | 53 | /* Build canonical model */ |
| 54 | //val tboxCanon = rsa.canonicalModel() | 54 | //val tboxCanon = rsa.canonicalModel() |
| 55 | 55 | ||
| 56 | /* Load query */ | 56 | /* Load query */ |
| 57 | //val query = ... | 57 | val query = RSA.test_query |
| 58 | 58 | ||
| 59 | /* Compute the filtering program from the given query */ | 59 | /* Compute the filtering program from the given query */ |
| 60 | //val tboxFilter = rsa.filteringProgram(query) | 60 | val filter = ontology.getFilteringProgram(query) |
| 61 | 61 | ||
| 62 | /* ... */ | 62 | /* ... */ |
| 63 | |||
| 64 | } | ||
| 63 | 65 | ||
| 64 | /* DEBUG ONLY */ | 66 | /* DEBUG ONLY */ |
| 65 | println("Ok!") | 67 | println("Ok!") |
diff --git a/src/main/scala/rsacomb/RDFTriple.scala b/src/main/scala/rsacomb/RDFTriple.scala new file mode 100644 index 0000000..11ad6d4 --- /dev/null +++ b/src/main/scala/rsacomb/RDFTriple.scala | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | package rsacomb | ||
| 2 | |||
| 3 | import tech.oxfordsemantic.jrdfox.logic.{Atom, IRI, TupleTableName} | ||
| 4 | |||
| 5 | trait RDFTriple { | ||
| 6 | |||
| 7 | implicit class RDFTriple(atom: Atom) { | ||
| 8 | |||
| 9 | /* Is this the best way to determine if an atom is an RDF triple? | ||
| 10 | * Note that we can't use `getNumberOfArguments()` because is not | ||
| 11 | * "consistent": | ||
| 12 | * - for an atom created with `rdf(<term1>, <term2>, <term3>)`, | ||
| 13 | * `getNumberOfArguments` returns 3 | ||
| 14 | * - for an atom created with `Atom.create(<tupletablename>, <term1>, | ||
| 15 | * <term2>, <term3>)`, `getNumberOfArguments()` returns 3 | ||
| 16 | * | ||
| 17 | * This is probably because `Atom.rdf(...) is implemented as: | ||
| 18 | * ```scala | ||
| 19 | * def rdf(term1: Term, term2: Term, term3: Term): Atom = | ||
| 20 | * Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3) | ||
| 21 | * ``` | ||
| 22 | */ | ||
| 23 | def isRdfTriple: Boolean = | ||
| 24 | atom.getTupleTableName.getIRI.equals("internal:triple") | ||
| 25 | |||
| 26 | def isClassAssertion: Boolean = | ||
| 27 | atom.isRdfTriple && atom.getArgument(1).equals(IRI.RDF_TYPE) | ||
| 28 | |||
| 29 | def isRoleAssertion: Boolean = | ||
| 30 | atom.isRdfTriple && !atom.getArgument(1).equals(IRI.RDF_TYPE) | ||
| 31 | |||
| 32 | def suffix(sx: String): Atom = | ||
| 33 | if (this.isClassAssertion) { | ||
| 34 | val newclass = atom.getArgument(2) match { | ||
| 35 | case iri: IRI => IRI.create(iri.getIRI.appendedAll(sx)) | ||
| 36 | case other => other | ||
| 37 | } | ||
| 38 | Atom.rdf( | ||
| 39 | atom getArgument 0, | ||
| 40 | atom getArgument 1, | ||
| 41 | newclass | ||
| 42 | ) | ||
| 43 | } else if (this.isRoleAssertion) { | ||
| 44 | val newrole = atom.getArgument(1) match { | ||
| 45 | case iri: IRI => IRI.create(iri.getIRI.appendedAll(sx)) | ||
| 46 | case other => other | ||
| 47 | } | ||
| 48 | Atom.rdf( | ||
| 49 | atom getArgument 0, | ||
| 50 | newrole, | ||
| 51 | atom getArgument 2 | ||
| 52 | ) | ||
| 53 | } else { | ||
| 54 | val newname = | ||
| 55 | TupleTableName.create(atom.getTupleTableName.getIRI.appendedAll(sx)) | ||
| 56 | Atom.create(newname, atom.getArguments()) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | } | ||
diff --git a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala b/src/main/scala/rsacomb/RDFoxAxiomConverter.scala index 0a79823..d90c966 100644 --- a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala +++ b/src/main/scala/rsacomb/RDFoxAxiomConverter.scala | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | package rsacomb | 1 | package rsacomb |
| 2 | 2 | ||
| 3 | import org.semanticweb.owlapi.model.{OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom, OWLObjectPropertyExpression} | 3 | import org.semanticweb.owlapi.model.{ |
| 4 | OWLAxiom, | ||
| 5 | OWLSubClassOfAxiom, | ||
| 6 | OWLEquivalentClassesAxiom, | ||
| 7 | OWLObjectPropertyExpression | ||
| 8 | } | ||
| 4 | import org.semanticweb.owlapi.model.OWLAxiomVisitorEx | 9 | import org.semanticweb.owlapi.model.OWLAxiomVisitorEx |
| 5 | 10 | ||
| 6 | import tech.oxfordsemantic.jrdfox.logic.{Rule, BodyFormula} | 11 | import tech.oxfordsemantic.jrdfox.logic.{Rule, BodyFormula} |
| @@ -17,22 +22,24 @@ import org.semanticweb.owlapi.model.OWLObjectProperty | |||
| 17 | object RDFoxAxiomConverter { | 22 | object RDFoxAxiomConverter { |
| 18 | 23 | ||
| 19 | def apply( | 24 | def apply( |
| 20 | term : Term = Variable.create("x"), | 25 | term: Term, |
| 21 | skolem : SkolemStrategy = SkolemStrategy.None, | 26 | skolem: SkolemStrategy = SkolemStrategy.None, |
| 22 | unsafe : List[OWLObjectPropertyExpression] = List() | 27 | unsafe: List[OWLObjectPropertyExpression] = List() |
| 23 | ) : RDFoxAxiomConverter = | 28 | ): RDFoxAxiomConverter = |
| 24 | new RDFoxAxiomConverter(term, skolem, unsafe) | 29 | new RDFoxAxiomConverter(term, skolem, unsafe) |
| 25 | 30 | ||
| 26 | } // object RDFoxAxiomConverter | 31 | } // object RDFoxAxiomConverter |
| 27 | 32 | ||
| 28 | class RDFoxAxiomConverter(term : Term, skolem : SkolemStrategy, unsafe : List[OWLObjectPropertyExpression]) | 33 | class RDFoxAxiomConverter( |
| 29 | extends OWLAxiomVisitorEx[List[Rule]] | 34 | term: Term, |
| 30 | { | 35 | skolem: SkolemStrategy, |
| 36 | unsafe: List[OWLObjectPropertyExpression] | ||
| 37 | ) extends OWLAxiomVisitorEx[List[Rule]] { | ||
| 31 | 38 | ||
| 32 | override | 39 | override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = { |
| 33 | def visit(axiom : OWLSubClassOfAxiom) : List[Rule] = { | ||
| 34 | // Skolemization is needed only for the head of an axiom | 40 | // Skolemization is needed only for the head of an axiom |
| 35 | val subVisitor = new RDFoxClassExprConverter(term,SkolemStrategy.None, unsafe) | 41 | val subVisitor = |
| 42 | new RDFoxClassExprConverter(term, SkolemStrategy.None, unsafe) | ||
| 36 | val superVisitor = new RDFoxClassExprConverter(term, skolem, unsafe) | 43 | val superVisitor = new RDFoxClassExprConverter(term, skolem, unsafe) |
| 37 | // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext): | 44 | // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext): |
| 38 | // - the `res` List is a list of atoms resulting from the conversion | 45 | // - the `res` List is a list of atoms resulting from the conversion |
| @@ -45,30 +52,27 @@ class RDFoxAxiomConverter(term : Term, skolem : SkolemStrategy, unsafe : List[OW | |||
| 45 | val sup = axiom.getSuperClass.accept(superVisitor) | 52 | val sup = axiom.getSuperClass.accept(superVisitor) |
| 46 | val head = sup.res.asJava | 53 | val head = sup.res.asJava |
| 47 | val body = (sub.res ++ sup.ext).asJava | 54 | val body = (sub.res ++ sup.ext).asJava |
| 48 | List(Rule.create(head,body)) | 55 | List(Rule.create(head, body)) |
| 49 | } | 56 | } |
| 50 | 57 | ||
| 51 | override | 58 | override def visit(axiom: OWLEquivalentClassesAxiom): List[Rule] = { |
| 52 | def visit(axiom : OWLEquivalentClassesAxiom) : List[Rule] = { | ||
| 53 | for { | 59 | for { |
| 54 | axiom1 <- axiom.asPairwiseAxioms.asScala.toList | 60 | axiom1 <- axiom.asPairwiseAxioms.asScala.toList |
| 55 | axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList | 61 | axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList |
| 56 | rule <- axiom2.accept(this) | 62 | rule <- axiom2.accept(this) |
| 57 | } yield rule | 63 | } yield rule |
| 58 | } | 64 | } |
| 59 | 65 | ||
| 60 | override | 66 | override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = { |
| 61 | def visit(axiom : OWLSubObjectPropertyOfAxiom) : List[Rule] = { | 67 | val term1 = RSA.getFreshVariable() |
| 62 | // TODO: variables needs to be handled at visitor level. Hardcoding | 68 | val subVisitor = |
| 63 | // the name of the varibles might lead to errors for complex cases. | 69 | new RDFoxPropertyExprConverter(term, term1, SkolemStrategy.None) |
| 64 | val term1 = Variable.create("y") | 70 | val superVisitor = new RDFoxPropertyExprConverter(term, term1, skolem) |
| 65 | val subVisitor = new RDFoxPropertyExprConverter(term,term1,SkolemStrategy.None) | ||
| 66 | val superVisitor = new RDFoxPropertyExprConverter(term,term1,skolem) | ||
| 67 | val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor) | 71 | val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor) |
| 68 | val head: List[Atom] = axiom.getSuperProperty.accept(superVisitor) | 72 | val head: List[Atom] = axiom.getSuperProperty.accept(superVisitor) |
| 69 | List(Rule.create(head.asJava,body.asJava)) | 73 | List(Rule.create(head.asJava, body.asJava)) |
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | def doDefault(axiom : OWLAxiom) : List[Rule] = List() | 76 | def doDefault(axiom: OWLAxiom): List[Rule] = List() |
| 73 | 77 | ||
| 74 | } // class RDFoxAxiomConverter | 78 | } // class RDFoxAxiomConverter |
diff --git a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala index b9d3ea2..a319d86 100644 --- a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala +++ b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala | |||
| @@ -34,7 +34,7 @@ import org.semanticweb.owlapi.model.OWLObjectProperty | |||
| 34 | object RDFoxClassExprConverter { | 34 | object RDFoxClassExprConverter { |
| 35 | 35 | ||
| 36 | def apply( | 36 | def apply( |
| 37 | term: Term = Variable.create("x"), | 37 | term: Term, |
| 38 | skolem: SkolemStrategy = SkolemStrategy.None, | 38 | skolem: SkolemStrategy = SkolemStrategy.None, |
| 39 | unsafe: List[OWLObjectPropertyExpression] = List() | 39 | unsafe: List[OWLObjectPropertyExpression] = List() |
| 40 | ): RDFoxClassExprConverter = | 40 | ): RDFoxClassExprConverter = |
| @@ -61,7 +61,8 @@ class RDFoxClassExprConverter( | |||
| 61 | 61 | ||
| 62 | // OWLClass | 62 | // OWLClass |
| 63 | override def visit(expr: OWLClass): RDFoxRuleShards = { | 63 | override def visit(expr: OWLClass): RDFoxRuleShards = { |
| 64 | val atom = List(Atom.rdf(term, IRI.RDF_TYPE, expr.getIRI())) | 64 | val iri: IRI = if (expr.isTopEntity()) IRI.THING else expr.getIRI() |
| 65 | val atom = List(Atom.rdf(term, IRI.RDF_TYPE, iri)) | ||
| 65 | RDFoxRuleShards(atom, List()) | 66 | RDFoxRuleShards(atom, List()) |
| 66 | } | 67 | } |
| 67 | 68 | ||
| @@ -94,9 +95,8 @@ class RDFoxClassExprConverter( | |||
| 94 | 95 | ||
| 95 | // OWLObjectSomeValuesFrom | 96 | // OWLObjectSomeValuesFrom |
| 96 | override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = { | 97 | override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = { |
| 97 | // TODO: variables needs to be handled at visitor level. Hardcoding | 98 | val y = RSA.getFreshVariable() |
| 98 | // the name of the varibles might lead to errors for complex cases. | 99 | // Here we are assuming a role name |
| 99 | val y = Variable.create("y") | ||
| 100 | val prop = expr.getProperty() | 100 | val prop = expr.getProperty() |
| 101 | // Computes the result of rule skolemization. Depending on the used | 101 | // Computes the result of rule skolemization. Depending on the used |
| 102 | // technique it might involve the introduction of additional atoms, | 102 | // technique it might involve the introduction of additional atoms, |
| @@ -139,7 +139,7 @@ class RDFoxClassExprConverter( | |||
| 139 | // OWLObjectMaxCardinality | 139 | // OWLObjectMaxCardinality |
| 140 | override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = { | 140 | override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = { |
| 141 | // TODO: again, no hardcoded variables | 141 | // TODO: again, no hardcoded variables |
| 142 | val vars = List(Variable.create("y"), Variable.create("z")) | 142 | val vars = List(RSA.getFreshVariable(), RSA.getFreshVariable()) |
| 143 | val classResult = RDFoxClassExprConverter.merge( | 143 | val classResult = RDFoxClassExprConverter.merge( |
| 144 | vars | 144 | vars |
| 145 | .map(new RDFoxClassExprConverter(_, skolem, unsafe)) | 145 | .map(new RDFoxClassExprConverter(_, skolem, unsafe)) |
diff --git a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala b/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala index 340fa90..6eecdbf 100644 --- a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala +++ b/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala | |||
| @@ -3,7 +3,7 @@ package rsacomb | |||
| 3 | import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty} | 3 | import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty} |
| 4 | import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx | 4 | import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx |
| 5 | 5 | ||
| 6 | import tech.oxfordsemantic.jrdfox.logic.{Atom, Term, IRI, Variable, Literal} | 6 | import tech.oxfordsemantic.jrdfox.logic.{Atom, Term, IRI, Literal} |
| 7 | 7 | ||
| 8 | import rsacomb.SkolemStrategy | 8 | import rsacomb.SkolemStrategy |
| 9 | import org.semanticweb.owlapi.model.OWLObjectInverseOf | 9 | import org.semanticweb.owlapi.model.OWLObjectInverseOf |
diff --git a/src/main/scala/rsacomb/RDFoxUtil.scala b/src/main/scala/rsacomb/RDFoxUtil.scala index 4cefd83..9699fb4 100644 --- a/src/main/scala/rsacomb/RDFoxUtil.scala +++ b/src/main/scala/rsacomb/RDFoxUtil.scala | |||
| @@ -15,6 +15,10 @@ object RDFoxUtil { | |||
| 15 | IRI.create(iri.getIRIString()) | 15 | IRI.create(iri.getIRIString()) |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | implicit def owlapi2rdfox(iri: String): IRI = { | ||
| 19 | IRI.create(iri) | ||
| 20 | } | ||
| 21 | |||
| 18 | def openConnection( | 22 | def openConnection( |
| 19 | dataStore: String | 23 | dataStore: String |
| 20 | ): (ServerConnection, DataStoreConnection) = { | 24 | ): (ServerConnection, DataStoreConnection) = { |
| @@ -37,7 +41,7 @@ object RDFoxUtil { | |||
| 37 | (server, data) | 41 | (server, data) |
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | def query( | 44 | def submitQuery( |
| 41 | data: DataStoreConnection, | 45 | data: DataStoreConnection, |
| 42 | prefixes: Prefixes, | 46 | prefixes: Prefixes, |
| 43 | query: String | 47 | query: String |
diff --git a/src/main/scala/rsacomb/RSA.scala b/src/main/scala/rsacomb/RSA.scala index 79617c7..9125f9f 100644 --- a/src/main/scala/rsacomb/RSA.scala +++ b/src/main/scala/rsacomb/RSA.scala | |||
| @@ -8,6 +8,18 @@ import tech.oxfordsemantic.jrdfox.Prefixes | |||
| 8 | import tech.oxfordsemantic.jrdfox.logic.IRI | 8 | import tech.oxfordsemantic.jrdfox.logic.IRI |
| 9 | import org.semanticweb.owlapi.apibinding.OWLManager | 9 | import org.semanticweb.owlapi.apibinding.OWLManager |
| 10 | import org.semanticweb.owlapi.model.OWLOntology | 10 | import org.semanticweb.owlapi.model.OWLOntology |
| 11 | import rsacomb.RSAOntology | ||
| 12 | |||
| 13 | // Debug only | ||
| 14 | import tech.oxfordsemantic.jrdfox.logic.{ | ||
| 15 | Formula, | ||
| 16 | Atom, | ||
| 17 | Variable, | ||
| 18 | Query, | ||
| 19 | QueryType, | ||
| 20 | Conjunction | ||
| 21 | } | ||
| 22 | import scala.collection.JavaConverters._ | ||
| 11 | 23 | ||
| 12 | object RSA extends RSAOntology { | 24 | object RSA extends RSAOntology { |
| 13 | 25 | ||
| @@ -18,9 +30,30 @@ object RSA extends RSAOntology { | |||
| 18 | Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") | 30 | Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") |
| 19 | Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") | 31 | Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") |
| 20 | 32 | ||
| 21 | def internal(name: String): IRI = | 33 | // Counter used to implement a simple fresh variable generator |
| 34 | private var counter = -1; | ||
| 35 | |||
| 36 | def getFreshVariable(): Variable = { | ||
| 37 | counter += 1 | ||
| 38 | Variable.create(f"I$counter%03d") | ||
| 39 | } | ||
| 40 | |||
| 41 | val varX = Variable.create("X") | ||
| 42 | val varY = Variable.create("Y") | ||
| 43 | val varZ = Variable.create("Z") | ||
| 44 | val testAnswerVars = List[Variable](varX, varY, varZ).asJava; | ||
| 45 | val testFormula: Formula = | ||
| 46 | Conjunction.create( | ||
| 47 | Atom.rdf(varX, IRI.TOP_OBJECT_PROPERTY, varY), | ||
| 48 | Atom.rdf(varY, IRI.TOP_OBJECT_PROPERTY, varZ) | ||
| 49 | ) | ||
| 50 | val test_query = | ||
| 51 | Query.create(QueryType.SELECT, false, testAnswerVars, testFormula) | ||
| 52 | |||
| 53 | def internal(name: Any): IRI = | ||
| 22 | IRI.create( | 54 | IRI.create( |
| 23 | Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI + name | 55 | Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI |
| 56 | + name.toString | ||
| 24 | ) | 57 | ) |
| 25 | 58 | ||
| 26 | // TODO: move this somewhere else... maybe an OntoUtils class or something. | 59 | // TODO: move this somewhere else... maybe an OntoUtils class or something. |
diff --git a/src/main/scala/rsacomb/RSAAxiom.scala b/src/main/scala/rsacomb/RSAAxiom.scala index 9e9a016..50dc37e 100644 --- a/src/main/scala/rsacomb/RSAAxiom.scala +++ b/src/main/scala/rsacomb/RSAAxiom.scala | |||
| @@ -1,10 +1,23 @@ | |||
| 1 | package rsacomb | 1 | package rsacomb |
| 2 | 2 | ||
| 3 | /* Java imports */ | 3 | /* Java imports */ |
| 4 | import org.semanticweb.owlapi.model.{OWLAxiom,OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} | 4 | import org.semanticweb.owlapi.model.{ |
| 5 | import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression,OWLClass,OWLClassExpression,OWLObjectSomeValuesFrom,OWLObjectMaxCardinality} | 5 | OWLAxiom, |
| 6 | OWLSubClassOfAxiom, | ||
| 7 | OWLEquivalentClassesAxiom | ||
| 8 | } | ||
| 9 | import org.semanticweb.owlapi.model.{ | ||
| 10 | OWLObjectPropertyExpression, | ||
| 11 | OWLClass, | ||
| 12 | OWLClassExpression, | ||
| 13 | OWLObjectSomeValuesFrom, | ||
| 14 | OWLObjectMaxCardinality | ||
| 15 | } | ||
| 6 | import org.semanticweb.owlapi.model.ClassExpressionType | 16 | import org.semanticweb.owlapi.model.ClassExpressionType |
| 7 | import org.semanticweb.owlapi.model.{OWLAxiomVisitorEx,OWLClassExpressionVisitorEx} | 17 | import org.semanticweb.owlapi.model.{ |
| 18 | OWLAxiomVisitorEx, | ||
| 19 | OWLClassExpressionVisitorEx | ||
| 20 | } | ||
| 8 | 21 | ||
| 9 | /* Wrapper trait for the implicit class `RSAAxiom`. | 22 | /* Wrapper trait for the implicit class `RSAAxiom`. |
| 10 | */ | 23 | */ |
| @@ -16,10 +29,10 @@ trait RSAAxiom { | |||
| 16 | */ | 29 | */ |
| 17 | private sealed trait RSAAxiomType | 30 | private sealed trait RSAAxiomType |
| 18 | private object RSAAxiomType { | 31 | private object RSAAxiomType { |
| 19 | case object T3 extends RSAAxiomType // ∃R.A ⊑ B | 32 | case object T3 extends RSAAxiomType // ∃R.A ⊑ B |
| 20 | case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B | 33 | case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B |
| 21 | case object T4 extends RSAAxiomType // A ⊑ ≤1R.B | 34 | case object T4 extends RSAAxiomType // A ⊑ ≤1R.B |
| 22 | case object T5 extends RSAAxiomType // A ⊑ ∃R.B | 35 | case object T5 extends RSAAxiomType // A ⊑ ∃R.B |
| 23 | } | 36 | } |
| 24 | 37 | ||
| 25 | /* Implements additional features on top of `OWLAxiom` from | 38 | /* Implements additional features on top of `OWLAxiom` from |
| @@ -32,20 +45,22 @@ trait RSAAxiom { | |||
| 32 | * In order to reason about role unsafety in Horn-ALCHOIQ | 45 | * In order to reason about role unsafety in Horn-ALCHOIQ |
| 33 | * ontologies we need to detect and filter axioms by their | 46 | * ontologies we need to detect and filter axioms by their |
| 34 | * "type". | 47 | * "type". |
| 35 | * | 48 | * |
| 36 | * This is a simple implementation following the Visitor | 49 | * This is a simple implementation following the Visitor |
| 37 | * pattern imposed by the OWLAPI. | 50 | * pattern imposed by the OWLAPI. |
| 38 | */ | 51 | */ |
| 39 | private class RSAAxiomTypeDetector(t: RSAAxiomType) | 52 | private class RSAAxiomTypeDetector(t: RSAAxiomType) |
| 40 | extends OWLAxiomVisitorEx[Boolean] | 53 | extends OWLAxiomVisitorEx[Boolean] { |
| 41 | { | 54 | override def visit(axiom: OWLSubClassOfAxiom): Boolean = { |
| 42 | override | ||
| 43 | def visit(axiom: OWLSubClassOfAxiom): Boolean = { | ||
| 44 | val sub = axiom.getSubClass().getClassExpressionType() | 55 | val sub = axiom.getSubClass().getClassExpressionType() |
| 45 | val sup = axiom.getSuperClass().getClassExpressionType() | 56 | val sup = axiom.getSuperClass().getClassExpressionType() |
| 46 | t match { | 57 | t match { |
| 47 | case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B | 58 | case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B |
| 48 | axiom.isT5 && axiom.getSubClass().asInstanceOf[OWLObjectSomeValuesFrom].getFiller.isOWLThing | 59 | axiom.isT3 && axiom |
| 60 | .getSubClass() | ||
| 61 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
| 62 | .getFiller | ||
| 63 | .isOWLThing | ||
| 49 | case RSAAxiomType.T3 => // ∃R.A ⊑ B | 64 | case RSAAxiomType.T3 => // ∃R.A ⊑ B |
| 50 | sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS | 65 | sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS |
| 51 | case RSAAxiomType.T4 => // A ⊑ ≤1R.B | 66 | case RSAAxiomType.T4 => // A ⊑ ≤1R.B |
| @@ -55,13 +70,12 @@ trait RSAAxiom { | |||
| 55 | } | 70 | } |
| 56 | } | 71 | } |
| 57 | 72 | ||
| 58 | override | 73 | override def visit(axiom: OWLEquivalentClassesAxiom): Boolean = { |
| 59 | def visit(axiom: OWLEquivalentClassesAxiom): Boolean = { | ||
| 60 | // TODO | 74 | // TODO |
| 61 | false | 75 | false |
| 62 | } | 76 | } |
| 63 | 77 | ||
| 64 | def doDefault(axiom : OWLAxiom): Boolean = false | 78 | def doDefault(axiom: OWLAxiom): Boolean = false |
| 65 | } | 79 | } |
| 66 | 80 | ||
| 67 | private def isOfType(t: RSAAxiomType): Boolean = { | 81 | private def isOfType(t: RSAAxiomType): Boolean = { |
| @@ -74,13 +88,13 @@ trait RSAAxiom { | |||
| 74 | def isT3: Boolean = isOfType(RSAAxiomType.T3) | 88 | def isT3: Boolean = isOfType(RSAAxiomType.T3) |
| 75 | def isT4: Boolean = isOfType(RSAAxiomType.T4) | 89 | def isT4: Boolean = isOfType(RSAAxiomType.T4) |
| 76 | def isT5: Boolean = isOfType(RSAAxiomType.T5) | 90 | def isT5: Boolean = isOfType(RSAAxiomType.T5) |
| 77 | 91 | ||
| 78 | /* Extracting ObjectPropertyExpressions from axioms | 92 | /* Extracting ObjectPropertyExpressions from axioms |
| 79 | * | 93 | * |
| 80 | * This extracts all ObjectPropertyExpressions from a given | 94 | * This extracts all ObjectPropertyExpressions from a given |
| 81 | * axiom. While the implementation is generic we use it on axioms | 95 | * axiom. While the implementation is generic we use it on axioms |
| 82 | * of specific types (see above). | 96 | * of specific types (see above). |
| 83 | * | 97 | * |
| 84 | * NOTE: it is not possible to use the `objectPropertyInSignature` | 98 | * NOTE: it is not possible to use the `objectPropertyInSignature` |
| 85 | * method of `OWLAxiom` because it returns all "role names" involved | 99 | * method of `OWLAxiom` because it returns all "role names" involved |
| 86 | * in the signature of an axiom. In particular we won't get the inverse | 100 | * in the signature of an axiom. In particular we won't get the inverse |
| @@ -88,55 +102,61 @@ trait RSAAxiom { | |||
| 88 | * itself instead). | 102 | * itself instead). |
| 89 | */ | 103 | */ |
| 90 | private class RSAAxiomRoleExtractor() | 104 | private class RSAAxiomRoleExtractor() |
| 91 | extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] | 105 | extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] { |
| 92 | { | ||
| 93 | 106 | ||
| 94 | private class RSAExprRoleExtractor() | 107 | private class RSAExprRoleExtractor() |
| 95 | extends OWLClassExpressionVisitorEx[List[OWLObjectPropertyExpression]] | 108 | extends OWLClassExpressionVisitorEx[ |
| 96 | { | 109 | List[OWLObjectPropertyExpression] |
| 97 | override | 110 | ] { |
| 98 | def visit(expr: OWLObjectSomeValuesFrom): List[OWLObjectPropertyExpression] = | 111 | override def visit( |
| 112 | expr: OWLObjectSomeValuesFrom | ||
| 113 | ): List[OWLObjectPropertyExpression] = | ||
| 99 | List(expr.getProperty) | 114 | List(expr.getProperty) |
| 100 | 115 | ||
| 101 | override | 116 | override def visit( |
| 102 | def visit(expr: OWLObjectMaxCardinality): List[OWLObjectPropertyExpression] = | 117 | expr: OWLObjectMaxCardinality |
| 118 | ): List[OWLObjectPropertyExpression] = | ||
| 103 | List(expr.getProperty) | 119 | List(expr.getProperty) |
| 104 | 120 | ||
| 105 | /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However | 121 | /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However |
| 106 | * if missing, the code throws a `NullPointerException`. It seems like, for some | 122 | * if missing, the code throws a `NullPointerException`. It seems like, for some |
| 107 | * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as | 123 | * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as |
| 108 | * stated in the JavaDocs. | 124 | * stated in the JavaDocs. |
| 109 | */ | 125 | */ |
| 110 | override | 126 | override def visit(expr: OWLClass): List[OWLObjectPropertyExpression] = |
| 111 | def visit(expr: OWLClass): List[OWLObjectPropertyExpression] = | ||
| 112 | List() | 127 | List() |
| 113 | 128 | ||
| 114 | def doDefault(expr: OWLClassExpression): List[OWLObjectPropertyExpression] = | 129 | def doDefault( |
| 130 | expr: OWLClassExpression | ||
| 131 | ): List[OWLObjectPropertyExpression] = | ||
| 115 | List() | 132 | List() |
| 116 | } | 133 | } |
| 117 | 134 | ||
| 118 | override | 135 | override def visit( |
| 119 | def visit(axiom: OWLSubClassOfAxiom): List[OWLObjectPropertyExpression] = { | 136 | axiom: OWLSubClassOfAxiom |
| 137 | ): List[OWLObjectPropertyExpression] = { | ||
| 120 | val visitor = new RSAExprRoleExtractor() | 138 | val visitor = new RSAExprRoleExtractor() |
| 121 | val sub = axiom.getSubClass.accept(visitor) | 139 | val sub = axiom.getSubClass.accept(visitor) |
| 122 | val sup = axiom.getSuperClass.accept(visitor) | 140 | val sup = axiom.getSuperClass.accept(visitor) |
| 123 | sub ++ sup | 141 | sub ++ sup |
| 124 | } | 142 | } |
| 125 | 143 | ||
| 126 | override | 144 | override def visit( |
| 127 | def visit(axiom: OWLEquivalentClassesAxiom): List[OWLObjectPropertyExpression] = { | 145 | axiom: OWLEquivalentClassesAxiom |
| 146 | ): List[OWLObjectPropertyExpression] = { | ||
| 128 | // TODO | 147 | // TODO |
| 129 | List() | 148 | List() |
| 130 | } | 149 | } |
| 131 | 150 | ||
| 132 | def doDefault(axiom : OWLAxiom): List[OWLObjectPropertyExpression] = List() | 151 | def doDefault(axiom: OWLAxiom): List[OWLObjectPropertyExpression] = List() |
| 133 | } | 152 | } |
| 134 | 153 | ||
| 135 | /* Exposed methods */ | 154 | /* Exposed methods */ |
| 136 | def objectPropertyExpressionsInSignature: List[OWLObjectPropertyExpression] = { | 155 | def objectPropertyExpressionsInSignature |
| 156 | : List[OWLObjectPropertyExpression] = { | ||
| 137 | val visitor = new RSAAxiomRoleExtractor() | 157 | val visitor = new RSAAxiomRoleExtractor() |
| 138 | axiom.accept(visitor) | 158 | axiom.accept(visitor) |
| 139 | } | 159 | } |
| 140 | } | 160 | } |
| 141 | 161 | ||
| 142 | } // trait RSAAxiom \ No newline at end of file | 162 | } // trait RSAAxiom |
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala index 3d9210e..efd6e7f 100644 --- a/src/main/scala/rsacomb/RSAOntology.scala +++ b/src/main/scala/rsacomb/RSAOntology.scala | |||
| @@ -19,6 +19,7 @@ import scalax.collection.GraphEdge.UnDiEdge | |||
| 19 | 19 | ||
| 20 | /* Debug only */ | 20 | /* Debug only */ |
| 21 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer | 21 | import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer |
| 22 | import tech.oxfordsemantic.jrdfox.logic._ | ||
| 22 | 23 | ||
| 23 | /* Wrapper trait for the implicit class `RSAOntology`. | 24 | /* Wrapper trait for the implicit class `RSAOntology`. |
| 24 | */ | 25 | */ |
| @@ -27,7 +28,9 @@ trait RSAOntology { | |||
| 27 | /* Implements additional features to reason about RSA ontologies | 28 | /* Implements additional features to reason about RSA ontologies |
| 28 | * on top of `OWLOntology` from the OWLAPI. | 29 | * on top of `OWLOntology` from the OWLAPI. |
| 29 | */ | 30 | */ |
| 30 | implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom { | 31 | implicit class RSAOntology(ontology: OWLOntology) |
| 32 | extends RSAAxiom | ||
| 33 | with RDFTriple { | ||
| 31 | 34 | ||
| 32 | /* Steps for RSA check | 35 | /* Steps for RSA check |
| 33 | * 1) convert ontology axioms into LP rules | 36 | * 1) convert ontology axioms into LP rules |
| @@ -60,7 +63,7 @@ trait RSAOntology { | |||
| 60 | val datalog = for { | 63 | val datalog = for { |
| 61 | axiom <- axioms | 64 | axiom <- axioms |
| 62 | visitor = new RDFoxAxiomConverter( | 65 | visitor = new RDFoxAxiomConverter( |
| 63 | Variable.create("x"), | 66 | RSA.getFreshVariable(), |
| 64 | SkolemStrategy.ConstantRSA(axiom.toString), | 67 | SkolemStrategy.ConstantRSA(axiom.toString), |
| 65 | unsafe | 68 | unsafe |
| 66 | ) | 69 | ) |
| @@ -163,8 +166,9 @@ trait RSAOntology { | |||
| 163 | } yield role1 | 166 | } yield role1 |
| 164 | 167 | ||
| 165 | /* TODO: We should be able to avoid this last conversion to List. | 168 | /* TODO: We should be able to avoid this last conversion to List. |
| 166 | * Maybe we should just move everything to Sets instead of Lists, since | 169 | * Maybe we should just move everything to Sets instead of Lists, |
| 167 | * they have a more straightforward conversion from Java collections. | 170 | * since they have a more straightforward conversion from Java |
| 171 | * collections. | ||
| 168 | */ | 172 | */ |
| 169 | (unsafe1 ++ unsafe2).toList | 173 | (unsafe1 ++ unsafe2).toList |
| 170 | } | 174 | } |
| @@ -184,6 +188,99 @@ trait RSAOntology { | |||
| 184 | Graph(edges: _*) | 188 | Graph(edges: _*) |
| 185 | } | 189 | } |
| 186 | 190 | ||
| 191 | def getFilteringProgram(query: Query): List[Rule] = { | ||
| 192 | |||
| 193 | // Import implicit conversion to RDFox IRI | ||
| 194 | import RDFoxUtil._ | ||
| 195 | |||
| 196 | sealed trait Reified; | ||
| 197 | case class ReifiedHead(bind: BindAtom, atoms: List[Atom]) extends Reified | ||
| 198 | case class ReifiedBody(atoms: List[Atom]) extends Reified | ||
| 199 | case class Unaltered(formula: BodyFormula) extends Reified | ||
| 200 | |||
| 201 | def getBindAtom(atom: Atom): BindAtom = { | ||
| 202 | // TODO: We need to implement another way to introduce fresh | ||
| 203 | // variables. | ||
| 204 | val newvar = RSA.getFreshVariable() | ||
| 205 | val name = | ||
| 206 | Literal.create(atom.getTupleTableName.getIRI, Datatype.XSD_STRING) | ||
| 207 | val args = atom | ||
| 208 | .getArguments() | ||
| 209 | .asScala | ||
| 210 | .toSeq | ||
| 211 | .prepended(name) | ||
| 212 | BindAtom.create( | ||
| 213 | BuiltinFunctionCall | ||
| 214 | .create("SKOLEM", args: _*), | ||
| 215 | newvar | ||
| 216 | ) | ||
| 217 | } | ||
| 218 | |||
| 219 | def reifyAtom(atom: Atom, variable: Variable): List[Atom] = { | ||
| 220 | def iri(i: Int) = atom.getTupleTableName().getIRI() ++ s"_$i" | ||
| 221 | atom | ||
| 222 | .getArguments() | ||
| 223 | .asScala | ||
| 224 | .zipWithIndex | ||
| 225 | .map { case (t, i) => Atom.rdf(variable, iri(i), t) } | ||
| 226 | .toList | ||
| 227 | } | ||
| 228 | |||
| 229 | def reify( | ||
| 230 | formula: BodyFormula, | ||
| 231 | head: Boolean | ||
| 232 | ): Reified = { | ||
| 233 | def default[A <: BodyFormula](x: A) = Unaltered(x) | ||
| 234 | formula match { | ||
| 235 | case a: Atom => { | ||
| 236 | if (!a.isRdfTriple) { | ||
| 237 | if (head) { | ||
| 238 | val b = getBindAtom(a) | ||
| 239 | ReifiedHead(b, reifyAtom(a, b.getBoundVariable)) | ||
| 240 | } else { | ||
| 241 | val varA = RSA.getFreshVariable() | ||
| 242 | ReifiedBody(reifyAtom(a, varA)) | ||
| 243 | } | ||
| 244 | } else { | ||
| 245 | default(a) | ||
| 246 | } | ||
| 247 | } | ||
| 248 | case a => default(a) | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | def skolemizeRule(rule: Rule): Rule = { | ||
| 253 | // Rule body | ||
| 254 | val body = | ||
| 255 | rule.getBody.asScala.map(reify(_, false)).flatMap { | ||
| 256 | case ReifiedHead(_, _) => List(); /* handle impossible case */ | ||
| 257 | case ReifiedBody(x) => x; | ||
| 258 | case Unaltered(x) => List(x) | ||
| 259 | } | ||
| 260 | // Rule head | ||
| 261 | val reified = rule.getHead.asScala.map(reify(_, true)) | ||
| 262 | val skols = reified.flatMap { | ||
| 263 | case ReifiedHead(x, _) => Some(x); | ||
| 264 | case ReifiedBody(_) => None; /* handle impossible case */ | ||
| 265 | case Unaltered(_) => None | ||
| 266 | } | ||
| 267 | val head = reified.flatMap { | ||
| 268 | case ReifiedHead(_, x) => x; | ||
| 269 | case ReifiedBody(_) => List(); /* handle impossible case */ | ||
| 270 | case Unaltered(x) => | ||
| 271 | List(x.asInstanceOf[Atom]) /* Can we do better that a cast? */ | ||
| 272 | } | ||
| 273 | Rule.create(head.asJava, (skols ++ body).asJava) | ||
| 274 | } | ||
| 275 | |||
| 276 | // DEBUG | ||
| 277 | val rules = FilteringProgram(query, List()).rules | ||
| 278 | println("FILTERING PROGRAM:") | ||
| 279 | rules.map(skolemizeRule(_)).foreach(println(_)) | ||
| 280 | |||
| 281 | List() | ||
| 282 | } | ||
| 283 | |||
| 187 | } // implicit class RSAOntology | 284 | } // implicit class RSAOntology |
| 188 | 285 | ||
| 189 | } // trait RSAOntology | 286 | } // trait RSAOntology |
