From a72e44139897052eb83fe464fca94489e8f80092 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Wed, 2 Dec 2020 16:44:14 +0000 Subject: Adapt canonical model computation to new RDFox converter When it comes to the canonical model, now facts are imported as facts in RDFox. --- .../scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala | 133 ++++++++++----------- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 27 ++--- .../ox/cs/rsacomb/converter/RDFoxConverter.scala | 63 +++++----- 3 files changed, 105 insertions(+), 118 deletions(-) (limited to 'src') 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 0f3b16a..bcc336a 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/CanonicalModel.scala @@ -3,9 +3,10 @@ package uk.ac.ox.cs.rsacomb import org.semanticweb.owlapi.model.{OWLObjectInverseOf, OWLObjectProperty} import org.semanticweb.owlapi.model.{ OWLClass, + OWLLogicalAxiom, // OWLObjectProperty, OWLSubObjectPropertyOfAxiom, - // OWLObjectPropertyExpression, + OWLObjectPropertyExpression, OWLObjectSomeValuesFrom, OWLSubClassOfAxiom } @@ -25,10 +26,11 @@ import tech.oxfordsemantic.jrdfox.logic.expression.{ import uk.ac.ox.cs.rsacomb.converter.{ SkolemStrategy, - RDFoxAxiomConverter, - RDFoxPropertyExprConverter + RDFoxConverter + // RDFoxAxiomConverter, + // RDFoxPropertyExprConverter } -import uk.ac.ox.cs.rsacomb.suffix.{Empty, Forward, Backward, Inverse} +import uk.ac.ox.cs.rsacomb.suffix._ import uk.ac.ox.cs.rsacomb.util.RSA class CanonicalModel(val ontology: RSAOntology) { @@ -107,22 +109,28 @@ class CanonicalModel(val ontology: RSAOntology) { ) } - val rules: List[Rule] = { + val (facts, rules): (List[TupleTableAtom], List[Rule]) = { // Compute rules from ontology axioms - val rules = ontology.axioms.flatMap(_.accept(RuleGenerator)) - // Return full set of rules - rules ::: rolesAdditionalRules ::: topAxioms ::: equalityAxioms + val (facts, rules) = { + val term = RSAOntology.genFreshVariable() + val unsafe = ontology.unsafeRoles + val skolem = SkolemStrategy.None + val suffix = Empty + ontology.axioms + .map(CanonicalModelConverter.convert(_, term, unsafe, skolem, suffix)) + .unzip + } + ( + facts.flatten, + rolesAdditionalRules ::: topAxioms ::: equalityAxioms ::: rules.flatten + ) } - object RuleGenerator - extends RDFoxAxiomConverter( - Variable.create("X"), - ontology.unsafeRoles, - SkolemStrategy.None, - Empty - ) { + object CanonicalModelConverter extends RDFoxConverter { - private def rules1(axiom: OWLSubClassOfAxiom): List[Rule] = { + private def rules1( + axiom: OWLSubClassOfAxiom + ): Result = { val unfold = ontology.unfold(axiom).toList // Fresh Variables val v0 = RSA("v0_" ++ axiom.hashed) @@ -134,13 +142,9 @@ class CanonicalModel(val ontology: RSAOntology) { TupleTableAtom.rdf(varX, IRI.RDF_TYPE, cls) } val roleRf: TupleTableAtom = { - val visitor = - new RDFoxPropertyExprConverter(varX, v0, Forward) - axiom.getSuperClass - .asInstanceOf[OWLObjectSomeValuesFrom] - .getProperty - .accept(visitor) - .head + val prop = + axiom.getSuperClass.asInstanceOf[OWLObjectSomeValuesFrom].getProperty + super.convert(prop, varX, v0, Forward) } val atomB: TupleTableAtom = { val cls = axiom.getSuperClass @@ -154,12 +158,12 @@ class CanonicalModel(val ontology: RSAOntology) { // returning facts as `Rule`s with true body. While this is correct // there is an easier way to import facts into RDFox. Are we able to // do that? - val facts = unfold.map(x => Rule.create(RSA.In(x))) + val facts = unfold map RSA.In val rules = List( Rule.create(roleRf, atomA, RSA.NotIn(varX)), Rule.create(atomB, atomA, RSA.NotIn(varX)) ) - facts ++ rules + (facts, rules) } private def rules2(axiom: OWLSubClassOfAxiom): List[Rule] = { @@ -177,11 +181,8 @@ class CanonicalModel(val ontology: RSAOntology) { val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls) } - def roleRf(t1: Term, t2: Term): TupleTableAtom = { - val visitor = - new RDFoxPropertyExprConverter(t1, t2, Forward) - roleR.accept(visitor).head - } + def roleRf(t1: Term, t2: Term): TupleTableAtom = + super.convert(roleR, t1, t2, Forward) def atomB(t: Term): TupleTableAtom = { val cls = axiom.getSuperClass .asInstanceOf[OWLObjectSomeValuesFrom] @@ -215,11 +216,8 @@ class CanonicalModel(val ontology: RSAOntology) { val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls) } - def roleRf(t: Term): TupleTableAtom = { - val visitor = - new RDFoxPropertyExprConverter(t, v1, Forward) - roleR.accept(visitor).head - } + def roleRf(t: Term): TupleTableAtom = + super.convert(roleR, t, v1, Forward) val atomB: TupleTableAtom = { val cls = axiom.getSuperClass .asInstanceOf[OWLObjectSomeValuesFrom] @@ -236,46 +234,37 @@ class CanonicalModel(val ontology: RSAOntology) { } } - override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = { - if (axiom.isT5) { - // TODO: get role in T5 axiom - // Assuming one role here - val role = axiom.objectPropertyExpressionsInSignature(0) - if (ontology.unsafeRoles contains role) { - val visitor = - new RDFoxAxiomConverter( - Variable.create("X"), - ontology.unsafeRoles, - SkolemStrategy.Standard(axiom.toString), - Forward - ) - axiom.accept(visitor) - } else { - rules1(axiom) ::: rules2(axiom) ::: rules3(axiom) + override def convert( + axiom: OWLLogicalAxiom, + term: Term, + unsafe: List[OWLObjectPropertyExpression], + skolem: SkolemStrategy, + suffix: RSASuffix + ): Result = + axiom match { + + case a: OWLSubClassOfAxiom if a.isT5 => { + val role = axiom.objectPropertyExpressionsInSignature(0) + if (unsafe contains role) { + val skolem = SkolemStrategy.Standard(a.toString) + super.convert(a, term, unsafe, skolem, Forward) + } else { + val (f1, r1) = rules1(a) + (f1, r1 ::: rules2(a) ::: rules3(a)) + } } - } else { - // Fallback to standard OWL to LP translation - super.visit(axiom) - } - } - override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = { - val varX = Variable.create("X") - val visitorF = new RDFoxAxiomConverter( - varX, - ontology.unsafeRoles, - SkolemStrategy.None, - Forward - ) - val visitorB = new RDFoxAxiomConverter( - varX, - ontology.unsafeRoles, - SkolemStrategy.None, - Backward - ) - axiom.accept(visitorB) ::: axiom.accept(visitorF) - } + case a: OWLSubObjectPropertyOfAxiom => { + val (factsF, rulesF) = + super.convert(a, term, unsafe, SkolemStrategy.None, Forward) + val (factsB, rulesB) = + super.convert(a, term, unsafe, SkolemStrategy.None, Backward) + (factsF ::: factsB, rulesF ::: rulesB) + } + case a => super.convert(a, term, unsafe, skolem, suffix) + + } } } 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 4dd554a..fef4cfa 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -7,7 +7,7 @@ import java.util.stream.{Collectors, Stream} import java.io.File import org.semanticweb.owlapi.apibinding.OWLManager import org.semanticweb.owlapi.util.OWLOntologyMerger -import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom} +import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom, OWLLogicalAxiom} import org.semanticweb.owlapi.model.{ OWLClass, OWLObjectProperty, @@ -82,30 +82,28 @@ object RSAOntology { class RSAOntology(val ontology: OWLOntology) { import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ + import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ // Gather TBox/RBox/ABox from original ontology - val tbox: List[OWLAxiom] = + val tbox: List[OWLLogicalAxiom] = ontology .tboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) - .asScala - .toList + .collect { case a: OWLLogicalAxiom => a } - val rbox: List[OWLAxiom] = + val rbox: List[OWLLogicalAxiom] = ontology .rboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) - .asScala - .toList + .collect { case a: OWLLogicalAxiom => a } - val abox: List[OWLAxiom] = + val abox: List[OWLLogicalAxiom] = ontology .aboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) - .asScala - .toList + .collect { case a: OWLLogicalAxiom => a } - val axioms: List[OWLAxiom] = abox ::: tbox ::: rbox + val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox /* Retrieve individuals in the original ontology */ @@ -267,8 +265,8 @@ class RSAOntology(val ontology: OWLOntology) { ): Graph[Resource, UnDiEdge] = { val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get - var edges: Seq[UnDiEdge[Resource]] = answers.map { - case Seq(n1, n2) => UnDiEdge(n1, n2) + var edges: Seq[UnDiEdge[Resource]] = answers.map { case Seq(n1, n2) => + UnDiEdge(n1, n2) } Graph(edges: _*) } @@ -310,7 +308,8 @@ class RSAOntology(val ontology: OWLOntology) { def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { import implicits.JavaCollections._ val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) - data.addRules(this.canonicalModel.rules) + RDFoxUtil.addRules(data, this.canonicalModel.rules) + RDFoxUtil.addFacts(data, this.canonicalModel.facts) data.addRules(this.filteringProgram(query).rules) val answers = RDFoxUtil .submitQuery( diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala index 9b2071e..7fd4dbe 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/converter/RDFoxConverter.scala @@ -57,7 +57,7 @@ import uk.ac.ox.cs.rsacomb.util.RSA * normalization procedure that will prevent errors or unexpected * results. */ -object RDFoxConverter { +trait RDFoxConverter { /** Simplify conversion between Java and Scala collections */ import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ @@ -85,7 +85,7 @@ object RDFoxConverter { * along with a set of atoms for the body of the rule (namely * `R(x,y), B(y), R(x,z), B(z)`). */ - private type Shards = (List[TupleTableAtom], List[BodyFormula]) + protected type Shards = (List[TupleTableAtom], List[BodyFormula]) /** Represent the result of the conversion of * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]]. @@ -93,7 +93,10 @@ object RDFoxConverter { * In general we have assertion returning (a collection of) atoms, * while other axioms that generate rules. */ - private type Result = Either[List[TupleTableAtom], List[Rule]] + protected type Result = (List[TupleTableAtom], List[Rule]) + protected def Result(): Result = (List(), List()) + protected def ResultF(atoms: List[TupleTableAtom]): Result = (atoms, List()) + protected def ResultR(rules: List[Rule]): Result = (List(), rules) /** Converts a * [[org.semanticweb.owlapi.model.OWLLogicalAxiom OWLLogicalAxiom]] @@ -145,35 +148,32 @@ object RDFoxConverter { val (sup, ext) = convert(a.getSuperClass, term, unsafe, skolem, suffix) val rule = Rule.create(sup, ext ::: sub) - Right(List(rule)) + ResultR(List(rule)) } // cannot be left // http://www.w3.org/TR/owl2-syntax/#Equivalent_Classes - case a: OWLEquivalentClassesAxiom => - Right( - a.asPairwiseAxioms - .flatMap(_.asOWLSubClassOfAxioms) - .map(convert(_, term, unsafe, skolem, suffix)) - .collect { case Right(rs) => rs } - .flatten - ) + case a: OWLEquivalentClassesAxiom => { + val (atoms, rules) = a.asPairwiseAxioms + .flatMap(_.asOWLSubClassOfAxioms) + .map(convert(_, term, unsafe, skolem, suffix)) + .unzip + (atoms.flatten, rules.flatten) + } case a: OWLEquivalentObjectPropertiesAxiom => { - Right( - a.asPairwiseAxioms - .flatMap(_.asSubObjectPropertyOfAxioms) - .map(convert(_, term, unsafe, skolem, suffix)) - .collect { case Right(rs) => rs } - .flatten - ) + val (atoms, rules) = a.asPairwiseAxioms + .flatMap(_.asSubObjectPropertyOfAxioms) + .map(convert(_, term, unsafe, skolem, suffix)) + .unzip + (atoms.flatten, rules.flatten) } case a: OWLSubObjectPropertyOfAxiom => { val term1 = RSAOntology.genFreshVariable() val body = convert(a.getSubProperty, term, term1, suffix) val head = convert(a.getSuperProperty, term, term1, suffix) - Right(List(Rule.create(head, body))) + ResultR(List(Rule.create(head, body))) } case a: OWLObjectPropertyDomainAxiom => @@ -183,19 +183,18 @@ object RDFoxConverter { val term1 = RSAOntology.genFreshVariable() val (res, ext) = convert(a.getRange, term, unsafe, skolem, suffix) val prop = convert(a.getProperty, term1, term, suffix) - Right(List(Rule.create(res, prop :: ext))) + ResultR(List(Rule.create(res, prop :: ext))) } case a: OWLDataPropertyDomainAxiom => convert(a.asOWLSubClassOfAxiom, term, unsafe, skolem, suffix) - case a: OWLInverseObjectPropertiesAxiom => - Right( - a.asSubObjectPropertyOfAxioms - .map(convert(_, term, unsafe, skolem, suffix)) - .collect { case Right(rs) => rs } - .flatten - ) + case a: OWLInverseObjectPropertiesAxiom => { + val (atoms, rules) = a.asSubObjectPropertyOfAxioms + .map(convert(_, term, unsafe, skolem, suffix)) + .unzip + (atoms.flatten, rules.flatten) + } case a: OWLClassAssertionAxiom => { val ind = a.getIndividual @@ -204,20 +203,20 @@ object RDFoxConverter { val cls = a.getClassExpression val (res, _) = convert(cls, i.getIRI, unsafe, SkolemStrategy.None, suffix) - Left(res) + ResultF(res) } - case _ => Left(List()) + case _ => Result() } } case a: OWLObjectPropertyAssertionAxiom => if (!a.getSubject.isNamed || !a.getObject.isNamed) - Left(List()) + Result() else { val subj = a.getSubject.asOWLNamedIndividual.getIRI val obj = a.getObject.asOWLNamedIndividual.getIRI val prop = convert(a.getProperty, subj, obj, suffix) - Left(List(prop)) + ResultF(List(prop)) } /** Catch-all case for all unhandled axiom types. */ -- cgit v1.2.3