From 333e4f36e787f93c4c9547b17004da1574b5f991 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 7 Dec 2020 13:54:37 +0000 Subject: Add first implementation of a logger This is needed mostly for debugging and benchmarking purposes. We tried to make it as "out of the way" as possible. --- src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 16 +- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 230 ++++++++++++--------- .../scala/uk/ac/ox/cs/rsacomb/util/Logger.scala | 45 ++++ .../scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | 68 ++++-- 4 files changed, 230 insertions(+), 129 deletions(-) create mode 100644 src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index d41ca8c..5246d2f 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala @@ -10,7 +10,7 @@ import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} /* Local imports */ -import util.{RDFoxUtil, RSA} +import util.{Logger, RDFoxUtil, RSA} import sparql.ConjunctiveQuery object RSAComb extends App { @@ -53,15 +53,17 @@ object RSAComb extends App { val ontology = RSAOntology(ontoPaths: _*) if (ontology.isRSA) { - //println("ONTOLOGY IS RSA") + + Logger print "Ontology is RSA!" /** Read SPARQL query from file */ - val source = io.Source.fromFile(queryPath.getAbsoluteFile) - val query = source.getLines mkString "\n" - source.close() + val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) /* Compute answers to query */ - val answers = ConjunctiveQuery(query).map(ontology ask _) - answers map (_.toString) foreach println + ConjunctiveQuery(query).map(ontology ask _) match { + case Some(answers) => Logger print answers + case None => + throw new RuntimeException("Submitted query is not conjunctive") + } } } 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 52d4905..87a2312 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -54,6 +54,7 @@ import uk.ac.ox.cs.rsacomb.converter._ 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 object RSAOntology { @@ -141,87 +142,91 @@ class RSAOntology(val ontology: OWLOntology) { * why the ontology might not be RSA. This could help a second * step of approximation of an Horn-ALCHOIQ to RSA */ - lazy val isRSA: Boolean = { - - val unsafe = this.unsafeRoles - - /* DEBUG: print rules in DL syntax and unsafe roles */ - //val renderer = new DLSyntaxObjectRenderer() - //println("\nDL rules:") - //axioms.foreach(x => println(renderer.render(x))) - //println("\nUnsafe roles:") - //println(unsafe) - - 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) - if unsafe contains e.getProperty => { - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) - } - - case (e: OWLDataSomeValuesFrom, c: Constant) - if unsafe contains e.getProperty => { - val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) - (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) + lazy val isRSA: Boolean = Logger.timed( + { + val unsafe = this.unsafeRoles + + // val renderer = new DLSyntaxObjectRenderer() + // println() + // println("Unsafe roles:") + // println(unsafe) + // println() + // println("DL rules:") + // tbox.foreach(x => println(renderer.render(x))) + + 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) + if unsafe contains e.getProperty => { + val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) + (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) + } + + case (e: OWLDataSomeValuesFrom, c: Constant) + if unsafe contains e.getProperty => { + val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) + (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) + } + + case _ => super.convert(expr, term, unsafe, skolem, suffix) } - case _ => super.convert(expr, term, unsafe, skolem, suffix) - } - - } - - /* Ontology convertion into LP rules */ - val term = RSAOntology.genFreshVariable() - val datalog = axioms - .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) - .unzip - val facts = datalog._1.flatten - val rules = datalog._2.flatten - - /* DEBUG: print datalog rules */ - //println("\nDatalog rules:") - //rules.foreach(println) - - // Open connection with RDFox - val (server, data) = RDFoxUtil.openConnection("RSACheck") - - /* Add built-in rules - * TODO: substitute with RDFoxUtil.addRules - */ - data.importData( - UpdateType.ADDITION, - RSA.Prefixes, - "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ." - ) - - /* Add ontology facts and rules */ - RDFoxUtil.addFacts(data, facts) - RDFoxUtil.addRules(data, rules) - - /* Build graph */ - val graph = this.rsaGraph(data); - //println(graph) - - // Close connection to RDFox - RDFoxUtil.closeConnection(server, data) + } + + /* Ontology convertion into LP rules */ + val term = RSAOntology.genFreshVariable() + val datalog = axioms + .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) + .unzip + val facts = datalog._1.flatten + val rules = datalog._2.flatten + + //println("Datalog rules:") + //rules foreach println + + // Open connection with RDFox + val (server, data) = RDFoxUtil.openConnection("RSACheck") + + /* Add built-in rules + * TODO: substitute with RDFoxUtil.addRules + */ + data.importData( + UpdateType.ADDITION, + RSA.Prefixes, + "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ." + ) - /* To check if the graph is tree-like we check for acyclicity in a - * undirected graph. - * - * TODO: Implement additional checks (taking into account equality) - */ - graph.isAcyclic - } + /* Add ontology facts and rules */ + RDFoxUtil.addFacts(data, facts) + RDFoxUtil.addRules(data, rules) + + /* Build graph */ + val graph = this.rsaGraph(data); + //println("Graph:") + //println(graph) + + // Close connection to RDFox + RDFoxUtil.closeConnection(server, data) + + /* To check if the graph is tree-like we check for acyclicity in a + * undirected graph. + * + * TODO: Implement additional checks (taking into account equality) + */ + graph.isAcyclic + }, + "RSA check", + Logger.DEBUG + ) lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { @@ -288,9 +293,17 @@ class RSAOntology(val ontology: OWLOntology) { } def filteringProgram(query: ConjunctiveQuery): FilteringProgram = - new FilteringProgram(query, individuals) + Logger.timed( + new FilteringProgram(query, individuals), + "Generating filtering program", + Logger.DEBUG + ) - lazy val canonicalModel = new CanonicalModel(this) + lazy val canonicalModel = Logger.timed( + new CanonicalModel(this), + "Generating canonical model program", + Logger.DEBUG + ) // TODO: the following functions needs testing def confl( @@ -321,27 +334,42 @@ class RSAOntology(val ontology: OWLOntology) { * @param query query to execute * @return a collection of answers */ - def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { - import implicits.JavaCollections._ - val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) - val filter = this.filteringProgram(query) - RDFoxUtil.addRules(data, this.canonicalModel.rules) - RDFoxUtil.addFacts(data, this.canonicalModel.facts) - RDFoxUtil.addRules(data, filter.rules) - RDFoxUtil.addFacts(data, filter.facts) - val answers = RDFoxUtil - .submitQuery( - data, - RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), - RSA.Prefixes - ) - .map( - new ConjunctiveQueryAnswers(query.bcq, _) - ) - .get - RDFoxUtil.closeConnection(server, data) - answers - } + 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) + + Logger print s"Canonical model: ${canon.rules.length} rules" + RDFoxUtil.addRules(data, this.canonicalModel.rules) + + Logger print s"Canonical model: ${canon.facts.length} facts" + RDFoxUtil.addFacts(data, this.canonicalModel.facts) + + RDFoxUtil printStatisticsFor data + + Logger print s"Filtering program: ${filter.rules.length} rules" + RDFoxUtil.addRules(data, filter.rules) + + Logger print s"Filtering program: ${filter.facts.length} facts" + RDFoxUtil.addFacts(data, filter.facts) + + RDFoxUtil printStatistics data + + val answers = { + val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size) + RDFoxUtil + .submitQuery(data, ans, RSA.Prefixes) + .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _)) + .get + } + RDFoxUtil.closeConnection(server, data) + answers + }, + "Answers computation", + Logger.DEBUG + ) /** Query the RDFox data store used for query answering. * diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala new file mode 100644 index 0000000..74797a2 --- /dev/null +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala @@ -0,0 +1,45 @@ +package uk.ac.ox.cs.rsacomb.util + +import java.util.Calendar +import java.io.PrintStream + +/** Rough implementation of a logger. + * + * This is a WIP class for debugging and benchmarking. + */ +object Logger { + + /** Output stream for the logger. */ + var output: PrintStream = System.out + + /** Logger levels (i.e., verbosity of output) */ + sealed abstract class Level(val level: Int, val name: String) + extends Ordered[Level] { + def compare(that: Level) = this.level - that.level + override def toString = name + } + case object QUIET extends Level(0, "normal") + case object NORMAL extends Level(1, "normal") + case object DEBUG extends Level(2, "debug") + case object VERBOSE extends Level(3, "verbose") + + /** Currend logger level */ + var level: Level = DEBUG + + def print(str: Any, lvl: Level = NORMAL): Unit = { + if (lvl <= level) { + val time = Calendar.getInstance.getTime + output println s"[$lvl][$time] $str" + } + } + + def timed[A](expr: => A, desc: String = "", lvl: Level = NORMAL): A = { + val t0 = System.currentTimeMillis() + print(s"$desc (START)", lvl) + val result = expr + val t1 = System.currentTimeMillis() + print(s"$desc (END): ${(t1 - t0).toFloat / 1000}s", lvl) + result + } + +} diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala index 9dd52cf..51ef903 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala @@ -1,5 +1,6 @@ package uk.ac.ox.cs.rsacomb.util +import java.io.File import java.io.StringReader import tech.oxfordsemantic.jrdfox.Prefixes import tech.oxfordsemantic.jrdfox.client.{ @@ -20,6 +21,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{ import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery import uk.ac.ox.cs.rsacomb.suffix.Nth +import uk.ac.ox.cs.rsacomb.util.Logger /** A collection of helper methods for RDFox */ object RDFoxUtil { @@ -67,18 +69,21 @@ object RDFoxUtil { (server, data) } - /** Gather statistics from RDFox datastore. + /** Prints statistics from RDFox datastore. + * + * Prints something only when Logger level is set to DEBUG or more. * * @see [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#in-depth-diagnostic-information]] * and [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#managing-statistics]] * for more ways of gathering diagnostics from RDFox. */ - def gatherStatistics(data: DataStoreConnection): String = { + def printStatisticsFor(data: DataStoreConnection): Unit = { val info = data.getComponentInfo(true) - s"${info.getName}: ${info.getPropertyValues}" + val stats = s"${info.getName}: ${info.getPropertyValues}" .replaceAll("\\{", "{\n ") .replaceAll(", ", ",\n ") .replaceAll("\\}", "\n}") + Logger.print(stats, Logger.DEBUG) } /** Adds a collection of rules to a data store. @@ -87,7 +92,11 @@ object RDFoxUtil { * @param rules collection of rules to be added to the data store */ def addRules(data: DataStoreConnection, rules: Seq[Rule]): Unit = - data addRules rules + Logger.timed( + data addRules rules, + "Loading rules", + Logger.DEBUG + ) /** Adds a collection of facts to a data store. * @@ -95,12 +104,25 @@ object RDFoxUtil { * @param facts collection of facts to be added to the data store */ def addFacts(data: DataStoreConnection, facts: Seq[TupleTableAtom]): Unit = - data.importData( - UpdateType.ADDITION, - RSA.Prefixes, - facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") + Logger.timed( + data.importData( + UpdateType.ADDITION, + RSA.Prefixes, + facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") + ), + "Loading facts", + Logger.DEBUG ) + /** Load SPARQL query from file. */ + def loadQueryFromFile(file: File): String = { + val source = io.Source.fromFile(file) + val query = source.getLines mkString "\n" + Logger print s"Loaded query:\n$query" + source.close() + query + } + /** Parse a SELECT query from a string in SPARQL format. * * @param query the string containing the SPARQL query @@ -136,19 +158,23 @@ object RDFoxUtil { data: DataStoreConnection, query: SelectQuery, opts: RDFoxOpts = RDFoxOpts() - ): QueryAnswers = { - val cursor = data.createCursor(query, opts) - var answers = QueryAnswers() - var mul = cursor.open() - while (mul > 0) { - val answer = - (0 until cursor.getArity).map(cursor.getResource(_)).toList - answers = answer :: answers - mul = cursor.advance() - } - cursor.close(); - answers - } + ): QueryAnswers = Logger.timed( + { + val cursor = data.createCursor(query, opts) + var answers = QueryAnswers() + var mul = cursor.open() + while (mul > 0) { + val answer = + (0 until cursor.getArity).map(cursor.getResource(_)).toList + answers = answer :: answers + mul = cursor.advance() + } + cursor.close(); + answers + }, + "Answer query", + Logger.DEBUG + ) /** Execute a query over a given datastore connection. * -- cgit v1.2.3 From 2506ce08cb6660305922fc649c221226332d5783 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 7 Dec 2020 16:20:31 +0000 Subject: Add additional diagnostics --- src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 8 +++++++- src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) 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 87a2312..8a40e1e 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -108,6 +108,10 @@ class RSAOntology(val ontology: OWLOntology) { val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox + Logger.print(s"Original TBox: ${tbox.length} axioms", Logger.DEBUG) + Logger.print(s"Original RBox: ${tbox.length} axioms", Logger.DEBUG) + Logger.print(s"Original ABox: ${tbox.length} axioms", Logger.DEBUG) + /* Retrieve individuals in the original ontology */ val individuals: List[IRI] = @@ -347,6 +351,7 @@ class RSAOntology(val ontology: OWLOntology) { Logger print s"Canonical model: ${canon.facts.length} facts" RDFoxUtil.addFacts(data, this.canonicalModel.facts) + RDFoxUtil materialize data RDFoxUtil printStatisticsFor data Logger print s"Filtering program: ${filter.rules.length} rules" @@ -355,7 +360,8 @@ class RSAOntology(val ontology: OWLOntology) { Logger print s"Filtering program: ${filter.facts.length} facts" RDFoxUtil.addFacts(data, filter.facts) - RDFoxUtil printStatistics data + RDFoxUtil materialize data + RDFoxUtil printStatisticsFor data val answers = { val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala index 51ef903..31cc850 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala @@ -114,6 +114,10 @@ object RDFoxUtil { Logger.DEBUG ) + /** Force materialization in RDFox. */ + def materialize(data: DataStoreConnection): Unit = + Logger.timed(data.updateMaterialization(), "Materialization", Logger.DEBUG) + /** Load SPARQL query from file. */ def loadQueryFromFile(file: File): String = { val source = io.Source.fromFile(file) -- cgit v1.2.3 From daf6c30152f3bb1ac6f10f1eeb783687f1a6a214 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 7 Dec 2020 16:44:19 +0000 Subject: Fix typo in ontology diagnostics --- src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8a40e1e..708e4c7 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -109,8 +109,8 @@ class RSAOntology(val ontology: OWLOntology) { val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox Logger.print(s"Original TBox: ${tbox.length} axioms", Logger.DEBUG) - Logger.print(s"Original RBox: ${tbox.length} axioms", Logger.DEBUG) - Logger.print(s"Original ABox: ${tbox.length} axioms", Logger.DEBUG) + Logger.print(s"Original RBox: ${rbox.length} axioms", Logger.DEBUG) + Logger.print(s"Original ABox: ${abox.length} axioms", Logger.DEBUG) /* Retrieve individuals in the original ontology */ -- cgit v1.2.3 From b241f9b23b225dec5ffc3f8ddd6c81771091f599 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 7 Dec 2020 18:08:33 +0000 Subject: Add diagnostics for (un)filtered answer ratio --- src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 26 +++++++++++++++++----- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 14 ++++++++++++ .../rsacomb/sparql/ConjunctiveQueryAnswers.scala | 3 +++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index 60511af..6891c8c 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala @@ -57,13 +57,29 @@ object RSAComb extends App { Logger print "Ontology is RSA!" /** Read SPARQL query from file */ - val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) - - /* Compute answers to query */ - ConjunctiveQuery.parse(query).map(ontology ask _) match { - case Some(answers) => Logger print answers + val strQuery = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile) + val query = ConjunctiveQuery parse strQuery + + query match { + case Some(query) => { + val answers = ontology ask query + Logger.print(s"$answers", Logger.QUIET) + Logger print s"Number of answer: ${answers.length}" + + val unfiltered = ontology askUnfiltered query + val percentage = unfiltered match { + case Some(u) => + if (u.length > 0) (1 - answers.length / u.length) * 100 else 0 + case None => 0 + } + Logger.print( + s"Percentage of spurious answers: $percentage%", + Logger.DEBUG + ) + } case None => throw new RuntimeException("Submitted query is not conjunctive") } + } } 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 708e4c7..6a34555 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -403,6 +403,20 @@ class RSAOntology(val ontology: OWLOntology) { answers } + /** Returns set of unfiltered answers. + * + * This is equivalent to quering just the canonical model. + * + * @note this method does not load any data to RDFox. The return + * value is considered well defined only after + * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]] + * for the corresponding query has been called. + */ + def askUnfiltered(cq: ConjunctiveQuery): Option[Seq[Seq[Resource]]] = { + val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length) + queryDataStore(cq, 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) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala index 327ae8e..50cbb86 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala @@ -17,6 +17,9 @@ class ConjunctiveQueryAnswers( val answers: Seq[Seq[Resource]] ) { + /** Returns number of answers. */ + val length: Int = if (bcq) 0 else answers.length + override def toString(): String = if (bcq) { if (answers.isEmpty) "FALSE" else "TRUE" -- cgit v1.2.3 From 17285895dec47f4ca05590c861a7e8ef6f595c0c Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Tue, 8 Dec 2020 13:09:09 +0000 Subject: Remove (forced) materialization This is a useless call since RDFox automatically materialize it's datasets when committing a transactions. Since loading rules/facts uses an internal transaction internally, these materialization calls do nothing. It will be still usefull when operating inside transactions. --- src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 2 -- 1 file changed, 2 deletions(-) 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 6a34555..79f2ef3 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -351,7 +351,6 @@ class RSAOntology(val ontology: OWLOntology) { Logger print s"Canonical model: ${canon.facts.length} facts" RDFoxUtil.addFacts(data, this.canonicalModel.facts) - RDFoxUtil materialize data RDFoxUtil printStatisticsFor data Logger print s"Filtering program: ${filter.rules.length} rules" @@ -360,7 +359,6 @@ class RSAOntology(val ontology: OWLOntology) { Logger print s"Filtering program: ${filter.facts.length} facts" RDFoxUtil.addFacts(data, filter.facts) - RDFoxUtil materialize data RDFoxUtil printStatisticsFor data val answers = { -- cgit v1.2.3 From 43327d8a986e41ba26b6f2b5c911646c7c9a254b Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Wed, 9 Dec 2020 11:19:27 +0000 Subject: Keep track of answer multiplicity --- src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala | 7 ++++++- .../scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala | 11 ++++++----- .../cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala | 21 ++++++++++++++------- .../scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala | 8 ++++---- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala index 6891c8c..eaacedc 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala @@ -68,8 +68,13 @@ object RSAComb extends App { val unfiltered = ontology askUnfiltered query val percentage = unfiltered match { - case Some(u) => + case Some(u) => { + Logger.print( + s"Number of spurious answers: ${u.length}.", + Logger.DEBUG + ) if (u.length > 0) (1 - answers.length / u.length) * 100 else 0 + } case None => 0 } Logger.print( 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 dc64c79..0f1cd5e 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala @@ -298,9 +298,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.collect { case (_, Seq(n1, n2)) => UnDiEdge(n1, n2) } Graph(edges: _*) } @@ -402,7 +401,7 @@ class RSAOntology(val ontology: OWLOntology) { query: String, prefixes: Prefixes = new Prefixes(), opts: ju.Map[String, String] = new ju.HashMap[String, String]() - ): Option[Seq[Seq[Resource]]] = { + ): Option[Seq[(Long, Seq[Resource])]] = { val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) val answers = RDFoxUtil.submitQuery(data, query, prefixes, opts) RDFoxUtil.closeConnection(server, data) @@ -418,7 +417,9 @@ class RSAOntology(val ontology: OWLOntology) { * [[uk.ac.ox.cs.rsacomb.RSAOntology.ask RSAOntology.ask]] * for the corresponding query has been called. */ - def askUnfiltered(cq: ConjunctiveQuery): Option[Seq[Seq[Resource]]] = { + def askUnfiltered( + cq: ConjunctiveQuery + ): Option[Seq[(Long, Seq[Resource])]] = { val query = RDFoxUtil.buildDescriptionQuery("QM", cq.variables.length) queryDataStore(cq, query, RSA.Prefixes) } diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala index 667defc..7edc867 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQueryAnswers.scala @@ -19,12 +19,15 @@ import tech.oxfordsemantic.jrdfox.logic.expression.{ class ConjunctiveQueryAnswers( bcq: Boolean, val variables: Seq[Variable], - val answers: Seq[Seq[Resource]] + val answers: Seq[(Long, Seq[Resource])] ) { - /** Returns number of answers. */ + /** Returns number of distinct answers. */ val length: Int = if (bcq) 0 else answers.length + /** Returns number of answers taking into account multiplicity. */ + val lengthWithMultiplicity: Long = answers.map(_._1).sum + override def toString(): String = if (bcq) { if (answers.isEmpty) "FALSE" else "TRUE" @@ -34,11 +37,15 @@ class ConjunctiveQueryAnswers( else { val header = variables map (_.getName) mkString "\t" val body = answers - .map(_.map { - case x: IRI => x.getIRI - case x: Literal => x.getLexicalForm - case x => x.toString - }.mkString("\t")) + .map( + _._2 + .map { + case x: IRI => x.getIRI + case x: Literal => x.getLexicalForm + case x => x.toString + } + .mkString("\t") + ) .mkString("\n") s"$header\n$body" } diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala index 31cc850..76f720c 100644 --- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala +++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala @@ -37,8 +37,8 @@ object RDFoxUtil { /** Type alias for a collection of answers to a * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]]. */ - private type QueryAnswers = Seq[Seq[Resource]] - private def QueryAnswers() = List.empty[Seq[Resource]] + private type QueryAnswers = Seq[(Long, Seq[Resource])] + private def QueryAnswers() = List.empty[(Long, Seq[Resource])] /** Type alias for