diff options
| author | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-11-23 22:06:33 +0000 |
|---|---|---|
| committer | Federico Igne <federico.igne@cs.ox.ac.uk> | 2020-11-23 22:06:33 +0000 |
| commit | aeb5ad23e5f13952efdeaf4aec2e97a96b469655 (patch) | |
| tree | b9f76f4ba035fb33eddaf0d206ba768d512c1f1c /src/main/scala | |
| parent | 43be594ec3cd6f16e1ccfc31ce226f5266a7a19b (diff) | |
| download | RSAComb-aeb5ad23e5f13952efdeaf4aec2e97a96b469655.tar.gz RSAComb-aeb5ad23e5f13952efdeaf4aec2e97a96b469655.zip | |
Include BCQ case in filtering program generation
The FilteringProgram module has been slightly reworked.
Diffstat (limited to 'src/main/scala')
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} | |||
| 32 | import uk.ac.ox.cs.rsacomb.util.RSA | 32 | import uk.ac.ox.cs.rsacomb.util.RSA |
| 33 | import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery | 33 | import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery |
| 34 | 34 | ||
| 35 | /** Factory for [[uk.ac.ox.cs.rsacomb.FilteringProgram FilteringProgram]] */ | ||
| 35 | object FilteringProgram { | 36 | object 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 | */ | ||
| 42 | class FilteringProgram(query: ConjunctiveQuery, constants: List[Term]) | 56 | class 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 | |||
| 3 | import java.util.{Map => JMap, HashMap => JHashMap} | 3 | import java.util.{Map => JMap, HashMap => JHashMap} |
| 4 | import tech.oxfordsemantic.jrdfox.Prefixes | 4 | import tech.oxfordsemantic.jrdfox.Prefixes |
| 5 | import tech.oxfordsemantic.jrdfox.client.DataStoreConnection | 5 | import tech.oxfordsemantic.jrdfox.client.DataStoreConnection |
| 6 | import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom | ||
| 6 | import tech.oxfordsemantic.jrdfox.logic.expression.Variable | 7 | import tech.oxfordsemantic.jrdfox.logic.expression.Variable |
| 7 | import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{ | 8 | import 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 | ||
| 21 | import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery | ||
| 21 | import uk.ac.ox.cs.rsacomb.suffix.RSASuffix | 22 | import 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 = |
