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.scala230
-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.scala68
4 files changed, 230 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 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
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(query).map(ontology ask _) 63 ConjunctiveQuery(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 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._
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
@@ -141,87 +142,91 @@ class RSAOntology(val ontology: OWLOntology) {
141 * why the ontology might not be RSA. This could help a second 142 * why the ontology might not be RSA. This could help a second
142 * step of approximation of an Horn-ALCHOIQ to RSA 143 * step of approximation of an Horn-ALCHOIQ to RSA
143 */ 144 */
144 lazy val isRSA: Boolean = { 145 lazy val isRSA: Boolean = Logger.timed(
145 146 {
146 val unsafe = this.unsafeRoles 147 val unsafe = this.unsafeRoles
147 148
148 /* DEBUG: print rules in DL syntax and unsafe roles */ 149 // val renderer = new DLSyntaxObjectRenderer()
149 //val renderer = new DLSyntaxObjectRenderer() 150 // println()
150 //println("\nDL rules:") 151 // println("Unsafe roles:")
151 //axioms.foreach(x => println(renderer.render(x))) 152 // println(unsafe)
152 //println("\nUnsafe roles:") 153 // println()
153 //println(unsafe) 154 // println("DL rules:")
154 155 // tbox.foreach(x => println(renderer.render(x)))
155 object RSAConverter extends RDFoxConverter { 156
156 157 object RSAConverter extends RDFoxConverter {
157 override def convert( 158
158 expr: OWLClassExpression, 159 override def convert(
159 term: Term, 160 expr: OWLClassExpression,
160 unsafe: List[OWLObjectPropertyExpression], 161 term: Term,
161 skolem: SkolemStrategy, 162 unsafe: List[OWLObjectPropertyExpression],
162 suffix: RSASuffix 163 skolem: SkolemStrategy,
163 ): Shards = 164 suffix: RSASuffix
164 (expr, skolem) match { 165 ): Shards =
165 166 (expr, skolem) match {
166 case (e: OWLObjectSomeValuesFrom, c: Constant) 167
167 if unsafe contains e.getProperty => { 168 case (e: OWLObjectSomeValuesFrom, c: Constant)
168 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 169 if unsafe contains e.getProperty => {
169 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 170 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
170 } 171 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
171 172 }
172 case (e: OWLDataSomeValuesFrom, c: Constant) 173
173 if unsafe contains e.getProperty => { 174 case (e: OWLDataSomeValuesFrom, c: Constant)
174 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix) 175 if unsafe contains e.getProperty => {
175 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext) 176 val (res, ext) = super.convert(e, term, unsafe, skolem, suffix)
177 (RSA.PE(term, c.iri) :: RSA.U(c.iri) :: res, ext)
178 }
179
180 case _ => super.convert(expr, term, unsafe, skolem, suffix)
176 } 181 }
177 182
178 case _ => super.convert(expr, term, unsafe, skolem, suffix) 183 }
179 } 184
180 185 /* Ontology convertion into LP rules */
181 } 186 val term = RSAOntology.genFreshVariable()
182 187 val datalog = axioms
183 /* Ontology convertion into LP rules */ 188 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty))
184 val term = RSAOntology.genFreshVariable() 189 .unzip
185 val datalog = axioms 190 val facts = datalog._1.flatten
186 .map(a => RSAConverter.convert(a, term, unsafe, new Constant(a), Empty)) 191 val rules = datalog._2.flatten
187 .unzip 192
188 val facts = datalog._1.flatten 193 //println("Datalog rules:")
189 val rules = datalog._2.flatten 194 //rules foreach println
190 195
191 /* DEBUG: print datalog rules */ 196 // Open connection with RDFox
192 //println("\nDatalog rules:") 197 val (server, data) = RDFoxUtil.openConnection("RSACheck")
193 //rules.foreach(println) 198
194 199 /* Add built-in rules
195 // Open connection with RDFox 200 * TODO: substitute with RDFoxUtil.addRules
196 val (server, data) = RDFoxUtil.openConnection("RSACheck") 201 */
197 202 data.importData(
198 /* Add built-in rules 203 UpdateType.ADDITION,
199 * TODO: substitute with RDFoxUtil.addRules 204 RSA.Prefixes,
200 */ 205 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
201 data.importData( 206 )
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 207
218 /* To check if the graph is tree-like we check for acyclicity in a 208 /* Add ontology facts and rules */
219 * undirected graph. 209 RDFoxUtil.addFacts(data, facts)
220 * 210 RDFoxUtil.addRules(data, rules)
221 * TODO: Implement additional checks (taking into account equality) 211
222 */ 212 /* Build graph */
223 graph.isAcyclic 213 val graph = this.rsaGraph(data);
224 } 214 //println("Graph:")
215 //println(graph)
216
217 // Close connection to RDFox
218 RDFoxUtil.closeConnection(server, data)
219
220 /* To check if the graph is tree-like we check for acyclicity in a
221 * undirected graph.
222 *
223 * TODO: Implement additional checks (taking into account equality)
224 */
225 graph.isAcyclic
226 },
227 "RSA check",
228 Logger.DEBUG
229 )
225 230
226 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = { 231 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = {
227 232
@@ -288,9 +293,17 @@ class RSAOntology(val ontology: OWLOntology) {
288 } 293 }
289 294
290 def filteringProgram(query: ConjunctiveQuery): FilteringProgram = 295 def filteringProgram(query: ConjunctiveQuery): FilteringProgram =
291 new FilteringProgram(query, individuals) 296 Logger.timed(
297 new FilteringProgram(query, individuals),
298 "Generating filtering program",
299 Logger.DEBUG
300 )
292 301
293 lazy val canonicalModel = new CanonicalModel(this) 302 lazy val canonicalModel = Logger.timed(
303 new CanonicalModel(this),
304 "Generating canonical model program",
305 Logger.DEBUG
306 )
294 307
295 // TODO: the following functions needs testing 308 // TODO: the following functions needs testing
296 def confl( 309 def confl(
@@ -321,27 +334,42 @@ class RSAOntology(val ontology: OWLOntology) {
321 * @param query query to execute 334 * @param query query to execute
322 * @return a collection of answers 335 * @return a collection of answers
323 */ 336 */
324 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = { 337 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = Logger.timed(
325 import implicits.JavaCollections._ 338 {
326 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore) 339 import implicits.JavaCollections._
327 val filter = this.filteringProgram(query) 340 val (server, data) = RDFoxUtil.openConnection(RSAOntology.DataStore)
328 RDFoxUtil.addRules(data, this.canonicalModel.rules) 341 val canon = this.canonicalModel
329 RDFoxUtil.addFacts(data, this.canonicalModel.facts) 342 val filter = this.filteringProgram(query)
330 RDFoxUtil.addRules(data, filter.rules) 343
331 RDFoxUtil.addFacts(data, filter.facts) 344 Logger print s"Canonical model: ${canon.rules.length} rules"
332 val answers = RDFoxUtil 345 RDFoxUtil.addRules(data, this.canonicalModel.rules)
333 .submitQuery( 346
334 data, 347 Logger print s"Canonical model: ${canon.facts.length} facts"
335 RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size), 348 RDFoxUtil.addFacts(data, this.canonicalModel.facts)
336 RSA.Prefixes 349
337 ) 350 RDFoxUtil printStatisticsFor data
338 .map( 351
339 new ConjunctiveQueryAnswers(query.bcq, _) 352 Logger print s"Filtering program: ${filter.rules.length} rules"
340 ) 353 RDFoxUtil.addRules(data, filter.rules)
341 .get 354
342 RDFoxUtil.closeConnection(server, data) 355 Logger print s"Filtering program: ${filter.facts.length} facts"
343 answers 356 RDFoxUtil.addFacts(data, filter.facts)
344 } 357
358 RDFoxUtil printStatistics data
359
360 val answers = {
361 val ans = RDFoxUtil.buildDescriptionQuery("Ans", query.answer.size)
362 RDFoxUtil
363 .submitQuery(data, ans, RSA.Prefixes)
364 .map(new ConjunctiveQueryAnswers(query.bcq, query.variables, _))
365 .get
366 }
367 RDFoxUtil.closeConnection(server, data)
368 answers
369 },
370 "Answers computation",
371 Logger.DEBUG
372 )
345 373
346 /** Query the RDFox data store used for query answering. 374 /** Query the RDFox data store used for query answering.
347 * 375 *
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..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 @@
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,25 @@ 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 /** Load SPARQL query from file. */
118 def loadQueryFromFile(file: File): String = {
119 val source = io.Source.fromFile(file)
120 val query = source.getLines mkString "\n"
121 Logger print s"Loaded query:\n$query"
122 source.close()
123 query
124 }
125
104 /** Parse a SELECT query from a string in SPARQL format. 126 /** Parse a SELECT query from a string in SPARQL format.
105 * 127 *
106 * @param query the string containing the SPARQL query 128 * @param query the string containing the SPARQL query
@@ -136,19 +158,23 @@ object RDFoxUtil {
136 data: DataStoreConnection, 158 data: DataStoreConnection,
137 query: SelectQuery, 159 query: SelectQuery,
138 opts: RDFoxOpts = RDFoxOpts() 160 opts: RDFoxOpts = RDFoxOpts()
139 ): QueryAnswers = { 161 ): QueryAnswers = Logger.timed(
140 val cursor = data.createCursor(query, opts) 162 {
141 var answers = QueryAnswers() 163 val cursor = data.createCursor(query, opts)
142 var mul = cursor.open() 164 var answers = QueryAnswers()
143 while (mul > 0) { 165 var mul = cursor.open()
144 val answer = 166 while (mul > 0) {
145 (0 until cursor.getArity).map(cursor.getResource(_)).toList 167 val answer =
146 answers = answer :: answers 168 (0 until cursor.getArity).map(cursor.getResource(_)).toList
147 mul = cursor.advance() 169 answers = answer :: answers
148 } 170 mul = cursor.advance()
149 cursor.close(); 171 }
150 answers 172 cursor.close();
151 } 173 answers
174 },
175 "Answer query",
176 Logger.DEBUG
177 )
152 178
153 /** Execute a query over a given datastore connection. 179 /** Execute a query over a given datastore connection.
154 * 180 *