From e932527e33b6f4c1634995224188b26d870d92b2 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 31 May 2021 15:06:47 +0100 Subject: Add scafolding for generic approximation support --- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 305 +++++++++++---------- 1 file changed, 162 insertions(+), 143 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 c7b3bf0..e048c28 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -64,6 +64,104 @@ import uk.ac.ox.cs.rsacomb.sparql._ import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} import uk.ac.ox.cs.rsacomb.util.Logger +object RSAUtil { + + implicit def axiomsToOntology(axioms: Seq[OWLAxiom]) = { + val manager = OWLManager.createOWLOntologyManager() + manager.createOntology(axioms.asJava) + } + + /** Compute the RSA dependency graph for a set of axioms + * + * @return a tuple containing the dependency graph and a map between + * the newly introduced constants and the corresponding input axioms. + * + * @note no check on the ontology language is performed since the + * construction of the dependency graph is computed regardless. The + * input axioms are assumed to be normalized. + */ + private def dependencyGraph( + axioms: Seq[OWLAxiom], + datafiles: Seq[File] + ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { + val unsafe = this.unsafeRoles + var nodemap = Map.empty[String, OWLAxiom] + + object RSAConverter extends RDFoxConverter { + + override def convert( + expr: OWLClassExpression, + term: Term, + unsafe: List[OWLObjectPropertyExpression], + skolem: SkolemStrategy, + suffix: RSASuffix + ): Shards = + (expr, skolem) match { + + case (e: OWLObjectSomeValuesFrom, c: Constant) => { + nodemap.update(c.iri.getIRI, c.axiom) + val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) + if (unsafe contains e.getProperty) + (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) + else + (RSA.PE(term, c.iri) :: res, ext) + } + + case (e: OWLDataSomeValuesFrom, c: Constant) => { + nodemap.update(c.iri.getIRI, c.axiom) + val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) + if (unsafe contains e.getProperty) + (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) + else + (RSA.PE(term, c.iri) :: res, ext) + } + + case _ => super.convert(expr, term, unsafe, skolem, suffix) + } + } + + /* Ontology convertion into LP rules */ + val term = RSAOntology.genFreshVariable() + val result = axioms.map(a => + RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) + ) + + val datalog = result.unzip + val facts = datalog._1.flatten + var rules = datalog._2.flatten + + /* Open connection with RDFox */ + val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph") + + /* Add additional built-in rules */ + val varX = Variable.create("X") + val varY = Variable.create("Y") + rules = Rule.create( + RSA.E(varX, varY), + RSA.PE(varX, varY), + RSA.U(varX), + RSA.U(varY) + ) :: rules + /* Load facts and rules from ontology */ + RDFoxUtil.addFacts(data, facts) + RDFoxUtil.addRules(data, rules) + /* Load data files */ + RDFoxUtil.addData(data, datafiles: _*) + + /* Build the graph */ + val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" + val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get + var edges: Seq[DiEdge[Resource]] = + answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 } + val graph = Graph(edges: _*) + + /* Close connection to RDFox */ + RDFoxUtil.closeConnection(server, data) + + (graph, nodemap) + } +} + object RSAOntology { /** Name of the RDFox data store used for CQ answering */ @@ -79,14 +177,63 @@ object RSAOntology { /** Manager instance to interface with OWLAPI */ val manager = OWLManager.createOWLOntologyManager() - def apply(ontology: File, data: File*): RSAOntology = + def apply( + ontofile: File, + datafiles: Seq[File], + approx: Option[Approximation] = None + ): RSAOntology = { + val ontology = manager.loadOntologyFromOntologyDocument(ontofile) + RSAOntology(ontology, datafiles, approx) + } + + def apply( + ontology: OWLOntology, + datafiles: Seq[File], + approx: Option[Approximation] = None + ): RSAOntology = { + val normalizer = new Normalizer() + + /** TBox axioms */ + var tbox: List[OWLLogicalAxiom] = + original + .tboxAxioms(Imports.INCLUDED) + .collect(Collectors.toList()) + .collect { case a: OWLLogicalAxiom => a } + .flatMap(normalizer.normalize) + + /** RBox axioms */ + var rbox: List[OWLLogicalAxiom] = + original + .rboxAxioms(Imports.INCLUDED) + .collect(Collectors.toList()) + .collect { case a: OWLLogicalAxiom => a } + .flatMap(normalizer.normalize) + + /** ABox axioms + * + * @note this represents only the set of assertions contained in the + * ontology file. Data files specified in `datafiles` are directly + * imported in RDFox due to performance issues when trying to import + * large data files via OWLAPI. + */ + var abox: List[OWLLogicalAxiom] = + original + .aboxAxioms(Imports.INCLUDED) + .collect(Collectors.toList()) + .collect { case a: OWLLogicalAxiom => a } + .flatMap(normalizer.normalize) + + /** Collection of logical axioms in the input ontology */ + var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox + new RSAOntology( - manager.loadOntologyFromOntologyDocument(ontology), - data: _* + approx match { + case Some(a) => a.approximate(axioms, datafiles) + case None => axioms + }, + datafiles ) - - def apply(ontology: OWLOntology, data: File*): RSAOntology = - new RSAOntology(ontology, data: _*) + } } /** Wrapper class for an ontology in RSA @@ -94,7 +241,7 @@ object RSAOntology { * @param ontology the input OWL2 ontology. * @param datafiles additinal data (treated as part of the ABox) */ -class RSAOntology(val original: OWLOntology, val datafiles: File*) { +class RSAOntology(val axioms: Seq[OWLAxiom], val datafiles: File*) { /** Simplify conversion between OWLAPI and RDFox concepts */ import implicits.RDFox._ @@ -104,49 +251,8 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { /** Set of axioms removed during the approximation to RSA */ private var removed: Seq[OWLAxiom] = Seq.empty - /** The normalizer normalizes the ontology and approximate it to - * Horn-ALCHOIQ. A further step is needed to obtain an RSA - * approximation of the input ontology `original`. - */ - private val normalizer = new Normalizer() - - /** TBox axioms */ - var tbox: List[OWLLogicalAxiom] = - original - .tboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** RBox axioms */ - var rbox: List[OWLLogicalAxiom] = - original - .rboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** ABox axioms - * - * @note this represents only the set of assertions contained in the - * ontology file. Data files specified in `datafiles` are directly - * imported in RDFox due to performance issues when trying to import - * large data files via OWLAPI. - */ - var abox: List[OWLLogicalAxiom] = - original - .aboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** Collection of logical axioms in the input ontology */ - var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox - /** Normalized Horn-ALCHOIQ ontology */ - val ontology = RSAOntology.manager.createOntology( - axioms.asInstanceOf[List[OWLAxiom]].asJava - ) + val ontology = RSAOntology.manager.createOntology(axioms.asJava) /** OWLAPI internal reasoner instantiated over the approximated ontology */ private val reasoner = @@ -170,7 +276,7 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { val concepts: List[OWLClass] = ontology.getClassesInSignature().asScala.toList val roles: List[OWLObjectPropertyExpression] = - (tbox ++ rbox) + axioms .flatMap(_.objectPropertyExpressionsInSignature) .distinct @@ -190,12 +296,12 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { /* Checking for unsafety condition (1) */ val unsafe1 = for { - axiom <- tbox + axiom <- axioms if axiom.isT5 role1 <- axiom.objectPropertyExpressionsInSignature roleSuper = role1 +: reasoner.superObjectProperties(role1) roleSuperInv = roleSuper.map(_.getInverseProperty) - axiom <- tbox + axiom <- axioms if axiom.isT3 && !axiom.isT3top role2 <- axiom.objectPropertyExpressionsInSignature if roleSuperInv contains role2 @@ -203,12 +309,12 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { /* Checking for unsafety condition (2) */ val unsafe2 = for { - axiom <- tbox + axiom <- axioms if axiom.isT5 role1 <- axiom.objectPropertyExpressionsInSignature roleSuper = role1 +: reasoner.superObjectProperties(role1) roleSuperInv = roleSuper.map(_.getInverseProperty) - axiom <- tbox + axiom <- axioms if axiom.isT4 role2 <- axiom.objectPropertyExpressionsInSignature if roleSuper.contains(role2) || roleSuperInv.contains(role2) @@ -217,93 +323,6 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { unsafe1 ++ unsafe2 } - /** Compute the RSA dependency graph - * - * This is used to approximate the input ontology to RSA. - * - * @return a tuple containing the dependency graph and a map between - * the constants newly introduced and the corresponding axioms in the - * ontology. - */ - private def dependencyGraph() - : (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { - val unsafe = this.unsafeRoles - var nodemap = Map.empty[String, OWLAxiom] - - object RSAConverter extends RDFoxConverter { - - override def convert( - expr: OWLClassExpression, - term: Term, - unsafe: List[OWLObjectPropertyExpression], - skolem: SkolemStrategy, - suffix: RSASuffix - ): Shards = - (expr, skolem) match { - - case (e: OWLObjectSomeValuesFrom, c: Constant) => { - nodemap.update(c.iri.getIRI, c.axiom) - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - if (unsafe contains e.getProperty) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) - else - (RSA.PE(term, c.iri) :: res, ext) - } - - case (e: OWLDataSomeValuesFrom, c: Constant) => { - nodemap.update(c.iri.getIRI, c.axiom) - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - if (unsafe contains e.getProperty) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) - else - (RSA.PE(term, c.iri) :: res, ext) - } - - case _ => super.convert(expr, term, unsafe, skolem, suffix) - } - } - - /* Ontology convertion into LP rules */ - val term = RSAOntology.genFreshVariable() - val result = axioms.map(a => - RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) - ) - - val datalog = result.unzip - val facts = datalog._1.flatten - var rules = datalog._2.flatten - - /* Open connection with RDFox */ - val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph") - - /* Add additional built-in rules */ - val varX = Variable.create("X") - val varY = Variable.create("Y") - rules = Rule.create( - RSA.E(varX, varY), - RSA.PE(varX, varY), - RSA.U(varX), - RSA.U(varY) - ) :: rules - /* Load facts and rules from ontology */ - RDFoxUtil.addFacts(data, facts) - RDFoxUtil.addRules(data, rules) - /* Load data files */ - RDFoxUtil.addData(data, datafiles: _*) - - /* Build the graph */ - val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" - val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get - var edges: Seq[DiEdge[Resource]] = - answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 } - val graph = Graph(edges: _*) - - /* Close connection to RDFox */ - RDFoxUtil.closeConnection(server, data) - - (graph, nodemap) - } - /** Approximate a Horn-ALCHOIQ ontology to RSA * * This is done by gathering those axioms that prevent the ontology @@ -653,7 +672,7 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { val conflR = this.confl(roleR) // We just need the TBox to find val terms = for { - axiom1 <- tbox + axiom1 <- axioms if axiom1.isT5 // We expect only one role coming out of a T5 axiom roleS <- axiom1.objectPropertyExpressionsInSignature @@ -693,7 +712,7 @@ class RSAOntology(val original: OWLOntology, val datafiles: File*) { ) Logger.print( s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology - .getLogicalAxiomCount(true)} (${tbox.length}/${rbox.length}/${abox.length})", + .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})", level ) Logger.print( -- cgit v1.2.3 From 5f9815c3f67114645593840a8648bffb1207b8d0 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 31 May 2021 16:58:15 +0100 Subject: Multiple fixes --- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 88 ++++++++++++---------- 1 file changed, 48 insertions(+), 40 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 e048c28..4d0f13d 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -50,13 +50,13 @@ import scala.collection.JavaConverters._ import scala.collection.mutable.{Set, Map} import scalax.collection.Graph import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ -import scalax.collection.GraphTraversal._ /* Debug only */ import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer import tech.oxfordsemantic.jrdfox.logic._ import org.semanticweb.owlapi.model.OWLObjectInverseOf +import uk.ac.ox.cs.rsacomb.approximation.Approximation import uk.ac.ox.cs.rsacomb.converter._ import uk.ac.ox.cs.rsacomb.filtering.{FilteringProgram, FilterType} import uk.ac.ox.cs.rsacomb.suffix._ @@ -66,10 +66,10 @@ import uk.ac.ox.cs.rsacomb.util.Logger object RSAUtil { - implicit def axiomsToOntology(axioms: Seq[OWLAxiom]) = { - val manager = OWLManager.createOWLOntologyManager() - manager.createOntology(axioms.asJava) - } + // implicit def axiomsToOntology(axioms: Seq[OWLAxiom]) = { + // val manager = OWLManager.createOWLOntologyManager() + // manager.createOntology(axioms.asJava) + // } /** Compute the RSA dependency graph for a set of axioms * @@ -81,10 +81,10 @@ object RSAUtil { * input axioms are assumed to be normalized. */ private def dependencyGraph( - axioms: Seq[OWLAxiom], - datafiles: Seq[File] + axioms: List[OWLLogicalAxiom], + datafiles: List[File] ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { - val unsafe = this.unsafeRoles + val unsafe = RSAOntology(axioms, datafiles).unsafeRoles var nodemap = Map.empty[String, OWLAxiom] object RSAConverter extends RDFoxConverter { @@ -164,6 +164,8 @@ object RSAUtil { object RSAOntology { + import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ + /** Name of the RDFox data store used for CQ answering */ private val DataStore = "answer_computation" @@ -177,10 +179,15 @@ object RSAOntology { /** Manager instance to interface with OWLAPI */ val manager = OWLManager.createOWLOntologyManager() + def apply( + axioms: List[OWLLogicalAxiom], + datafiles: List[File] + ): RSAOntology = new RSAOntology(axioms, datafiles: _*) + def apply( ontofile: File, - datafiles: Seq[File], - approx: Option[Approximation] = None + datafiles: List[File], + approx: Option[Approximation] ): RSAOntology = { val ontology = manager.loadOntologyFromOntologyDocument(ontofile) RSAOntology(ontology, datafiles, approx) @@ -188,14 +195,14 @@ object RSAOntology { def apply( ontology: OWLOntology, - datafiles: Seq[File], - approx: Option[Approximation] = None + datafiles: List[File], + approx: Option[Approximation] ): RSAOntology = { val normalizer = new Normalizer() /** TBox axioms */ var tbox: List[OWLLogicalAxiom] = - original + ontology .tboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } @@ -203,7 +210,7 @@ object RSAOntology { /** RBox axioms */ var rbox: List[OWLLogicalAxiom] = - original + ontology .rboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } @@ -217,7 +224,7 @@ object RSAOntology { * large data files via OWLAPI. */ var abox: List[OWLLogicalAxiom] = - original + ontology .aboxAxioms(Imports.INCLUDED) .collect(Collectors.toList()) .collect { case a: OWLLogicalAxiom => a } @@ -231,9 +238,10 @@ object RSAOntology { case Some(a) => a.approximate(axioms, datafiles) case None => axioms }, - datafiles + datafiles: _* ) } + } /** Wrapper class for an ontology in RSA @@ -241,7 +249,7 @@ object RSAOntology { * @param ontology the input OWL2 ontology. * @param datafiles additinal data (treated as part of the ABox) */ -class RSAOntology(val axioms: Seq[OWLAxiom], val datafiles: File*) { +class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { /** Simplify conversion between OWLAPI and RDFox concepts */ import implicits.RDFox._ @@ -697,28 +705,28 @@ class RSAOntology(val axioms: Seq[OWLAxiom], val datafiles: File*) { this.self(axiom) | this.cycle(axiom) /** Log normalization/approximation statistics */ - def statistics(level: Logger.Level = Logger.DEBUG): Unit = { - Logger.print( - s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}", - level - ) - Logger.print( - s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}", - level - ) - Logger.print( - s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}", - level - ) - Logger.print( - s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology - .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})", - level - ) - Logger.print( - s"Logical axioms discarded in RSA approximation: ${removed.length}", - level - ) - } + // def statistics(level: Logger.Level = Logger.DEBUG): Unit = { + // Logger.print( + // s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}", + // level + // ) + // Logger.print( + // s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}", + // level + // ) + // Logger.print( + // s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}", + // level + // ) + // Logger.print( + // s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology + // .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})", + // level + // ) + // Logger.print( + // s"Logical axioms discarded in RSA approximation: ${removed.length}", + // level + // ) + // } } // class RSAOntology -- cgit v1.2.3 From 6b4226c41ec2a2439fb44a312ccaff01769e8212 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Tue, 1 Jun 2021 11:20:39 +0100 Subject: Fix minor compilation errors --- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 130 +++++++++++---------- 1 file changed, 69 insertions(+), 61 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 4d0f13d..247b5d5 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -80,7 +80,7 @@ object RSAUtil { * construction of the dependency graph is computed regardless. The * input axioms are assumed to be normalized. */ - private def dependencyGraph( + def dependencyGraph( axioms: List[OWLLogicalAxiom], datafiles: List[File] ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { @@ -166,18 +166,23 @@ object RSAOntology { import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ + /** Manager instance to interface with OWLAPI */ + val manager = OWLManager.createOWLOntologyManager() + val factory = manager.getOWLDataFactory() + /** Name of the RDFox data store used for CQ answering */ private val DataStore = "answer_computation" - /** Simple fresh variable generator */ + /** Simple fresh variable/class generator */ private var counter = -1; def genFreshVariable(): Variable = { counter += 1 Variable.create(f"I$counter%05d") } - - /** Manager instance to interface with OWLAPI */ - val manager = OWLManager.createOWLOntologyManager() + def getFreshOWLClass(): OWLClass = { + counter += 1 + factory.getOWLClass(s"X$counter") + } def apply( axioms: List[OWLLogicalAxiom], @@ -254,13 +259,16 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { /** Simplify conversion between OWLAPI and RDFox concepts */ import implicits.RDFox._ import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom._ + + /** Simplify conversion between Java and Scala collections */ import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ /** Set of axioms removed during the approximation to RSA */ private var removed: Seq[OWLAxiom] = Seq.empty /** Normalized Horn-ALCHOIQ ontology */ - val ontology = RSAOntology.manager.createOntology(axioms.asJava) + val ontology = + RSAOntology.manager.createOntology((axioms: List[OWLAxiom]).asJava) /** OWLAPI internal reasoner instantiated over the approximated ontology */ private val reasoner = @@ -340,61 +348,61 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { * @param graph the graph used to compute the axioms to remove. * @param nodemap map from graph nodes to ontology axioms. */ - def toRSA(): RSAOntology = Logger.timed( - { - - /* Compute the dependency graph for the ontology */ - val (graph, nodemap) = this.dependencyGraph() - - /* Define node colors for the graph visit */ - sealed trait NodeColor - case object Unvisited extends NodeColor - case object Visited extends NodeColor - case object ToDelete extends NodeColor - - /* Keep track of node colors during graph visit */ - var color = Map.from[Resource, NodeColor]( - graph.nodes.toOuter.map(k => (k, Unvisited)) - ) - - for { - component <- graph.componentTraverser().map(_ to Graph) - edge <- component - .outerEdgeTraverser(component.nodes.head) - .withKind(BreadthFirst) - } yield { - val source = edge._1 - val target = edge._2 - color(source) match { - case Unvisited | Visited => { - color(target) match { - case Unvisited => - color(source) = Visited; - color(target) = Visited - case Visited => - color(source) = ToDelete - case ToDelete => - color(source) = Visited - } - } - case ToDelete => - } - } - - val toDelete = color.iterator.collect { case (resource: IRI, ToDelete) => - nodemap(resource.getIRI) - }.toSeq - - /* Remove axioms from approximated ontology */ - ontology.removeAxioms(toDelete: _*) - this.removed = toDelete - - /* Return RSA ontology */ - RSAOntology(ontology, datafiles: _*) - }, - "Horn-ALCHOIQ to RSA approximation:", - Logger.DEBUG - ) + // def toRSA(): RSAOntology = Logger.timed( + // { + + // /* Compute the dependency graph for the ontology */ + // val (graph, nodemap) = this.dependencyGraph() + + // /* Define node colors for the graph visit */ + // sealed trait NodeColor + // case object Unvisited extends NodeColor + // case object Visited extends NodeColor + // case object ToDelete extends NodeColor + + // /* Keep track of node colors during graph visit */ + // var color = Map.from[Resource, NodeColor]( + // graph.nodes.toOuter.map(k => (k, Unvisited)) + // ) + + // for { + // component <- graph.componentTraverser().map(_ to Graph) + // edge <- component + // .outerEdgeTraverser(component.nodes.head) + // .withKind(BreadthFirst) + // } yield { + // val source = edge._1 + // val target = edge._2 + // color(source) match { + // case Unvisited | Visited => { + // color(target) match { + // case Unvisited => + // color(source) = Visited; + // color(target) = Visited + // case Visited => + // color(source) = ToDelete + // case ToDelete => + // color(source) = Visited + // } + // } + // case ToDelete => + // } + // } + + // val toDelete = color.iterator.collect { case (resource: IRI, ToDelete) => + // nodemap(resource.getIRI) + // }.toSeq + + // /* Remove axioms from approximated ontology */ + // ontology.removeAxioms(toDelete: _*) + // this.removed = toDelete + + // /* Return RSA ontology */ + // RSAOntology(ontology, datafiles: _*) + // }, + // "Horn-ALCHOIQ to RSA approximation:", + // Logger.DEBUG + // ) // val edges1 = Seq('A ~> 'B, 'B ~> 'C, 'C ~> 'D, 'D ~> 'H, 'H ~> // 'G, 'G ~> 'F, 'E ~> 'A, 'E ~> 'F, 'B ~> 'E, 'F ~> 'G, 'B ~> 'F, // 'C ~> 'G, 'D ~> 'C, 'H ~> 'D) -- cgit v1.2.3 From 53646646f924887768688794aee46874ed194673 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Tue, 20 Jul 2021 15:09:20 +0100 Subject: Generalize dependency graph generation The code to generate the dependency graph has been moved in the companion object of the generic OWL 2 ontology wrapper Ontology. This signals that we could potentially build a dependency graph for any ontology (and not only RSA ontology). Moreover, a dependency graph can be build for an Ontology object or an arbitrary TBox and Abox. --- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 111 +++------------------ 1 file changed, 13 insertions(+), 98 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 bbbbcf3..9902fcd 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -71,108 +71,10 @@ object RSAUtil { // manager.createOntology(axioms.asJava) // } - /** Compute the RSA dependency graph for a set of axioms - * - * @return a tuple containing the dependency graph and a map between - * the newly introduced constants and the corresponding input axioms. - * - * @note no check on the ontology language is performed since the - * construction of the dependency graph is computed regardless. The - * input axioms are assumed to be normalized. - */ - def dependencyGraph( - axioms: List[OWLLogicalAxiom], - datafiles: List[File] - ): (Graph[Resource, DiEdge], Map[String, OWLAxiom]) = { - val unsafe = RSAOntology(axioms, datafiles).unsafeRoles - var nodemap = Map.empty[String, OWLAxiom] - - object RSAConverter extends RDFoxConverter { - - override def convert( - expr: OWLClassExpression, - term: Term, - unsafe: List[OWLObjectPropertyExpression], - skolem: SkolemStrategy, - suffix: RSASuffix - ): Shards = - (expr, skolem) match { - - case (e: OWLObjectSomeValuesFrom, c: Constant) => { - nodemap.update(c.iri.getIRI, c.axiom) - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - if (unsafe contains e.getProperty) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) - else - (RSA.PE(term, c.iri) :: res, ext) - } - - case (e: OWLDataSomeValuesFrom, c: Constant) => { - nodemap.update(c.iri.getIRI, c.axiom) - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - if (unsafe contains e.getProperty) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) - else - (RSA.PE(term, c.iri) :: res, ext) - } - - case _ => super.convert(expr, term, unsafe, skolem, suffix) - } - } - - /* Ontology convertion into LP rules */ - val term = RSAOntology.genFreshVariable() - val result = axioms.map(a => - RSAConverter.convert(a, term, unsafe, new Constant(a), Empty) - ) - - val datalog = result.unzip - val facts = datalog._1.flatten - var rules = datalog._2.flatten - - /* Open connection with RDFox */ - val (server, data) = RDFoxUtil.openConnection("rsa_dependency_graph") - - /* Add additional built-in rules */ - val varX = Variable.create("X") - val varY = Variable.create("Y") - rules = Rule.create( - RSA.E(varX, varY), - RSA.PE(varX, varY), - RSA.U(varX), - RSA.U(varY) - ) :: rules - /* Load facts and rules from ontology */ - RDFoxUtil.addFacts(data, facts) - RDFoxUtil.addRules(data, rules) - /* Load data files */ - RDFoxUtil.addData(data, datafiles: _*) - - /* Build the graph */ - val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" - val answers = RDFoxUtil.submitQuery(data, query, RSA.Prefixes).get - var edges: Seq[DiEdge[Resource]] = - answers.collect { case (_, Seq(n1, n2)) => n1 ~> n2 } - val graph = Graph(edges: _*) - - /* Close connection to RDFox */ - RDFoxUtil.closeConnection(server, data) - - (graph, nodemap) - } -} - -object RSAOntology { - - import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ - /** Manager instance to interface with OWLAPI */ val manager = OWLManager.createOWLOntologyManager() val factory = manager.getOWLDataFactory() - /** Name of the RDFox data store used for CQ answering */ - private val DataStore = "answer_computation" - /** Simple fresh variable/class generator */ private var counter = -1; def genFreshVariable(): Variable = { @@ -184,6 +86,19 @@ object RSAOntology { factory.getOWLClass(s"X$counter") } +} + +object RSAOntology { + + import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ + + /** Manager instance to interface with OWLAPI */ + val manager = OWLManager.createOWLOntologyManager() + val factory = manager.getOWLDataFactory() + + /** Name of the RDFox data store used for CQ answering */ + private val DataStore = "answer_computation" + def apply( axioms: List[OWLLogicalAxiom], datafiles: List[File] -- cgit v1.2.3 From 18ddefc7c9e9cfaca027a054495325737ae6b9e6 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Thu, 22 Jul 2021 08:29:11 +0100 Subject: Move some generic commands from RSAOntology to Ontology --- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 445 ++++++++++----------- 1 file changed, 204 insertions(+), 241 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 9902fcd..630d2a0 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -63,6 +63,7 @@ import uk.ac.ox.cs.rsacomb.suffix._ import uk.ac.ox.cs.rsacomb.sparql._ import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} import uk.ac.ox.cs.rsacomb.util.Logger +import uk.ac.ox.cs.rsacomb.ontology.Ontology object RSAUtil { @@ -94,82 +95,94 @@ object RSAOntology { /** Manager instance to interface with OWLAPI */ val manager = OWLManager.createOWLOntologyManager() - val factory = manager.getOWLDataFactory() /** Name of the RDFox data store used for CQ answering */ private val DataStore = "answer_computation" + /** Filtering program for a given query + * + * @param query the query to derive the filtering program + * @return the filtering program for the given query + */ + def filteringProgram(query: ConjunctiveQuery): FilteringProgram = + Logger.timed( + FilteringProgram(FilterType.REVISED)(query), + "Generating filtering program", + Logger.DEBUG + ) + def apply( axioms: List[OWLLogicalAxiom], datafiles: List[File] - ): RSAOntology = new RSAOntology(axioms, datafiles: _*) - - def apply( - ontofile: File, - datafiles: List[File], - approx: Option[Approximation] - ): RSAOntology = { - val ontology = manager.loadOntologyFromOntologyDocument(ontofile) - RSAOntology(ontology, datafiles, approx) - } + ): RSAOntology = new RSAOntology(axioms, datafiles) + + // def apply( + // ontofile: File, + // datafiles: List[File], + // approx: Option[Approximation] + // ): RSAOntology = { + // val ontology = manager.loadOntologyFromOntologyDocument(ontofile) + // RSAOntology(ontology, datafiles, approx) + // } - def apply( - ontology: OWLOntology, - datafiles: List[File], - approx: Option[Approximation] - ): RSAOntology = { - val normalizer = new Normalizer() - - /** TBox axioms */ - var tbox: List[OWLLogicalAxiom] = - ontology - .tboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** RBox axioms */ - var rbox: List[OWLLogicalAxiom] = - ontology - .rboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** ABox axioms - * - * @note this represents only the set of assertions contained in the - * ontology file. Data files specified in `datafiles` are directly - * imported in RDFox due to performance issues when trying to import - * large data files via OWLAPI. - */ - var abox: List[OWLLogicalAxiom] = - ontology - .aboxAxioms(Imports.INCLUDED) - .collect(Collectors.toList()) - .collect { case a: OWLLogicalAxiom => a } - .flatMap(normalizer.normalize) - - /** Collection of logical axioms in the input ontology */ - var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox - - new RSAOntology( - approx match { - case Some(a) => a.approximate(axioms, datafiles) - case None => axioms - }, - datafiles: _* - ) - } + // def apply( + // ontology: OWLOntology, + // datafiles: List[File], + // approx: Option[Approximation] + // ): RSAOntology = { + // val normalizer = new Normalizer() + + // /** TBox axioms */ + // var tbox: List[OWLLogicalAxiom] = + // ontology + // .tboxAxioms(Imports.INCLUDED) + // .collect(Collectors.toList()) + // .collect { case a: OWLLogicalAxiom => a } + // .flatMap(normalizer.normalize) + + // /** RBox axioms */ + // var rbox: List[OWLLogicalAxiom] = + // ontology + // .rboxAxioms(Imports.INCLUDED) + // .collect(Collectors.toList()) + // .collect { case a: OWLLogicalAxiom => a } + // .flatMap(normalizer.normalize) + + // /** ABox axioms + // * + // * @note this represents only the set of assertions contained in the + // * ontology file. Data files specified in `datafiles` are directly + // * imported in RDFox due to performance issues when trying to import + // * large data files via OWLAPI. + // */ + // var abox: List[OWLLogicalAxiom] = + // ontology + // .aboxAxioms(Imports.INCLUDED) + // .collect(Collectors.toList()) + // .collect { case a: OWLLogicalAxiom => a } + // .flatMap(normalizer.normalize) + + // /** Collection of logical axioms in the input ontology */ + // var axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox + + // new RSAOntology( + // approx match { + // case Some(a) => a.approximate(axioms, datafiles) + // case None => axioms + // }, + // datafiles: _* + // ) + // } } -/** Wrapper class for an ontology in RSA +/** A wrapper for an RSA ontology * * @param ontology the input OWL2 ontology. * @param datafiles additinal data (treated as part of the ABox) */ -class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { +class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) + extends Ontology(axioms, datafiles) { /** Simplify conversion between OWLAPI and RDFox concepts */ import implicits.RDFox._ @@ -179,34 +192,26 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { import uk.ac.ox.cs.rsacomb.implicits.JavaCollections._ /** Set of axioms removed during the approximation to RSA */ - private var removed: Seq[OWLAxiom] = Seq.empty - - /** Normalized Horn-ALCHOIQ ontology */ - val ontology = - RSAOntology.manager.createOntology((axioms: List[OWLAxiom]).asJava) - - /** OWLAPI internal reasoner instantiated over the approximated ontology */ - private val reasoner = - (new StructuralReasonerFactory()).createReasoner(ontology) + //private var removed: Seq[OWLAxiom] = Seq.empty /** Retrieve individuals/literals in the ontology */ - val individuals: List[IRI] = + private val individuals: List[IRI] = ontology .getIndividualsInSignature() .asScala .map(_.getIRI) .map(implicits.RDFox.owlapiToRdfoxIri) .toList - val literals: List[Literal] = + private val literals: List[Literal] = axioms .collect { case a: OWLDataPropertyAssertionAxiom => a } .map(_.getObject) .map(implicits.RDFox.owlapiToRdfoxLiteral) /** Retrieve concepts/roles in the ontology */ - val concepts: List[OWLClass] = + private val concepts: List[OWLClass] = ontology.getClassesInSignature().asScala.toList - val roles: List[OWLObjectPropertyExpression] = + private val roles: List[OWLObjectPropertyExpression] = axioms .flatMap(_.objectPropertyExpressionsInSignature) .distinct @@ -223,36 +228,36 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { * if there exists a role p2 appearing in an axiom of type T4 and * p1 is a subproperty of either p2 or the inverse of p2. */ - val unsafeRoles: List[OWLObjectPropertyExpression] = { - - /* Checking for unsafety condition (1) */ - val unsafe1 = for { - axiom <- axioms - if axiom.isT5 - role1 <- axiom.objectPropertyExpressionsInSignature - roleSuper = role1 +: reasoner.superObjectProperties(role1) - roleSuperInv = roleSuper.map(_.getInverseProperty) - axiom <- axioms - if axiom.isT3 && !axiom.isT3top - role2 <- axiom.objectPropertyExpressionsInSignature - if roleSuperInv contains role2 - } yield role1 - - /* Checking for unsafety condition (2) */ - val unsafe2 = for { - axiom <- axioms - if axiom.isT5 - role1 <- axiom.objectPropertyExpressionsInSignature - roleSuper = role1 +: reasoner.superObjectProperties(role1) - roleSuperInv = roleSuper.map(_.getInverseProperty) - axiom <- axioms - if axiom.isT4 - role2 <- axiom.objectPropertyExpressionsInSignature - if roleSuper.contains(role2) || roleSuperInv.contains(role2) - } yield role1 - - unsafe1 ++ unsafe2 - } + // val unsafeRoles: List[OWLObjectPropertyExpression] = { + + // /* Checking for unsafety condition (1) */ + // val unsafe1 = for { + // axiom <- axioms + // if axiom.isT5 + // role1 <- axiom.objectPropertyExpressionsInSignature + // roleSuper = role1 +: reasoner.superObjectProperties(role1) + // roleSuperInv = roleSuper.map(_.getInverseProperty) + // axiom <- axioms + // if axiom.isT3 && !axiom.isT3top + // role2 <- axiom.objectPropertyExpressionsInSignature + // if roleSuperInv contains role2 + // } yield role1 + + // /* Checking for unsafety condition (2) */ + // val unsafe2 = for { + // axiom <- axioms + // if axiom.isT5 + // role1 <- axiom.objectPropertyExpressionsInSignature + // roleSuper = role1 +: reasoner.superObjectProperties(role1) + // roleSuperInv = roleSuper.map(_.getInverseProperty) + // axiom <- axioms + // if axiom.isT4 + // role2 <- axiom.objectPropertyExpressionsInSignature + // if roleSuper.contains(role2) || roleSuperInv.contains(role2) + // } yield role1 + + // unsafe1 ++ unsafe2 + // } /** Approximate a Horn-ALCHOIQ ontology to RSA * @@ -396,31 +401,27 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { ) } + /** Canonical model of the ontology */ lazy val canonicalModel = Logger.timed( new CanonicalModel(this), "Generating canonical model program", Logger.DEBUG ) - def filteringProgram(query: ConjunctiveQuery): FilteringProgram = - Logger.timed( - FilteringProgram(FilterType.REVISED)(query), - "Generating filtering program", - Logger.DEBUG - ) - + /** Computes all roles conflicting with a given role + * + * @param role a role (object property expression). + * @return a set of roles conflicting with `role`. + */ def confl( role: OWLObjectPropertyExpression ): Set[OWLObjectPropertyExpression] = { - - val invSuperRoles = reasoner + reasoner .superObjectProperties(role) .collect(Collectors.toSet()) .asScala .addOne(role) .map(_.getInverseProperty) - - invSuperRoles .flatMap(x => reasoner .subObjectProperties(x) @@ -432,6 +433,77 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) } + /** Selfloop detection for a given axiom + * + * @param axiom an axiom of type [[OWLSubClassOfAxiom]] + * @return unfold set for the axiom + */ + def self(axiom: OWLSubClassOfAxiom): Set[Term] = { + val role = axiom.objectPropertyExpressionsInSignature(0) + if (this.confl(role).contains(role)) { + Set(RSA("v0_" ++ axiom.hashed), RSA("v1_" ++ axiom.hashed)) + } else { + Set() + } + } + + /** Cycle detection for a give axiom + * + * @param axiom an axiom of type [[OWLSubClassOfAxiom]] + * @return unfold set for the axiom + * + * @todo we can actually use `toTriple` from `RSAAxiom` to get the + * classes and the role for a given axiom + */ + def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = { + val classes = + axiom.classesInSignature.collect(Collectors.toList()).asScala + val classA = classes(0) + val roleR = axiom + .objectPropertyExpressionsInSignature(0) + .asInstanceOf[OWLObjectProperty] + val classB = classes(1) + cycle_aux(classA, roleR, classB) + } + + /** Auxiliary function for [[RSAOntology.cycle]] */ + private def cycle_aux( + classA: OWLClass, + roleR: OWLObjectProperty, + classB: OWLClass + ): Set[Term] = { + val conflR = this.confl(roleR) + // TODO: technically we just need the TBox here + val terms = for { + axiom1 <- axioms + if axiom1.isT5 + // We expect only one role coming out of a T5 axiom + roleS <- axiom1.objectPropertyExpressionsInSignature + // Triples ordering is among triples involving safe roles. + if !unsafe.contains(roleS) + if conflR.contains(roleS) + tripleARB = RSAAxiom.hashed(classA, roleR, classB) + tripleDSC = axiom1.hashed + individual = + if (tripleARB > tripleDSC) { + RSA("v1_" ++ tripleDSC) + } else { + // Note that this is also the case for + // `tripleARB == tripleDSC` + RSA("v0_" ++ tripleDSC) + } + } yield individual + terms to Set + } + + /** Returns unfold set for self-loop and cycle for the input axiom + * + * @param axiom an axiom of type [[OWLSubClassOfAxiom]] + * @return unfold set for the axiom + */ + def unfold(axiom: OWLSubClassOfAxiom): Set[Term] = + this.self(axiom) | this.cycle(axiom) + /** Returns the answers to a query * * @param query query to execute @@ -439,10 +511,9 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { */ def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed( { - import implicits.JavaCollections._ val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) val canon = this.canonicalModel - val filter = this.filteringProgram(query) + val filter = RSAOntology.filteringProgram(query) /* Upload data from data file */ RDFoxUtil.addData(data, datafiles: _*) @@ -460,12 +531,15 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { new java.util.HashMap[String, String] ) + /* Add canonical model */ Logger print s"Canonical model rules: ${canon.rules.length}" RDFoxUtil.addRules(data, canon.rules) Logger print s"Canonical model facts: ${canon.facts.length}" RDFoxUtil.addFacts(data, canon.facts) + RDFoxUtil printStatisticsFor data + //{ // import java.io.{PrintStream, FileOutputStream, File} // val rules1 = new FileOutputStream(new File("rules1-lubm200.dlog")) @@ -475,16 +549,13 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { // rules2.print(filter.rules.mkString("\n")) //} - //canon.facts.foreach(println) - //filter.rules.foreach(println) - - RDFoxUtil printStatisticsFor data - + /* Add filtering program */ Logger print s"Filtering program rules: ${filter.rules.length}" RDFoxUtil.addRules(data, filter.rules) RDFoxUtil printStatisticsFor data + /* Gather answers to the query */ val answers = { val ans = filter.answerQuery RDFoxUtil @@ -492,7 +563,9 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _)) .get } + RDFoxUtil.closeConnection(server, data) + answers }, "Answers computation", @@ -503,14 +576,15 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { * * @note This method does not add any facts or rules to the data * store. It is most useful after the execution of a query using - * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]]. - * @note This method has been introduced mostly for debugging purposes. + * [[RSAOntology.ask]]. * * @param query query to be executed against the environment * @param prefixes additional prefixes for the query. It defaults to * an empty set. * @param opts additional options to RDFox. * @return a collection of answers to the input query. + * + * @note This method has been introduced mostly for debugging purposes. */ def queryDataStore( query: String, @@ -532,122 +606,11 @@ class RSAOntology(val axioms: List[OWLLogicalAxiom], val datafiles: File*) { * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]] * for the corresponding query has been called. */ - def askUnfiltered( - cq: ConjunctiveQuery - ): Option[Seq[(Long, Seq[Resource])]] = { - val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length) - queryDataStore(query, RSA.Prefixes) - } - - def self(axiom: OWLSubClassOfAxiom): Set[Term] = { - // Assuming just one role in the signature of a T5 axiom - val role = axiom.objectPropertyExpressionsInSignature(0) - if (this.confl(role).contains(role)) { - Set( - RSA("v0_" ++ axiom.hashed), - RSA("v1_" ++ axiom.hashed) - ) - } else { - Set() - } - } - - def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = { - // TODO: we can actually use `toTriple` from `RSAAxiom` - val classes = - axiom.classesInSignature.collect(Collectors.toList()).asScala - val classA = classes(0) - val roleR = axiom - .objectPropertyExpressionsInSignature(0) - .asInstanceOf[OWLObjectProperty] - val classB = classes(1) - cycle_aux1(classA, roleR, classB) - } - - def cycle_aux0( - classA: OWLClass, - roleR: OWLObjectProperty, - classB: OWLClass - ): Set[Term] = { - val conflR = this.confl(roleR) - val classes = ontology - .classesInSignature(Imports.INCLUDED) - .collect(Collectors.toSet()) - .asScala - for { - classD <- classes - roleS <- conflR - classC <- classes - // Keeping this check for now - if !unsafeRoles.contains(roleS) - tripleARB = RSAAxiom.hashed(classA, roleR, classB) - tripleDSC = RSAAxiom.hashed(classD, roleS, classC) - individual = - if (tripleARB > tripleDSC) { - RSA("v1_" ++ tripleDSC) - } else { - // Note that this is also the case for - // `tripleARB == tripleDSC` - RSA("v0_" ++ tripleDSC) - } - } yield individual - } - - def cycle_aux1( - classA: OWLClass, - roleR: OWLObjectProperty, - classB: OWLClass - ): Set[Term] = { - val conflR = this.confl(roleR) - // We just need the TBox to find - val terms = for { - axiom1 <- axioms - if axiom1.isT5 - // We expect only one role coming out of a T5 axiom - roleS <- axiom1.objectPropertyExpressionsInSignature - // Triples ordering is among triples involving safe roles. - if !unsafeRoles.contains(roleS) - if conflR.contains(roleS) - tripleARB = RSAAxiom.hashed(classA, roleR, classB) - tripleDSC = axiom1.hashed - individual = - if (tripleARB > tripleDSC) { - RSA("v1_" ++ tripleDSC) - } else { - // Note that this is also the case for - // `tripleARB == tripleDSC` - RSA("v0_" ++ tripleDSC) - } - } yield individual - terms to Set - } - - def unfold(axiom: OWLSubClassOfAxiom): Set[Term] = - this.self(axiom) | this.cycle(axiom) - - /** Log normalization/approximation statistics */ - // def statistics(level: Logger.Level = Logger.DEBUG): Unit = { - // Logger.print( - // s"Logical axioms in original input ontology: ${original.getLogicalAxiomCount(true)}", - // level - // ) - // Logger.print( - // s"Logical axioms discarded in Horn-ALCHOIQ approximation: ${normalizer.discarded}", - // level - // ) - // Logger.print( - // s"Logical axioms shifted in Horn-ALCHOIQ approximation: ${normalizer.shifted}", - // level - // ) - // Logger.print( - // s"Logical axioms in Horn-ALCHOIQ ontology: ${ontology - // .getLogicalAxiomCount(true)} (${axioms.length}/${axioms.length}/${axioms.length})", - // level - // ) - // Logger.print( - // s"Logical axioms discarded in RSA approximation: ${removed.length}", - // level - // ) + // def askUnfiltered( + // cq: ConjunctiveQuery + // ): Option[Seq[(Long, Seq[Resource])]] = { + // val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length) + // queryDataStore(query, RSA.Prefixes) // } -} // class RSAOntology +} -- cgit v1.2.3 From cb8572606f8951213bcfe9e6667caa208ad3d189 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Thu, 22 Jul 2021 09:59:07 +0100 Subject: Review main workflow --- src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala') 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 630d2a0..73c4411 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -209,9 +209,9 @@ class RSAOntology(axioms: List[OWLLogicalAxiom], datafiles: List[File]) .map(implicits.RDFox.owlapiToRdfoxLiteral) /** Retrieve concepts/roles in the ontology */ - private val concepts: List[OWLClass] = + val concepts: List[OWLClass] = ontology.getClassesInSignature().asScala.toList - private val roles: List[OWLObjectPropertyExpression] = + val roles: List[OWLObjectPropertyExpression] = axioms .flatMap(_.objectPropertyExpressionsInSignature) .distinct -- cgit v1.2.3