aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/uk/ac/ox/cs/rsacomb
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-20 13:10:44 +0000
committerFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-20 13:11:14 +0000
commit0ac35ab057257eadc297d430666d0c1da41756f6 (patch)
treefbbd64a74675989039c9c9c36e85b9aadd21fb97 /src/main/scala/uk/ac/ox/cs/rsacomb
parentc804109db7bb08cce1246acbca2030c3b979f242 (diff)
downloadRSAComb-0ac35ab057257eadc297d430666d0c1da41756f6.tar.gz
RSAComb-0ac35ab057257eadc297d430666d0c1da41756f6.zip
Simplify workflow for query execution
Input query is now read from file.
Diffstat (limited to 'src/main/scala/uk/ac/ox/cs/rsacomb')
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala186
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala55
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala98
3 files changed, 121 insertions, 218 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 db52793..678a5fa 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/Main.scala
@@ -46,9 +46,7 @@ object RSAComb extends App {
46 sys.exit; 46 sys.exit;
47 } 47 }
48 48
49 /* Create RSA object from generic OWLOntology 49 /* TODO: It might be required to check if the ontology in input is
50 *
51 * TODO: It might be required to check if the ontology in input is
52 * Horn-ALCHOIQ. At the moment we are assuming this is always the 50 * Horn-ALCHOIQ. At the moment we are assuming this is always the
53 * case. 51 * case.
54 */ 52 */
@@ -56,183 +54,13 @@ object RSAComb extends App {
56 val ontology = RSAOntology(ontoPath) 54 val ontology = RSAOntology(ontoPath)
57 if (ontology.isRSA) { 55 if (ontology.isRSA) {
58 56
59 /* Load query */ 57 /** Read SPARQL query from file */
60 val query = """ 58 val source = io.Source.fromFile(queryPath.getAbsoluteFile)
61 PREFIX : <http://example.com/rsa_example.owl#> 59 val query = source.getLines mkString "\n"
62 60 source.close()
63 SELECT ?X
64 WHERE {
65 ?X a :D ;
66 :R ?Y .
67 ?Y :S ?Z .
68 ?Z a :D .
69 }
70 """
71 61
72 /* Compute answers to query */ 62 /* Compute answers to query */
73 ConjunctiveQuery(query) match { 63 val answers = ConjunctiveQuery(query).map(ontology ask _)
74 case Some(query) => { 64 answers map (_.toString) foreach println
75
76 import implicits.JavaCollections._
77
78 // Open connection to RDFox
79 val (server, data) = RDFoxHelpers.openConnection("AnswerComputation")
80
81 {
82 println("\nQuery")
83 println(query)
84 }
85
86 val canon = ontology.canonicalModel
87 data.addRules(canon.rules)
88 val filter = ontology.filteringProgram(query)
89 data.addRules(filter.rules)
90
91 {
92 println("\nCanonical Model rules:")
93 canon.rules.foreach(println)
94 println("\nFiltering rules")
95 filter.rules.foreach(println)
96 }
97
98 // Retrieve answers
99 println("\nAnswers:")
100 val ans =
101 RDFoxHelpers.queryInternalPredicate(data, "Ans", filter.answer.length)
102 println(ans)
103
104 /* DEBUG: adding additional checks
105 */
106 {
107 import suffix.{Forward, Backward}
108
109 val arity = filter.answer.length + filter.bounded.length
110
111 println("\nIndividuals:")
112 ontology.individuals.foreach(println)
113
114 println("\nThings:")
115 val things = RDFoxHelpers.submitQuery(
116 data,
117 """
118 PREFIX owl: <http://www.w3.org/2002/07/owl#>
119
120 SELECT ?X {
121 ?X a owl:Thing
122 }
123 """
124 )
125 println(things)
126
127 println("\nNAMEDs:")
128 val named = RDFoxHelpers.submitQuery(
129 data,
130 """
131 SELECT ?X {
132 ?X a rsa:Named
133 }
134 """,
135 RSA.Prefixes
136 )
137 println(named)
138
139 println("\nNIs:")
140 val nis = RDFoxHelpers.submitQuery(
141 data,
142 """
143 SELECT ?X {
144 ?X a rsa:NI
145 }
146 """,
147 RSA.Prefixes
148 )
149 println(nis)
150
151 // ID instances
152 println("\nIDs:")
153 val ids = RDFoxHelpers.queryInternalPredicate(
154 data,
155 "ID",
156 arity + 2
157 )
158 println(ids)
159
160 println("\nCongruent:")
161 val equivs = RDFoxHelpers.submitQuery(
162 data,
163 """
164 SELECT ?X ?Y {
165 ?X rsa:congruent ?Y
166 }
167 """,
168 RSA.Prefixes
169 )
170 println(equivs)
171
172 // Unfiltered answers
173 println("\nPossible answers:")
174 val qms = RDFoxHelpers.queryInternalPredicate(
175 data,
176 "QM",
177 arity
178 )
179 println(qms)
180
181 // Cycle detected
182 println("\nCycle detection:")
183 val aqf = RDFoxHelpers.queryInternalPredicate(
184 data,
185 "AQ" :: Forward,
186 arity + 2
187 )
188 val aqb = RDFoxHelpers.queryInternalPredicate(
189 data,
190 "AQ" :: Backward,
191 arity + 2
192 )
193 println(aqf)
194 println(aqb)
195
196 // Forks detected
197 println("\nForks:")
198 val fk = RDFoxHelpers.queryInternalPredicate(
199 data,
200 "FK",
201 arity
202 )
203 println(fk)
204
205 // Spurious answers
206 println("\nSpurious answers")
207 val sp = RDFoxHelpers.queryInternalPredicate(
208 data,
209 "SP",
210 arity
211 )
212 println(sp)
213 }
214
215 // Close connection to RDFox
216 RDFoxHelpers.closeConnection(server, data)
217 }
218 case None => {}
219 }
220 } 65 }
221} 66}
222
223/* Notes:
224 *
225 * To establish a connection with a local RDFox instance, do the
226 * following:
227 *
228 * ```
229 * val serverConnection : ServerConnection = ConnectionFactory.newServerConnection("rdfox:local", "", "")
230 * serverConnection.createDataStore("test","seq",new HashMap())
231 * val dataStoreConnection : DataStoreConnection = serverConnection.newDataStoreConnection("test")
232 * dataStoreConnection.importData(
233 * UpdateType.ADDITION,
234 * Prefixes.s_emptyPrefixes,
235 * new File("./path/to/file")
236 * )
237 * ```
238 */
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 9640ccc..56fbac3 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/RSAOntology.scala
@@ -1,6 +1,7 @@
1package uk.ac.ox.cs.rsacomb 1package uk.ac.ox.cs.rsacomb
2 2
3/* Java imports */ 3/* Java imports */
4import java.{util => ju}
4import java.util.HashMap 5import java.util.HashMap
5import java.util.stream.{Collectors, Stream} 6import java.util.stream.{Collectors, Stream}
6import java.io.File 7import java.io.File
@@ -48,8 +49,8 @@ import org.semanticweb.owlapi.model.OWLObjectInverseOf
48 49
49import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy} 50import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy}
50import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom 51import uk.ac.ox.cs.rsacomb.implicits.RSAAxiom
51import uk.ac.ox.cs.rsacomb.suffix.{Empty, Forward, Backward, Inverse} 52import uk.ac.ox.cs.rsacomb.suffix._
52import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery 53import uk.ac.ox.cs.rsacomb.sparql._
53import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA} 54import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA}
54 55
55object RSAOntology { 56object RSAOntology {
@@ -256,8 +257,8 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom {
256 ): Graph[Resource, UnDiEdge] = { 257 ): Graph[Resource, UnDiEdge] = {
257 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }" 258 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }"
258 val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get 259 val answers = RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get
259 var edges: List[UnDiEdge[Resource]] = answers.map { 260 var edges: Seq[UnDiEdge[Resource]] = answers.map {
260 case n1 :: n2 :: _ => UnDiEdge(n1, n2) 261 case Seq(n1, n2) => UnDiEdge(n1, n2)
261 } 262 }
262 Graph(edges: _*) 263 Graph(edges: _*)
263 } 264 }
@@ -291,6 +292,52 @@ class RSAOntology(val ontology: OWLOntology) extends RSAAxiom {
291 .filterNot(_.getInverseProperty.isOWLTopObjectProperty()) 292 .filterNot(_.getInverseProperty.isOWLTopObjectProperty())
292 } 293 }
293 294
295 /** Returns the answers to a query
296 *
297 * @param query query to execute
298 * @return a collection of answers
299 */
300 def ask(query: ConjunctiveQuery): ConjunctiveQueryAnswers = {
301 import implicits.JavaCollections._
302 val (server, data) = RDFoxHelpers.openConnection("AnswerComputation")
303 data.addRules(this.canonicalModel.rules)
304 data.addRules(this.filteringProgram(query).rules)
305 new ConjunctiveQueryAnswers(
306 query.boolean,
307 queryInternalPredicate(data, "Ans", query.answer.size)
308 )
309 }
310
311 /** Returns instances of a reified predicate
312 *
313 * Predicated with arity higher than 2 are internally reified to be
314 * compatible with RDFox engine. This helper queries for predicate
315 * instances and returns a set of un-reified answers.
316 *
317 * @param data open datastore connection to RDFox
318 * @param pred name of the predicate
319 * @param arity arity of the predicate
320 * @param opts additional options to RDFox
321 * @return a collection of instances of the given predicate
322 */
323 private def queryInternalPredicate(
324 data: DataStoreConnection,
325 pred: String,
326 arity: Int,
327 opts: ju.Map[String, String] = new ju.HashMap[String, String]()
328 ): Seq[Seq[Resource]] = {
329 val query =
330 if (arity > 0) {
331 (0 until arity).mkString("SELECT ?X", " ?X", "\n") +
332 (0 until arity)
333 .map(i => s"?S rsa:${pred :: Nth(i)} ?X$i .")
334 .mkString("WHERE {\n", "\n", "\n}")
335 } else {
336 s"ASK { ?X a rsa:$pred }"
337 }
338 RDFoxHelpers.submitQuery(data, query, RSA.Prefixes).get
339 }
340
294 def self(axiom: OWLSubClassOfAxiom): Set[Term] = { 341 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
295 // Assuming just one role in the signature of a T5 axiom 342 // Assuming just one role in the signature of a T5 axiom
296 val role = axiom.objectPropertyExpressionsInSignature(0) 343 val role = axiom.objectPropertyExpressionsInSignature(0)
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala
index 5b85436..43e5d28 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RDFoxHelpers.scala
@@ -1,6 +1,5 @@
1package uk.ac.ox.cs.rsacomb.util 1package uk.ac.ox.cs.rsacomb.util
2 2
3import java.util.{Map => JMap, HashMap => JHashMap}
4import java.io.StringReader 3import java.io.StringReader
5import tech.oxfordsemantic.jrdfox.Prefixes 4import tech.oxfordsemantic.jrdfox.Prefixes
6import tech.oxfordsemantic.jrdfox.client.{ 5import tech.oxfordsemantic.jrdfox.client.{
@@ -12,32 +11,52 @@ import tech.oxfordsemantic.jrdfox.formats.SPARQLParser
12import tech.oxfordsemantic.jrdfox.logic.expression.Resource 11import tech.oxfordsemantic.jrdfox.logic.expression.Resource
13import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery 12import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
14 13
15import uk.ac.ox.cs.rsacomb.suffix.Nth 14/** A collection of helper methods for RDFox */
16
17object RDFoxHelpers { 15object RDFoxHelpers {
18 16
17 /** Type alias for a collection of answers to a
18 * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.Query]].
19 */
20 private type QueryAnswers = Seq[Seq[Resource]]
21 private def QueryAnswers() = List.empty[Seq[Resource]]
22
23 /** Type alias for <option => value> RDFox options. */
24 private type RDFoxOpts = java.util.Map[String, String]
25 private def RDFoxOpts() = new java.util.HashMap[String, String]()
26
27 /** Setup a new local connection with RDFox.
28 *
29 * @param dataStore data store identifier
30 * @param opts additional options to RDFox
31 * @return a tuple with the newly opened server and data store
32 * connections.
33 *
34 * @see [[uk.ac.ox.cs.rsacomb.util.RDFoxHelpers.closeConnection
35 * RDFoxHelpers.closeConnection]] for
36 * details on how to close an open connection.
37 */
19 def openConnection( 38 def openConnection(
20 dataStore: String, 39 dataStore: String,
21 opts: JMap[String, String] = new JHashMap[String, String]() 40 opts: RDFoxOpts = RDFoxOpts()
22 ): (ServerConnection, DataStoreConnection) = { 41 ): (ServerConnection, DataStoreConnection) = {
23 /* Create local server connection
24 */
25 val serverUrl = "rdfox:local" 42 val serverUrl = "rdfox:local"
26 val role = "" 43 val role = ""
27 val password = "" 44 val password = ""
28 val server = 45 val server =
29 ConnectionFactory.newServerConnection(serverUrl, role, password) 46 ConnectionFactory.newServerConnection(serverUrl, role, password)
30
31 /* Create datastore connection
32 */
33 // parameters.put("owl-in-rdf-support", "relaxed")
34 // parameters.put("equality", "noUNA")
35 server.createDataStore(dataStore, "par-complex-nn", opts) 47 server.createDataStore(dataStore, "par-complex-nn", opts)
36 val data = server.newDataStoreConnection(dataStore) 48 val data = server.newDataStoreConnection(dataStore)
37
38 (server, data) 49 (server, data)
39 } 50 }
40 51
52 /** Parse a SELECT query from a string in SPARQL format.
53 *
54 * @param query the string containing the SPARQL query
55 * @param prefixes additional prefixes for the query. It defaults to
56 * an empty set.
57 * @return a [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery SelectQuery]]
58 * if the input string is a SELECT query, none otherwise.
59 */
41 def parseSelectQuery( 60 def parseSelectQuery(
42 query: String, 61 query: String,
43 prefixes: Prefixes = new Prefixes() 62 prefixes: Prefixes = new Prefixes()
@@ -52,13 +71,22 @@ object RDFoxHelpers {
52 } 71 }
53 } 72 }
54 73
74 /** Execute a query over a given datastore connection.
75 *
76 * @param data RDFox datastore connection.
77 * @param query a
78 * [[tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery SelectQuery]]
79 * to be executed.
80 * @param opts additional options to RDFox.
81 * @returns a collection of answers to the query.
82 */
55 def submitSelectQuery( 83 def submitSelectQuery(
56 data: DataStoreConnection, 84 data: DataStoreConnection,
57 query: SelectQuery, 85 query: SelectQuery,
58 opts: JMap[String, String] = new JHashMap[String, String]() 86 opts: RDFoxOpts = RDFoxOpts()
59 ): List[List[Resource]] = { 87 ): QueryAnswers = {
60 val cursor = data.createCursor(query, opts) 88 val cursor = data.createCursor(query, opts)
61 var answers: List[List[Resource]] = List() 89 var answers = QueryAnswers()
62 var mul = cursor.open() 90 var mul = cursor.open()
63 while (mul > 0) { 91 while (mul > 0) {
64 val answer = 92 val answer =
@@ -70,32 +98,32 @@ object RDFoxHelpers {
70 answers 98 answers
71 } 99 }
72 100
101 /** Execute a query over a given datastore connection.
102 *
103 * @param data RDFox datastore connection.
104 * @param query a string representing a SPARQL query.
105 * @param prefixes additional prefixes for the query. It defaults to
106 * an empty set.
107 * @param opts additional options to RDFox.
108 * @returns a collection of answers to the query if the input query
109 * is a SELECT query, none otherwise.
110 */
73 def submitQuery( 111 def submitQuery(
74 data: DataStoreConnection, 112 data: DataStoreConnection,
75 query: String, 113 query: String,
76 prefixes: Prefixes = new Prefixes(), 114 prefixes: Prefixes = new Prefixes(),
77 opts: JMap[String, String] = new JHashMap[String, String]() 115 opts: RDFoxOpts = RDFoxOpts()
78 ): Option[List[List[Resource]]] = 116 ): Option[QueryAnswers] =
79 parseSelectQuery(query, prefixes).map(submitSelectQuery(data, _, opts)) 117 parseSelectQuery(query, prefixes).map(submitSelectQuery(data, _, opts))
80 118
81 def queryInternalPredicate( 119 /** Close an open connection to RDFox.
82 data: DataStoreConnection, 120 *
83 pred: String, 121 * @param server server connection
84 arity: Int, 122 * @param data data store connections
85 opts: JMap[String, String] = new JHashMap[String, String]() 123 *
86 ): List[List[Resource]] = { 124 * @see [[uk.ac.ox.cs.rsacomb.util.RDFoxHelpers.openConnection RDFoxHelpers.openConnection]]
87 var query = "SELECT" 125 * for details on how to create a new connection with RDFox.
88 for (i <- 0 until arity) { 126 */
89 query ++= s" ?X$i"
90 }
91 query ++= " WHERE {"
92 for (i <- 0 until arity) {
93 query ++= s" ?S rsa:${pred :: Nth(i)} ?X$i ."
94 }
95 query ++= " }"
96 submitQuery(data, query, RSA.Prefixes, opts).get
97 }
98
99 def closeConnection( 127 def closeConnection(
100 server: ServerConnection, 128 server: ServerConnection,
101 data: DataStoreConnection 129 data: DataStoreConnection