aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala532
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala39
-rw-r--r--src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala50
3 files changed, 316 insertions, 305 deletions
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala
index 6df30fd..055cf2a 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/FilteringProgram.scala
@@ -32,298 +32,304 @@ import uk.ac.ox.cs.rsacomb.suffix.{RSASuffix, Forward, Backward}
32import uk.ac.ox.cs.rsacomb.util.RSA 32import uk.ac.ox.cs.rsacomb.util.RSA
33import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery 33import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery
34 34
35/** Factory for [[uk.ac.ox.cs.rsacomb.FilteringProgram FilteringProgram]] */
35object FilteringProgram { 36object FilteringProgram {
36 37
38 /** Create a new FilteringProgram instance.
39 *
40 * @param query CQ to be converted into logic rules.
41 * @param constants constants in the original ontology. They will be
42 * used to initialize predicate `rsa:Named`.
43 */
37 def apply(query: ConjunctiveQuery, constants: List[Term]): FilteringProgram = 44 def apply(query: ConjunctiveQuery, constants: List[Term]): FilteringProgram =
38 new FilteringProgram(query, constants) 45 new FilteringProgram(query, constants)
39 46
40} // object FilteringProgram 47}
41 48
49/** Filtering Program generator
50 *
51 * Handles the conversion of a CQ into a set of logic rules,
52 * representing the filtering step of the RSA combined approach.
53 *
54 * Instances can be created using the companion object.
55 */
42class FilteringProgram(query: ConjunctiveQuery, constants: List[Term]) 56class FilteringProgram(query: ConjunctiveQuery, constants: List[Term])
43 extends RSAAtom { 57 extends RSAAtom {
44 58
45 /* Makes mplicit conversion OWLAPI IRI <-> RDFox IRI available */ 59 /** Implicit parameter used in RSA internal predicates.
46 // import implicits.RDFox._ 60 *
47 61 * @see [[uk.ac.ox.cs.rsacomb.util.RSA]] for more information.
48 val answer: List[Term] = query.answer.toList 62 */
49 val bounded: List[Term] = query.bounded.toList 63 implicit private[this] val _query = query
50 implicit val variables = (answer, bounded)
51 64
52 val named: List[Rule] = 65 /** General purpose variables used in rule instantiation.
53 constants.map(a => Rule.create(RSA.Named(a))) 66 *
67 * This is done to avoid creating new variables every time.
68 */
69 private val varX = Variable.create("X")
70 private val varY = Variable.create("Y")
71 private val varZ = Variable.create("Z")
72 private val varV = Variable.create("V")
73 private val varU = Variable.create("U")
74 private val varW = Variable.create("W")
54 75
55 val nis: Rule = { 76 /** Rule generating the instances of the predicate `rsa:NI`.
56 val varX = Variable.create("X") 77 *
57 val varY = Variable.create("Y") 78 * According to the original paper, the set of `rsa:NI` is defined as
79 * the set of constants that are equal (w.r.t. the congruence
80 * relation represented by `rsa:Congruent`) to a constant in the
81 * original ontology.
82 *
83 * @note that the set of `rsa:Named` constants is always a subset of
84 * the set of `rsa:NI`s.
85 *
86 * @note in the paper, instances of `rsa:NI` are introduced as facts
87 * during the canonical model computation. By definition of the
88 * predicate, this is not feasible, and the instances are instead
89 * generate in the filtering program using a logic rule.
90 */
91 val nis: Rule =
58 Rule.create(RSA.NI(varX), RSA.Congruent(varX, varY), RSA.Named(varY)) 92 Rule.create(RSA.NI(varX), RSA.Congruent(varX, varY), RSA.Named(varY))
59 }
60 93
94 /** Collection of filtering program rules. */
61 val rules: List[Rule] = 95 val rules: List[Rule] =
62 nis :: named ::: this.generateFilteringProgram().map(reifyRule) 96 nis :: {
63 97
64 /* NOTE: we are restricting to queries that contain conjunctions of 98 /** Negates a [[tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom TupleTableAtom]] */
65 * atoms for the time being. This might need to be reviewed in the 99 def not(atom: TupleTableAtom): BodyFormula = Negation.create(atom)
66 * future.
67 */
68 private def queryToBody(body: GroupGraphPattern): List[TupleTableAtom] =
69 body match {
70 case b: ConjunctionPattern => {
71 val conjuncts = b.getConjuncts.asScala.toList
72 conjuncts flatMap { conj =>
73 conj match {
74 case c: TriplePattern =>
75 List(
76 TupleTableAtom.rdf(c.getSubject, c.getPredicate, c.getObject)
77 )
78 case _ => List()
79 }
80 }
81 }
82 case _ => List()
83 }
84 100
85 private def generateFilteringProgram(): List[Rule] = { 101 /** Generates all possible, unfiltered answers.
86 // General purpose variables 102 *
87 val varU = Variable.create("U") 103 * @note corresponds to rule 1 in Table 3 in the paper.
88 val varV = Variable.create("V") 104 */
89 val varW = Variable.create("W") 105 val r1 = reifyRule(Rule.create(RSA.QM, query.atoms: _*))
90 // Query formula as a rule body
91 val body = queryToBody(query.where)
92 // Auxiliar predicates/helpers
93 def not(atom: TupleTableAtom): BodyFormula = Negation.create(atom)
94 // val predQM =
95 // TupleTableAtom.create(
96 // TupleTableName.create(RSA.Qm.getIRI),
97 // (answer ++ bounded): _*
98 // )
99 // def predID(t1: Term, t2: Term) =
100 // TupleTableAtom.create(
101 // TupleTableName.create(RSA.rsa("ID").getIRI),
102 // (answer ++ bounded).appended(t1).appended(t2): _*
103 // )
104 // def predNAMED(t1: Term): TupleTableAtom =
105 // TupleTableAtom.rdf(t1, IRI.RDF_TYPE, RSA.rsa("NAMED"))
106 // def predTQ(t1: Term, t2: Term, sx: RSASuffix) =
107 // TupleTableAtom.create(
108 // TupleTableName.create(RSA.rsa("TQ" :: sx).getIRI),
109 // (answer ++ bounded).appended(t1).appended(t2): _*
110 // )
111 // def predAQ(t1: Term, t2: Term, sx: RSASuffix) =
112 // TupleTableAtom.create(
113 // TupleTableName.create(RSA.rsa("AQ" :: sx).getIRI),
114 // (answer ++ bounded).appended(t1).appended(t2): _*
115 // )
116 // val predFK =
117 // TupleTableAtom.create(
118 // TupleTableName.create(RSA.rsa("FK").getIRI),
119 // (answer ++ bounded): _*
120 // )
121 // val predSP =
122 // TupleTableAtom.create(
123 // TupleTableName.create(RSA.rsa("SP").getIRI),
124 // (answer ++ bounded): _*
125 // )
126 // val predANS =
127 // TupleTableAtom.create(
128 // TupleTableName.create(RSA.rsa("ANS").getIRI),
129 // answer: _*
130 // )
131 106
132 /* Rule 1 */ 107 /** Initializes instances of `rsa:Named`.
133 val r1 = Rule.create(RSA.QM, body: _*) 108 *
109 * They represent the set of constants appearing in the original
110 * ontology.
111 *
112 * @note corresponds to rules 2 in Table 3.
113 */
114 val r2 = constants.map(c => Rule.create(RSA.Named(c)))
134 115
135 /* Rules 3x */ 116 /** Initializes instances of `rsa:ID`.
136 val r3a = 117 *
137 for ((v, i) <- bounded.zipWithIndex) 118 * They are initialized as a minimal congruence relation over the
138 yield Rule.create(RSA.ID(RSA(i), RSA(i)), RSA.QM, not(RSA.NI(v))) 119 * positions of the existential variables in the query which are
139 val r3b = Rule.create(RSA.ID(varV, varU), RSA.ID(varU, varV)) 120 * mapped to anonymous terms.
140 val r3c = 121 *
141 Rule.create(RSA.ID(varU, varW), RSA.ID(varU, varV), RSA.ID(varV, varW)) 122 * @note corresponds to rules 3x in Table 3.
123 */
124 val r3a =
125 for ((v, i) <- query.bounded.zipWithIndex)
126 yield Rule.create(RSA.ID(RSA(i), RSA(i)), RSA.QM, not(RSA.NI(v)))
127 val r3b = Rule.create(RSA.ID(varV, varU), RSA.ID(varU, varV))
128 val r3c =
129 Rule.create(RSA.ID(varU, varW), RSA.ID(varU, varV), RSA.ID(varV, varW))
142 130
143 /* Rules 4x */ 131 /** Detects forks in the canonical model.
144 val r4a = for { 132 *
145 role1 <- body.filter(_.isRoleAssertion) 133 * @note corresponds to rules 4x in Table 3.
146 if bounded contains (role1.getArguments.get(2)) 134 */
147 role2 <- body.filter(_.isRoleAssertion) 135 val r4a = for {
148 if bounded contains (role2.getArguments.get(2)) 136 role1 <- query.atoms filter (_.isRoleAssertion)
149 } yield Rule.create( 137 index1 = query.bounded indexOf (role1.getArguments get 2)
150 RSA.FK, 138 if index1 >= 0
151 role1 << Forward, 139 role2 <- query.atoms filter (_.isRoleAssertion)
152 role2 << Forward, 140 index2 = query.bounded indexOf (role2.getArguments get 2)
153 RSA.ID( 141 if index2 >= 0
154 RSA(bounded.indexOf(role1.getArguments.get(2))), 142 } yield Rule.create(
155 RSA(bounded.indexOf(role2.getArguments.get(2))) 143 RSA.FK,
156 ), 144 role1 << Forward,
157 not(RSA.Congruent(role1.getArguments.get(0), role2.getArguments.get(0))) 145 role2 << Forward,
158 ) 146 RSA.ID(RSA(index1), RSA(index2)),
159 val r4b = for { 147 not(RSA.Congruent(role1.getArguments get 0, role2.getArguments get 0))
160 role1 <- body.filter(_.isRoleAssertion) 148 )
161 if bounded contains (role1.getArguments.get(2)) 149 val r4b = for {
162 role2 <- body.filter(_.isRoleAssertion) 150 role1 <- query.atoms filter (_.isRoleAssertion)
163 if bounded contains (role2.getArguments.get(0)) 151 index1 = query.bounded indexOf (role1.getArguments get 2)
164 } yield Rule.create( 152 if index1 >= 0
165 RSA.FK, 153 role2 <- query.atoms filter (_.isRoleAssertion)
166 role1 << Forward, 154 index2 = query.bounded indexOf (role2.getArguments get 0)
167 role2 << Backward, 155 if index2 >= 0
168 RSA.ID( 156 } yield Rule.create(
169 RSA(bounded.indexOf(role1.getArguments.get(2))), 157 RSA.FK,
170 RSA(bounded.indexOf(role2.getArguments.get(0))) 158 role1 << Forward,
171 ), 159 role2 << Backward,
172 not(RSA.Congruent(role1.getArguments.get(0), role2.getArguments.get(2))) 160 RSA.ID(RSA(index1), RSA(index2)),
173 ) 161 not(RSA.Congruent(role1.getArguments get 0, role2.getArguments get 2))
174 val r4c = for { 162 )
175 role1 <- body.filter(_.isRoleAssertion) 163 val r4c = for {
176 if bounded contains (role1.getArguments.get(0)) 164 role1 <- query.atoms filter (_.isRoleAssertion)
177 role2 <- body.filter(_.isRoleAssertion) 165 index1 = query.bounded indexOf (role1.getArguments get 0)
178 if bounded contains (role2.getArguments.get(0)) 166 if index1 >= 0
179 } yield Rule.create( 167 role2 <- query.atoms filter (_.isRoleAssertion)
180 RSA.FK, 168 index2 = query.bounded indexOf (role2.getArguments get 0)
181 role1 << Backward, 169 if index2 >= 0
182 role2 << Backward, 170 } yield Rule.create(
183 RSA.ID( 171 RSA.FK,
184 RSA(bounded.indexOf(role1.getArguments.get(0))), 172 role1 << Backward,
185 RSA(bounded.indexOf(role2.getArguments.get(0))) 173 role2 << Backward,
186 ), 174 RSA.ID(RSA(index1), RSA(index2)),
187 not(RSA.Congruent(role1.getArguments.get(2), role2.getArguments.get(2))) 175 not(RSA.Congruent(role1.getArguments get 2, role2.getArguments get 2))
188 ) 176 )
189 177
190 /* Rules 5x */ 178 /** Recursively propagates `rsa:ID` predicate.
191 val r5a = for { 179 *
192 role1 <- body.filter(_.isRoleAssertion) 180 * @note corresponds to rules 5x in Table 3.
193 role1arg0 = role1.getArguments.get(0) 181 */
194 role1arg2 = role1.getArguments.get(2) 182 val r5a = for {
195 if bounded contains role1arg0 183 role1 <- query.atoms filter (_.isRoleAssertion)
196 if bounded contains role1arg2 184 r1arg0 = role1.getArguments get 0
197 role2 <- body.filter(_.isRoleAssertion) 185 if query.bounded contains r1arg0
198 role2arg0 = role2.getArguments.get(0) 186 r1arg2 = role1.getArguments get 2
199 role2arg2 = role2.getArguments.get(2) 187 if query.bounded contains r1arg2
200 if bounded contains role2arg0 188 role2 <- query.atoms filter (_.isRoleAssertion)
201 if bounded contains role2arg2 189 r2arg0 = role2.getArguments get 0
202 } yield Rule.create( 190 if query.bounded contains r2arg0
203 RSA.ID( 191 r2arg2 = role2.getArguments get 2
204 RSA(bounded indexOf role1arg0), 192 if query.bounded contains r2arg2
205 RSA(bounded indexOf role2arg0) 193 } yield Rule.create(
206 ), 194 RSA.ID(
207 role1 << Forward, 195 RSA(query.bounded indexOf r1arg0),
208 role2 << Forward, 196 RSA(query.bounded indexOf r2arg0)
209 RSA.ID( 197 ),
210 RSA(bounded indexOf role1arg2), 198 role1 << Forward,
211 RSA(bounded indexOf role2arg2) 199 role2 << Forward,
212 ), 200 RSA.ID(
213 RSA.Congruent(role1arg0, role2arg0), 201 RSA(query.bounded indexOf r1arg2),
214 not(RSA.NI(role1arg0)) 202 RSA(query.bounded indexOf r2arg2)
215 ) 203 ),
216 val r5b = for { 204 RSA.Congruent(r1arg0, r2arg0),
217 role1 <- body.filter(_.isRoleAssertion) 205 not(RSA.NI(r1arg0))
218 role1arg0 = role1.getArguments.get(0) 206 )
219 role1arg2 = role1.getArguments.get(2) 207 val r5b = for {
220 if bounded contains role1arg0 208 role1 <- query.atoms filter (_.isRoleAssertion)
221 if bounded contains role1arg2 209 r1arg0 = role1.getArguments get 0
222 role2 <- body.filter(_.isRoleAssertion) 210 if query.bounded contains r1arg0
223 role2arg0 = role2.getArguments.get(0) 211 r1arg2 = role1.getArguments get 2
224 role2arg2 = role2.getArguments.get(2) 212 if query.bounded contains r1arg2
225 if bounded contains role2arg0 213 role2 <- query.atoms filter (_.isRoleAssertion)
226 if bounded contains role2arg2 214 r2arg0 = role2.getArguments get 0
227 } yield Rule.create( 215 if query.bounded contains r2arg0
228 RSA.ID( 216 r2arg2 = role2.getArguments get 2
229 RSA(bounded indexOf role1arg0), 217 if query.bounded contains r2arg2
230 RSA(bounded indexOf role2arg2) 218 } yield Rule.create(
231 ), 219 RSA.ID(
232 role1 << Forward, 220 RSA(query.bounded indexOf r1arg0),
233 role2 << Backward, 221 RSA(query.bounded indexOf r2arg2)
234 RSA.ID( 222 ),
235 RSA(bounded indexOf role1arg2), 223 role1 << Forward,
236 RSA(bounded indexOf role2arg0) 224 role2 << Backward,
237 ), 225 RSA.ID(
238 RSA.Congruent(role1arg0, role2arg2), 226 RSA(query.bounded indexOf r1arg2),
239 not(RSA.NI(role1arg0)) 227 RSA(query.bounded indexOf r2arg0)
240 ) 228 ),
241 val r5c = for { 229 RSA.Congruent(r1arg0, r2arg2),
242 role1 <- body.filter(_.isRoleAssertion) 230 not(RSA.NI(r1arg0))
243 role1arg0 = role1.getArguments.get(0) 231 )
244 role1arg2 = role1.getArguments.get(2) 232 val r5c = for {
245 if bounded contains role1arg0 233 role1 <- query.atoms filter (_.isRoleAssertion)
246 if bounded contains role1arg2 234 r1arg0 = role1.getArguments get 0
247 role2 <- body.filter(_.isRoleAssertion) 235 if query.bounded contains r1arg0
248 role2arg0 = role2.getArguments.get(0) 236 r1arg2 = role1.getArguments get 2
249 role2arg2 = role2.getArguments.get(2) 237 if query.bounded contains r1arg2
250 if bounded contains role2arg0 238 role2 <- query.atoms filter (_.isRoleAssertion)
251 if bounded contains role2arg2 239 r2arg0 = role2.getArguments get 0
252 } yield Rule.create( 240 if query.bounded contains r2arg0
253 RSA.ID( 241 r2arg2 = role2.getArguments get 2
254 RSA(bounded indexOf role1arg2), 242 if query.bounded contains r2arg2
255 RSA(bounded indexOf role2arg2) 243 } yield Rule.create(
256 ), 244 RSA.ID(
257 role1 << Backward, 245 RSA(query.bounded indexOf r1arg2),
258 role2 << Backward, 246 RSA(query.bounded indexOf r2arg2)
259 RSA.ID( 247 ),
260 RSA(bounded indexOf role1arg0), 248 role1 << Backward,
261 RSA(bounded indexOf role2arg0) 249 role2 << Backward,
262 ), 250 RSA.ID(
263 RSA.Congruent(role1arg2, role2arg2), 251 RSA(query.bounded indexOf r1arg0),
264 not(RSA.NI(role1arg2)) 252 RSA(query.bounded indexOf r2arg0)
265 ) 253 ),
254 RSA.Congruent(r1arg2, r2arg2),
255 not(RSA.NI(r1arg2))
256 )
266 257
267 /* Rules 6 */ 258 /** Detect cycles in the canonical model.
268 val r6 = { 259 *
269 for { 260 * Cycles are detected by introducing a new predicate `rsa:AQ`
270 role <- body.filter(_.isRoleAssertion) 261 * and computing its transitive closure. Cycles are computed from
271 arg0 = role.getArguments.get(0) 262 * forward and backward roles separately.
272 arg2 = role.getArguments.get(2) 263 *
273 if bounded contains arg0 264 * @note corresponds to rules 6,7x in Table 3.
274 if bounded contains arg2 265 */
266 val r6 = for {
267 role <- query.atoms filter (_.isRoleAssertion)
268 index0 = query.bounded indexOf (role.getArguments get 0)
269 if index0 >= 0
270 index2 = query.bounded indexOf (role.getArguments get 2)
271 if index2 >= 0
275 suffix <- Seq(Forward, Backward) 272 suffix <- Seq(Forward, Backward)
276 } yield Rule.create( 273 } yield Rule.create(
277 RSA.AQ(varV, varW, suffix), 274 RSA.AQ(varV, varW, suffix),
278 role << suffix, 275 role << suffix,
279 RSA.ID(RSA(bounded indexOf arg0), varV), 276 RSA.ID(RSA(index0), varV),
280 RSA.ID(RSA(bounded indexOf arg2), varW) 277 RSA.ID(RSA(index2), varW)
281 ) 278 )
282 } 279 val r7a =
280 for (suffix <- List(Forward, Backward))
281 yield Rule.create(
282 RSA.TQ(varU, varV, suffix),
283 RSA.AQ(varU, varV, suffix)
284 )
285 val r7b =
286 for (suffix <- List(Forward, Backward))
287 yield Rule.create(
288 RSA.TQ(varU, varW, suffix),
289 RSA.AQ(varU, varV, suffix),
290 RSA.TQ(varV, varW, suffix)
291 )
283 292
284 /* Rules 7x */ 293 /** Flag spurious answers.
285 val r7a = { 294 *
286 for (suffix <- List(Forward, Backward)) 295 * @note corresponds to rules 8x in Table 3.
287 yield Rule.create( 296 */
288 RSA.TQ(varU, varV, suffix), 297 val r8a =
289 RSA.AQ(varU, varV, suffix) 298 for (v <- query.answer)
290 ) 299 yield Rule.create(RSA.SP, RSA.QM, not(RSA.Named(v)))
291 } 300 val r8b = Rule.create(RSA.SP, RSA.FK)
292 val r7b = { 301 val r8c =
293 for (suffix <- List(Forward, Backward)) 302 for (suffix <- List(Forward, Backward))
294 yield Rule.create( 303 yield Rule.create(
295 RSA.TQ(varU, varW, suffix), 304 RSA.SP,
296 RSA.AQ(varU, varV, suffix), 305 RSA.TQ(varV, varV, suffix)
297 RSA.TQ(varV, varW, suffix) 306 )
298 )
299 }
300
301 /* Rules 8x */
302 val r8a =
303 for (v <- answer)
304 yield Rule.create(RSA.SP, RSA.QM, not(RSA.Named(v)))
305 val r8b =
306 Rule.create(RSA.SP, RSA.FK)
307 val r8c =
308 for (suffix <- List(Forward, Backward))
309 yield Rule.create(
310 RSA.SP,
311 RSA.TQ(varV, varV, suffix)
312 )
313 307
314 /* Rule 9 */ 308 /** Determine answers to the query
315 val r9 = Rule.create(RSA.Ans, RSA.QM, not(RSA.SP)) 309 *
310 * Answers are identified by predicate `rsa:Ans`. In case the
311 * input query is a BCQ (answer is just true/false), we derive
312 * `rsa:Ans` for a fresh constant `c`. Later on we can query for
313 * instances of `rsa:Ans` as follows
314 *
315 * {{{
316 * ASK { ?X a rsa:Ans }
317 * }}}
318 *
319 * to determine whether the query is true or false.
320 *
321 * @note corresponds to rule 9 in Table 3.
322 */
323 val r9 = Rule.create(RSA.Ans, RSA.QM, not(RSA.SP))
316 324
317 r1 :: 325 (r1 :: r2 :::
318 r3a ::: r3b :: r3c :: 326 r3a ::: r3b :: r3c ::
319 r4c ::: r4b ::: r4a ::: 327 r4a ::: r4b ::: r4c :::
320 r5c ::: r5b ::: r5a ::: 328 // r5c ::: r5b ::: r5a :::
321 r6 ::: 329 r6 ::: r7b ::: r7a :::
322 r7b ::: r7a ::: 330 r8a ::: r8b :: r8c :::
323 r8a ::: r8b :: r8c ::: 331 r9 :: List()) map reifyRule
324 r9 :: 332 }
325 List()
326 }
327 333
328 private def reifyAtom(atom: Atom): (Option[BindAtom], List[Atom]) = { 334 private def reifyAtom(atom: Atom): (Option[BindAtom], List[Atom]) = {
329 atom match { 335 atom match {
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala
index e77a913..59eb626 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala
@@ -3,6 +3,7 @@ package uk.ac.ox.cs.rsacomb.sparql
3import java.util.{Map => JMap, HashMap => JHashMap} 3import java.util.{Map => JMap, HashMap => JHashMap}
4import tech.oxfordsemantic.jrdfox.Prefixes 4import tech.oxfordsemantic.jrdfox.Prefixes
5import tech.oxfordsemantic.jrdfox.client.DataStoreConnection 5import tech.oxfordsemantic.jrdfox.client.DataStoreConnection
6import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom
6import tech.oxfordsemantic.jrdfox.logic.expression.Variable 7import tech.oxfordsemantic.jrdfox.logic.expression.Variable
7import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{ 8import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{
8 ConjunctionPattern, 9 ConjunctionPattern,
@@ -77,32 +78,48 @@ class ConjunctiveQuery(
77 */ 78 */
78 val bcq: Boolean = select.isEmpty && !query.getAllPossibleVariables 79 val bcq: Boolean = select.isEmpty && !query.getAllPossibleVariables
79 80
80 /** Returns the full set of variables involved in the query. */ 81 /** Returns the query body as a sequence of atoms (triples). */
81 val variables: Set[Variable] = 82 val atoms: List[TupleTableAtom] =
82 where match { 83 where match {
83 case b: ConjunctionPattern => { 84 case b: ConjunctionPattern => {
84 b.getConjuncts.toSet.flatMap { conj: QueryPattern => 85 b.getConjuncts.toList.flatMap { conj: QueryPattern =>
85 conj match { 86 conj match {
86 case c: TriplePattern => 87 case c: TriplePattern =>
87 Set(c.getSubject, c.getPredicate, c.getObject).collect { 88 Seq(
88 case v: Variable => v 89 TupleTableAtom.rdf(c.getSubject, c.getPredicate, c.getObject)
89 } 90 )
90 case _ => Set() 91 case _ => List()
91 } 92 }
92 } 93 }
93 } 94 }
94 case _ => Set() 95 case _ => List()
95 } 96 }
96 97
98 /** Returns the full collection of variables involved in the query. */
99 val variables: List[Variable] = (where match {
100 case b: ConjunctionPattern => {
101 b.getConjuncts.toList.flatMap { conj: QueryPattern =>
102 conj match {
103 case c: TriplePattern =>
104 Set(c.getSubject, c.getPredicate, c.getObject).collect {
105 case v: Variable => v
106 }
107 case _ => List()
108 }
109 }
110 }
111 case _ => List()
112 }).distinct
113
97 /** Returns the collection of answer variables in the query. */ 114 /** Returns the collection of answer variables in the query. */
98 val answer: Set[Variable] = 115 val answer: List[Variable] =
99 if (query.getAllPossibleVariables) 116 if (query.getAllPossibleVariables)
100 variables 117 variables
101 else 118 else
102 select.map(_.getVariable).toSet 119 select.map(_.getVariable).toList.distinct
103 120
104 /** Returns the collection of bounded (existential) variables in the query. */ 121 /** Returns the collection of bounded (existential) variables in the query. */
105 val bounded: Set[Variable] = variables &~ answer 122 val bounded: List[Variable] = variables diff answer
106 123
107 override def toString(): String = query.toString 124 override def toString(): String = query.toString
108} 125}
diff --git a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala
index 6c8d42d..4b04c40 100644
--- a/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala
+++ b/src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala
@@ -18,6 +18,7 @@ import org.semanticweb.owlapi.model.{
18 OWLObjectPropertyExpression 18 OWLObjectPropertyExpression
19} 19}
20 20
21import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery
21import uk.ac.ox.cs.rsacomb.suffix.RSASuffix 22import uk.ac.ox.cs.rsacomb.suffix.RSASuffix
22 23
23// Debug only 24// Debug only
@@ -28,7 +29,7 @@ object RSA {
28 val Prefixes: Prefixes = new Prefixes() 29 val Prefixes: Prefixes = new Prefixes()
29 Prefixes.declarePrefix("rsa:", "http://www.cs.ox.ac.uk/isg/rsa/") 30 Prefixes.declarePrefix("rsa:", "http://www.cs.ox.ac.uk/isg/rsa/")
30 31
31 private def atom(name: IRI, vars: List[Term]) = 32 private def atom(name: IRI, vars: List[Term]): TupleTableAtom =
32 TupleTableAtom.create(TupleTableName.create(name.getIRI), vars: _*) 33 TupleTableAtom.create(TupleTableName.create(name.getIRI), vars: _*)
33 34
34 def PE(t1: Term, t2: Term) = 35 def PE(t1: Term, t2: Term) =
@@ -45,14 +46,11 @@ object RSA {
45 def Congruent(t1: Term, t2: Term) = 46 def Congruent(t1: Term, t2: Term) =
46 TupleTableAtom.rdf(t1, RSA("congruent"), t2) 47 TupleTableAtom.rdf(t1, RSA("congruent"), t2)
47 48
48 def QM(implicit variables: (List[Term], List[Term])) = { 49 def QM(implicit q: ConjunctiveQuery) =
49 val (answer, bounded) = variables 50 atom(RSA("QM"), q.answer ::: q.bounded)
50 atom(RSA("QM"), answer ::: bounded)
51 }
52 51
53 def ID(t1: Term, t2: Term)(implicit variables: (List[Term], List[Term])) = { 52 def ID(t1: Term, t2: Term)(implicit q: ConjunctiveQuery) = {
54 val (answer, bounded) = variables 53 atom(RSA("ID"), (q.answer ::: q.bounded) :+ t1 :+ t2)
55 atom(RSA("ID"), (answer ::: bounded) :+ t1 :+ t2)
56 } 54 }
57 55
58 def Named(t: Term) = 56 def Named(t: Term) =
@@ -64,33 +62,23 @@ object RSA {
64 def NI(t: Term) = 62 def NI(t: Term) =
65 TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("NI")) 63 TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("NI"))
66 64
67 def TQ(t1: Term, t2: Term, sx: RSASuffix)(implicit 65 def TQ(t1: Term, t2: Term, sx: RSASuffix)(implicit q: ConjunctiveQuery) =
68 variables: (List[Term], List[Term]) 66 atom(RSA("TQ" :: sx), (q.answer ::: q.bounded) :+ t1 :+ t2)
69 ) = {
70 val (answer, bounded) = variables
71 atom(RSA("TQ" :: sx), (answer ::: bounded) :+ t1 :+ t2)
72 }
73 67
74 def AQ(t1: Term, t2: Term, sx: RSASuffix)(implicit 68 def AQ(t1: Term, t2: Term, sx: RSASuffix)(implicit q: ConjunctiveQuery) =
75 variables: (List[Term], List[Term]) 69 atom(RSA("AQ" :: sx), (q.answer ::: q.bounded) :+ t1 :+ t2)
76 ) = {
77 val (answer, bounded) = variables
78 atom(RSA("AQ" :: sx), (answer ::: bounded) :+ t1 :+ t2)
79 }
80 70
81 def FK(implicit variables: (List[Term], List[Term])) = { 71 def FK(implicit q: ConjunctiveQuery) =
82 val (answer, bounded) = variables 72 atom(RSA("FK"), q.answer ::: q.bounded)
83 atom(RSA("FK"), answer ::: bounded)
84 }
85 73
86 def SP(implicit variables: (List[Term], List[Term])) = { 74 def SP(implicit q: ConjunctiveQuery) =
87 val (answer, bounded) = variables 75 atom(RSA("SP"), q.answer ::: q.bounded)
88 atom(RSA("SP"), answer ::: bounded)
89 }
90 76
91 def Ans(implicit variables: (List[Term], List[Term])) = { 77 def Ans(implicit q: ConjunctiveQuery) = {
92 val (answer, _) = variables 78 if (q.bcq)
93 atom(RSA("Ans"), answer) 79 TupleTableAtom.rdf(RSA("blank"), IRI.RDF_TYPE, RSA("Ans"))
80 else
81 atom(RSA("Ans"), q.answer)
94 } 82 }
95 83
96 def apply(name: Any): IRI = 84 def apply(name: Any): IRI =