aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala16
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala236
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/Logger.scala45
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxUtil.scala72
4 files changed, 240 insertions, 129 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 c7ace0f..60511af 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
10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term} 10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term}
11 11
12/* Local imports */ 12/* Local imports */
13import util.{RDFoxUtil, RSA} 13import util.{Logger, RDFoxUtil, RSA}
14import sparql.ConjunctiveQuery 14import sparql.ConjunctiveQuery
15 15
16object RSAComb extends App { 16object RSAComb extends App {
@@ -53,15 +53,17 @@ object RSAComb extends App {
53 53
54 val ontology = RSAOntology(ontoPaths: _*) 54 val ontology = RSAOntology(ontoPaths: _*)
55 if (ontology.isRSA) { 55 if (ontology.isRSA) {
56 //println("ONTOLOGY IS RSA") 56
57 Logger print "Ontology is RSA!"
57 58
58 /** Read SPARQL query from file */ 59 /** Read SPARQL query from file */
59 val source = io.Source.fromFile(queryPath.getAbsoluteFile) 60 val query = RDFoxUtil.loadQueryFromFile(queryPath.getAbsoluteFile)
60 val query = source.getLines mkString "\n"
61 source.close()
62 61
63 /* Compute answers to query */ 62 /* Compute answers to query */
64 val answers = ConjunctiveQuery.parse(query).map(ontology ask _) 63 ConjunctiveQuery.parse(query).map(ontology ask _) match {
65 answers map (_.toString) foreach println 64 case Some(answers) => Logger print answers
65 case None =>
66 throw new RuntimeException("Submitted query is not conjunctive")
67 }
66 } 68 }
67} 69}
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 7b4b142..8a40e1e 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._
54import uk.ac.ox.cs.rsacomb.suffix._ 54import uk.ac.ox.cs.rsacomb.suffix._
55import uk.ac.ox.cs.rsacomb.sparql._ 55import uk.ac.ox.cs.rsacomb.sparql._
56import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA} 56import uk.ac.ox.cs.rsacomb.util.{RDFoxUtil, RSA}
57import uk.ac.ox.cs.rsacomb.util.Logger
57 58
58object RSAOntology { 59object RSAOntology {
59 60
@@ -107,6 +108,10 @@ class RSAOntology(val ontology: OWLOntology) {
107 108
108 val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox 109 val axioms: List[OWLLogicalAxiom] = abox ::: tbox ::: rbox
109 110
111 Logger.print(s"Original TBox: ${tbox.length} axioms", Logger.DEBUG)
112 Logger.print(s"Original RBox: ${tbox.length} axioms", Logger.DEBUG)
113 Logger.print(s"Original ABox: ${tbox.length} axioms", Logger.DEBUG)
114
110 /* Retrieve individuals in the original ontology 115 /* Retrieve individuals in the original ontology
111 */ 116 */
112 val individuals: List[IRI] = 117 val individuals: List[IRI] =
@@ -141,87 +146,91 @@ class RSAOntology(val ontology: OWLOntology) {
141 * why the ontology might not be RSA. This could help a second 146 * why the ontology might not be RSA. This could help a second
142 * step of approximation of an Horn-ALCHOIQ to RSA 147 * step of approximation of an Horn-ALCHOIQ to RSA
143 */ 148 */
144 lazy val isRSA: Boolean = { 149 lazy val isRSA: Boolean = Logger.timed(
145 150 {
146 val unsafe = this.unsafeRoles 151 val unsafe = this.unsafeRoles
147 152
148 /* DEBUG: print rules in DL syntax and unsafe roles */ 153 // val renderer = new DLSyntaxObjectRenderer()
149 //val renderer = new DLSyntaxObjectRenderer() 154 // println()
150 //println("\nDL rules:") 155 // println("Unsafe roles:")
151 //axioms.foreach(x => println(renderer.render(x))) 156 // println(unsafe)
152 //println("\nUnsafe roles:") 157 // println()
153 //println(unsafe) 158 // println("DL rules:")
154 159 // tbox.foreach(x => println(renderer.render(x)))
155 object RSAConverter extends RDFoxConverter { 160
156 161 object RSAConverter extends RDFoxConverter {
157 override def convert( 162
158 expr: OWLClassExpression, 163 override def convert(
159 term: Term, 164 expr: OWLClassExpression,
160 unsafe: List[OWLObjectPropertyExpression], 165 term: Term,
161 skolem: SkolemStrategy, 166 unsafe: List[OWLObjectPropertyExpression],
162 suffix: RSASuffix 167 skolem: SkolemStrategy,
163 ): Shards = 168 suffix: RSASuffix
164 (expr, skolem) match { 169 ): Shards =
165 170 (expr, skolem) match {
166 case (e: OWLObjectSomeValuesFrom, c: Constant) 171
167 if unsafe contains e.getProperty => { 172 case (e: OWLObjectSomeValuesFrom, c: Constant)
168 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 173 if unsafe contains e.getProperty => {
169 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 174 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
175 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
176 }
177
178 case (e: OWLDataSomeValuesFrom, c: Constant)
179 if unsafe contains e.getProperty => {
180 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
181 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
182 }
183
184 case _ => super.convert(expr, term, unsafe, skolem, suffix)
170 } 185 }
171 186
172 case (e: OWLDataSomeValuesFrom, c: Constant) 187 }
173 if unsafe contains e.getProperty => { 188
174 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 189 /* Ontology convertion into LP rules */
175 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 190 val term = RSAOntology.genFreshVariable()
176 } 191 val datalog = axioms
177 192 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty))
178 case _ => super.convert(expr, term, unsafe, skolem, suffix) 193 .unzip
179 } 194 val facts = datalog._1.flatten
180 195 val rules = datalog._2.flatten
181 } 196
182 197 //println("Datalog rules:")
183 /* Ontology convertion into LP rules */ 198 //rules foreach println
184 val term = RSAOntology.genFreshVariable() 199
185 val datalog = axioms 200 // Open connection with RDFox
186 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) 201 val (server, data) = RDFoxUtil.openConnection("RSACheck")
187 .unzip 202
188 val facts = datalog._1.flatten 203 /* Add built-in rules
189 val rules = datalog._2.flatten 204 * TODO: substitute with RDFoxUtil.addRules
190 205 */
191 /* DEBUG: print datalog rules */ 206 data.importData(
192 //println("\nDatalog rules:") 207 UpdateType.ADDITION,
193 //rules.foreach(println) 208 RSA.Prefixes,
194 209 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
195 // Open connection with RDFox 210 )
196 val (server, data) = RDFoxUtil.openConnection("RSACheck")
197
198 /* Add built-in rules
199 * TODO: substitute with RDFoxUtil.addRules
200 */
201 data.importData(
202 UpdateType.ADDITION,
203 RSA.Prefixes,
204 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
205 )
206
207 /* Add ontology facts and rules */
208 RDFoxUtil.addFacts(data, facts)
209 RDFoxUtil.addRules(data, rules)
210
211 /* Build graph */
212 val graph = this.rsaGraph(data);
213 //println(graph)
214
215 // Close connection to RDFox
216 RDFoxUtil.closeConnection(server, data)
217 211
218 /* To check if the graph is tree-like we check for acyclicity in a 212 /* Add ontology facts and rules */
219 * undirected graph. 213 RDFoxUtil.addFacts(data, facts)
220 * 214 RDFoxUtil.addRules(data, rules)
221 * TODO: Implement additional checks (taking into account equality) 215
222 */ 216 /* Build graph */
223 graph.isAcyclic 217 val graph = this.rsaGraph(data);
224 } 218 //println("Graph:")
219 //println(graph)
220
221 // Close connection to RDFox
222 RDFoxUtil.closeConnection(server, data)
223
224 /* To check if the graph is tree-like we check for acyclicity in a
225 * undirected graph.
226 *
227 * TODO: Implement additional checks (taking into account equality)
228 */
229 graph.isAcyclic
230 },
231 "RSA check",
232 Logger.DEBUG
233 )
225 234
226 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { 235 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = {
227 236
@@ -288,9 +297,17 @@ class RSAOntology(val ontology: OWLOntology) {
288 } 297 }
289 298
290 def filteringProgram(query: ConjunctiveQuery): FilteringProgram = 299 def filteringProgram(query: ConjunctiveQuery): FilteringProgram =
291 new FilteringProgram(query, individuals) 300 Logger.timed(
301 new FilteringProgram(query, individuals),
302 "Generating filtering program",
303 Logger.DEBUG
304 )
292 305
293 lazy val canonicalModel = new CanonicalModel(this) 306 lazy val canonicalModel = Logger.timed(
307 new CanonicalModel(this),
308 "Generating canonical model program",
309 Logger.DEBUG
310 )
294 311
295 // TODO: the following functions needs testing 312 // TODO: the following functions needs testing
296 def confl( 313 def confl(
@@ -321,27 +338,44 @@ class RSAOntology(val ontology: OWLOntology) {
321 * @param query query to execute 338 * @param query query to execute
322 * @return a collection of answers 339 * @return a collection of answers
323 */ 340 */
324 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { 341 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed(
325 import implicits.JavaCollections._ 342 {
326 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 343 import implicits.JavaCollections._
327 val filter = this.filteringProgram(query) 344 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
328 RDFoxUtil.addRules(data, this.canonicalModel.rules) 345 val canon = this.canonicalModel
329 RDFoxUtil.addFacts(data, this.canonicalModel.facts) 346 val filter = this.filteringProgram(query)
330 RDFoxUtil.addRules(data, filter.rules) 347
331 RDFoxUtil.addFacts(data, filter.facts) 348 Logger print s"Canonical model: ${canon.rules.length} rules"
332 val answers = RDFoxUtil 349 RDFoxUtil.addRules(data, this.canonicalModel.rules)
333 .submitQuery( 350
334 data, 351 Logger print s"Canonical model: ${canon.facts.length} facts"
335 RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), 352 RDFoxUtil.addFacts(data, this.canonicalModel.facts)
336 RSA.Prefixes 353
337 ) 354 RDFoxUtil materialize data
338 .map( 355 RDFoxUtil printStatisticsFor data
339 new ConjunctiveQueryAnswers(query.bcq, query.variables, _) 356
340 ) 357 Logger print s"Filtering program: ${filter.rules.length} rules"
341 .get 358 RDFoxUtil.addRules(data, filter.rules)
342 RDFoxUtil.closeConnection(server, data) 359
343 answers 360 Logger print s"Filtering program: ${filter.facts.length} facts"
344 } 361 RDFoxUtil.addFacts(data, filter.facts)
362
363 RDFoxUtil materialize data
364 RDFoxUtil printStatisticsFor data
365
366 val answers = {
367 val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size)
368 RDFoxUtil
369 .submitQuery(data, ans, RSA.Prefixes)
370 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _))
371 .get
372 }
373 RDFoxUtil.closeConnection(server, data)
374 answers
375 },
376 "Answers computation",
377 Logger.DEBUG
378 )
345 379
346 /** Query the RDFox data store used for query answering. 380 /** Query the RDFox data store used for query answering.
347 * 381 *
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 @@
1package uk.ac.ox.cs.rsacomb.util
2
3import java.util.Calendar
4import java.io.PrintStream
5
6/** Rough implementation of a logger.
7 *
8 * This is a WIP class for debugging and benchmarking.
9 */
10object Logger {
11
12 /** Output stream for the logger. */
13 var output: PrintStream = System.out
14
15 /** Logger levels (i.e., verbosity of output) */
16 sealed abstract class Level(val level: Int, val name: String)
17 extends Ordered[Level] {
18 def compare(that: Level) = this.level - that.level
19 override def toString = name
20 }
21 case object QUIET extends Level(0, "normal")
22 case object NORMAL extends Level(1, "normal")
23 case object DEBUG extends Level(2, "debug")
24 case object VERBOSE extends Level(3, "verbose")
25
26 /** Currend logger level */
27 var level: Level = DEBUG
28
29 def print(str: Any, lvl: Level = NORMAL): Unit = {
30 if (lvl <= level) {
31 val time = Calendar.getInstance.getTime
32 output println s"[$lvl][$time] $str"
33 }
34 }
35
36 def timed[A](expr: => A, desc: String = "", lvl: Level = NORMAL): A = {
37 val t0 = System.currentTimeMillis()
38 print(s"$desc (START)", lvl)
39 val result = expr
40 val t1 = System.currentTimeMillis()
41 print(s"$desc (END): ${(t1 - t0).toFloat / 1000}s", lvl)
42 result
43 }
44
45}
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..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
@@ -1,5 +1,6 @@
1package uk.ac.ox.cs.rsacomb.util 1package uk.ac.ox.cs.rsacomb.util
2 2
3import java.io.File
3import java.io.StringReader 4import java.io.StringReader
4import tech.oxfordsemantic.jrdfox.Prefixes 5import tech.oxfordsemantic.jrdfox.Prefixes
5import tech.oxfordsemantic.jrdfox.client.{ 6import tech.oxfordsemantic.jrdfox.client.{
@@ -20,6 +21,7 @@ import tech.oxfordsemantic.jrdfox.logic.datalog.{
20import tech.oxfordsemantic.jrdfox.logic.expression.{Resource} 21import tech.oxfordsemantic.jrdfox.logic.expression.{Resource}
21import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery 22import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
22import uk.ac.ox.cs.rsacomb.suffix.Nth 23import uk.ac.ox.cs.rsacomb.suffix.Nth
24import uk.ac.ox.cs.rsacomb.util.Logger
23 25
24/** A collection of helper methods for RDFox */ 26/** A collection of helper methods for RDFox */
25object RDFoxUtil { 27object RDFoxUtil {
@@ -67,18 +69,21 @@ object RDFoxUtil {
67 (server, data) 69 (server, data)
68 } 70 }
69 71
70 /** Gather statistics from RDFox datastore. 72 /** Prints statistics from RDFox datastore.
73 *
74 * Prints something only when Logger level is set to DEBUG or more.
71 * 75 *
72 * @see [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#in-depth-diagnostic-information]] 76 * @see [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#in-depth-diagnostic-information]]
73 * and [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#managing-statistics]] 77 * and [[https://docs.oxfordsemantic.tech/programmatic-access-APIs.html#managing-statistics]]
74 * for more ways of gathering diagnostics from RDFox. 78 * for more ways of gathering diagnostics from RDFox.
75 */ 79 */
76 def gatherStatistics(data: DataStoreConnection): String = { 80 def printStatisticsFor(data: DataStoreConnection): Unit = {
77 val info = data.getComponentInfo(true) 81 val info = data.getComponentInfo(true)
78 s"${info.getName}: ${info.getPropertyValues}" 82 val stats = s"${info.getName}: ${info.getPropertyValues}"
79 .replaceAll("\\{", "{\n ") 83 .replaceAll("\\{", "{\n ")
80 .replaceAll(", ", ",\n ") 84 .replaceAll(", ", ",\n ")
81 .replaceAll("\\}", "\n}") 85 .replaceAll("\\}", "\n}")
86 Logger.print(stats, Logger.DEBUG)
82 } 87 }
83 88
84 /** Adds a collection of rules to a data store. 89 /** Adds a collection of rules to a data store.
@@ -87,7 +92,11 @@ object RDFoxUtil {
87 * @param rules collection of rules to be added to the data store 92 * @param rules collection of rules to be added to the data store
88 */ 93 */
89 def addRules(data: DataStoreConnection, rules: Seq[Rule]): Unit = 94 def addRules(data: DataStoreConnection, rules: Seq[Rule]): Unit =
90 data addRules rules 95 Logger.timed(
96 data addRules rules,
97 "Loading rules",
98 Logger.DEBUG
99 )
91 100
92 /** Adds a collection of facts to a data store. 101 /** Adds a collection of facts to a data store.
93 * 102 *
@@ -95,12 +104,29 @@ object RDFoxUtil {
95 * @param facts collection of facts to be added to the data store 104 * @param facts collection of facts to be added to the data store
96 */ 105 */
97 def addFacts(data: DataStoreConnection, facts: Seq[TupleTableAtom]): Unit = 106 def addFacts(data: DataStoreConnection, facts: Seq[TupleTableAtom]): Unit =
98 data.importData( 107 Logger.timed(
99 UpdateType.ADDITION, 108 data.importData(
100 RSA.Prefixes, 109 UpdateType.ADDITION,
101 facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".") 110 RSA.Prefixes,
111 facts.map(_.toString(Prefixes.s_emptyPrefixes)).mkString("", ".\n", ".")
112 ),
113 "Loading facts",
114 Logger.DEBUG
102 ) 115 )
103 116
117 /** Force materialization in RDFox. */
118 def materialize(data: DataStoreConnection): Unit =
119 Logger.timed(data.updateMaterialization(), "Materialization", Logger.DEBUG)
120
121 /** Load SPARQL query from file. */
122 def loadQueryFromFile(file: File): String = {
123 val source = io.Source.fromFile(file)
124 val query = source.getLines mkString "\n"
125 Logger print s"Loaded query:\n$query"
126 source.close()
127 query
128 }
129
104 /** Parse a SELECT query from a string in SPARQL format. 130 /** Parse a SELECT query from a string in SPARQL format.
105 * 131 *
106 * @param query the string containing the SPARQL query 132 * @param query the string containing the SPARQL query
@@ -136,19 +162,23 @@ object RDFoxUtil {
136 data: DataStoreConnection, 162 data: DataStoreConnection,
137 query: SelectQuery, 163 query: SelectQuery,
138 opts: RDFoxOpts = RDFoxOpts() 164 opts: RDFoxOpts = RDFoxOpts()
139 ): QueryAnswers = { 165 ): QueryAnswers = Logger.timed(
140 val cursor = data.createCursor(query, opts) 166 {
141 var answers = QueryAnswers() 167 val cursor = data.createCursor(query, opts)
142 var mul = cursor.open() 168 var answers = QueryAnswers()
143 while (mul > 0) { 169 var mul = cursor.open()
144 val answer = 170 while (mul > 0) {
145 (0 until cursor.getArity).map(cursor.getResource(_)).toList 171 val answer =
146 answers = answer :: answers 172 (0 until cursor.getArity).map(cursor.getResource(_)).toList
147 mul = cursor.advance() 173 answers = answer :: answers
148 } 174 mul = cursor.advance()
149 cursor.close(); 175 }
150 answers 176 cursor.close();
151 } 177 answers
178 },
179 "Answer query",
180 Logger.DEBUG
181 )
152 182
153 /** Execute a query over a given datastore connection. 183 /** Execute a query over a given datastore connection.
154 * 184 *