diff options
Diffstat (limited to 'src/main/scala/rsacomb/RSAOntology.scala')
| -rw-r--r-- | src/main/scala/rsacomb/RSAOntology.scala | 165 |
1 files changed, 163 insertions, 2 deletions
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala index 3d9210e..9d52612 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 | */ |
| @@ -163,8 +164,9 @@ trait RSAOntology { | |||
| 163 | } yield role1 | 164 | } yield role1 |
| 164 | 165 | ||
| 165 | /* TODO: We should be able to avoid this last conversion to List. | 166 | /* 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 | 167 | * Maybe we should just move everything to Sets instead of Lists, |
| 167 | * they have a more straightforward conversion from Java collections. | 168 | * since they have a more straightforward conversion from Java |
| 169 | * collections. | ||
| 168 | */ | 170 | */ |
| 169 | (unsafe1 ++ unsafe2).toList | 171 | (unsafe1 ++ unsafe2).toList |
| 170 | } | 172 | } |
| @@ -184,6 +186,165 @@ trait RSAOntology { | |||
| 184 | Graph(edges: _*) | 186 | Graph(edges: _*) |
| 185 | } | 187 | } |
| 186 | 188 | ||
| 189 | def getFilteringProgram(query: Query): List[Rule] = { | ||
| 190 | |||
| 191 | // Import implicit conversion to RDFox IRI | ||
| 192 | import RDFoxUtil._ | ||
| 193 | |||
| 194 | sealed trait Reified; | ||
| 195 | case class ReifiedHead(bind: BindAtom, atoms: List[Atom]) extends Reified | ||
| 196 | case class ReifiedBody(atoms: List[Atom]) extends Reified | ||
| 197 | case class Unaltered(formula: BodyFormula) extends Reified | ||
| 198 | |||
| 199 | def getBindAtom(atom: Atom): BindAtom = { | ||
| 200 | // TODO: We need to implement another way to introduce fresh | ||
| 201 | // variables. | ||
| 202 | val varA = Variable.create("A") | ||
| 203 | val args = atom | ||
| 204 | .getArguments() | ||
| 205 | .asScala | ||
| 206 | .toSeq | ||
| 207 | //.prepended(atom.getTupleTableName.getIRI) | ||
| 208 | BindAtom.create( | ||
| 209 | BuiltinFunctionCall | ||
| 210 | .create("SKOLEM", args: _*), | ||
| 211 | varA | ||
| 212 | ) | ||
| 213 | } | ||
| 214 | |||
| 215 | def reifyAtom(atom: Atom, variable: Variable): List[Atom] = { | ||
| 216 | def iri(i: Int) = atom.getTupleTableName().getIRI() ++ s"_$i" | ||
| 217 | atom | ||
| 218 | .getArguments() | ||
| 219 | .asScala | ||
| 220 | .zipWithIndex | ||
| 221 | .map { case (t, i) => Atom.rdf(variable, iri(i), t) } | ||
| 222 | .toList | ||
| 223 | } | ||
| 224 | |||
| 225 | // Is this the best way to determine if an atom is an RDF triple? | ||
| 226 | // Note that we can't use `getNumberOfArguments()` because is not | ||
| 227 | // "consistent": | ||
| 228 | // - for an atom created with `rdf(<term1>, <term2>, <term3>)`, | ||
| 229 | // `getNumberOfArguments` returns 3 | ||
| 230 | // - for an atom created with `Atom.create(<tupletablename>, <term1>, | ||
| 231 | // <term2>, <term3>)`, `getNumberOfArguments()` returns 3 | ||
| 232 | // | ||
| 233 | // This is probably because `Atom.rdf(...) is implemented as: | ||
| 234 | // ```scala | ||
| 235 | // def rdf(term1: Term, term2: Term, term3: Term): Atom = | ||
| 236 | // Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3) | ||
| 237 | // ``` | ||
| 238 | def isRdfTriple(atom: Atom): Boolean = | ||
| 239 | atom.getTupleTableName.getIRI.equals("internal:triple") | ||
| 240 | |||
| 241 | def reify( | ||
| 242 | formula: BodyFormula, | ||
| 243 | head: Boolean | ||
| 244 | ): Reified = { | ||
| 245 | def default[A <: BodyFormula](x: A) = Unaltered(x) | ||
| 246 | formula match { | ||
| 247 | case a: Atom => { | ||
| 248 | if (!isRdfTriple(a)) { | ||
| 249 | if (head) { | ||
| 250 | val b = getBindAtom(a) | ||
| 251 | ReifiedHead(b, reifyAtom(a, b.getBoundVariable)) | ||
| 252 | } else { | ||
| 253 | val varA = Variable.create("A") | ||
| 254 | ReifiedBody(reifyAtom(a, varA)) | ||
| 255 | } | ||
| 256 | } else { | ||
| 257 | default(a) | ||
| 258 | } | ||
| 259 | } | ||
| 260 | case a => default(a) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | def skolemizeRule(rule: Rule): Rule = { | ||
| 265 | // Rule body | ||
| 266 | val body = | ||
| 267 | rule.getBody.asScala.map(reify(_, false)).flatMap { | ||
| 268 | case ReifiedHead(_, _) => List(); /* handle impossible case */ | ||
| 269 | case ReifiedBody(x) => x; | ||
| 270 | case Unaltered(x) => List(x) | ||
| 271 | } | ||
| 272 | // Rule head | ||
| 273 | val reified = rule.getHead.asScala.map(reify(_, true)) | ||
| 274 | val skols = reified.flatMap { | ||
| 275 | case ReifiedHead(x, _) => Some(x); | ||
| 276 | case ReifiedBody(_) => None; /* handle impossible case */ | ||
| 277 | case Unaltered(_) => None | ||
| 278 | } | ||
| 279 | val head = reified.flatMap { | ||
| 280 | case ReifiedHead(_, x) => x; | ||
| 281 | case ReifiedBody(_) => List(); /* handle impossible case */ | ||
| 282 | case Unaltered(x) => | ||
| 283 | List(x.asInstanceOf[Atom]) /* Can we do better that a cast? */ | ||
| 284 | } | ||
| 285 | Rule.create(head.asJava, (skols ++ body).asJava) | ||
| 286 | } | ||
| 287 | |||
| 288 | def formulaToRuleBody(body: Formula): List[BodyFormula] = { | ||
| 289 | body match { | ||
| 290 | case a: BodyFormula => List(a); | ||
| 291 | case a: Conjunction => | ||
| 292 | a.getConjuncts().asScala.toList.flatMap(formulaToRuleBody(_)); | ||
| 293 | case _ => List() /* We don't handle this for now */ | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | val body = formulaToRuleBody(query.getQueryFormula) | ||
| 298 | val vars: List[Term] = query.getAnswerVariables.asScala.toList | ||
| 299 | def id(t1: Term, t2: Term) = | ||
| 300 | Atom.create( | ||
| 301 | TupleTableName.create("http://127.0.0.1/ID"), | ||
| 302 | vars.appendedAll(List(t1, t2)).asJava | ||
| 303 | ) | ||
| 304 | val qm = Atom.create(TupleTableName.create("QM"), vars.asJava) | ||
| 305 | |||
| 306 | /* Filtering program */ | ||
| 307 | val rule1 = Rule.create(qm, body.asJava) | ||
| 308 | val rule3a = | ||
| 309 | for ((v, i) <- vars.zipWithIndex) | ||
| 310 | yield Rule.create( | ||
| 311 | id( | ||
| 312 | IRI.create(s"http://127.0.0.1/$i"), | ||
| 313 | IRI.create(s"http://127.0.0.1/$i") | ||
| 314 | ), | ||
| 315 | List( | ||
| 316 | qm, | ||
| 317 | Negation.create( | ||
| 318 | Atom.rdf(v, IRI.RDF_TYPE, IRI.create("http://127.0.0.1/NI")) | ||
| 319 | ) | ||
| 320 | ).asJava | ||
| 321 | ) | ||
| 322 | val rule3b = Rule.create( | ||
| 323 | id(Variable.create("V"), Variable.create("U")), | ||
| 324 | id(Variable.create("U"), Variable.create("V")) | ||
| 325 | ) | ||
| 326 | val rule3c = Rule.create( | ||
| 327 | id(Variable.create("U"), Variable.create("W")), | ||
| 328 | List[BodyFormula]( | ||
| 329 | id(Variable.create("U"), Variable.create("V")), | ||
| 330 | id(Variable.create("V"), Variable.create("W")) | ||
| 331 | ).asJava | ||
| 332 | ) | ||
| 333 | |||
| 334 | var rules: List[Rule] = | ||
| 335 | List.empty | ||
| 336 | .prepended(rule3c) | ||
| 337 | .prepended(rule3b) | ||
| 338 | .prependedAll(rule3a) | ||
| 339 | .prepended(rule1) | ||
| 340 | |||
| 341 | // DEBUG | ||
| 342 | println("FILTERING PROGRAM:") | ||
| 343 | rules.map(skolemizeRule(_)).foreach(println(_)) | ||
| 344 | |||
| 345 | List() | ||
| 346 | } | ||
| 347 | |||
| 187 | } // implicit class RSAOntology | 348 | } // implicit class RSAOntology |
| 188 | 349 | ||
| 189 | } // trait RSAOntology | 350 | } // trait RSAOntology |
