From aeb5ad23e5f13952efdeaf4aec2e97a96b469655 Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Mon, 23 Nov 2020 22:06:33 +0000 Subject: Include BCQ case in filtering program generation The FilteringProgram module has been slightly reworked. --- .../uk/ac/ox/cs/rsacomb/FilteringProgram.scala | 532 +++++++++++---------- .../ac/ox/cs/rsacomb/sparql/ConjunctiveQuery.scala | 39 +- src/main/scala/uk/ac/ox/cs/rsacomb/util/RSA.scala | 50 +- 3 files changed, 316 insertions(+), 305 deletions(-) (limited to 'src') 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} import uk.ac.ox.cs.rsacomb.util.RSA import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery +/** Factory for [[uk.ac.ox.cs.rsacomb.FilteringProgram FilteringProgram]] */ object FilteringProgram { + /** Create a new FilteringProgram instance. + * + * @param query CQ to be converted into logic rules. + * @param constants constants in the original ontology. They will be + * used to initialize predicate `rsa:Named`. + */ def apply(query: ConjunctiveQuery, constants: List[Term]): FilteringProgram = new FilteringProgram(query, constants) -} // object FilteringProgram +} +/** Filtering Program generator + * + * Handles the conversion of a CQ into a set of logic rules, + * representing the filtering step of the RSA combined approach. + * + * Instances can be created using the companion object. + */ class FilteringProgram(query: ConjunctiveQuery, constants: List[Term]) extends RSAAtom { - /* Makes mplicit conversion OWLAPI IRI <-> RDFox IRI available */ - // import implicits.RDFox._ - - val answer: List[Term] = query.answer.toList - val bounded: List[Term] = query.bounded.toList - implicit val variables = (answer, bounded) + /** Implicit parameter used in RSA internal predicates. + * + * @see [[uk.ac.ox.cs.rsacomb.util.RSA]] for more information. + */ + implicit private[this] val _query = query - val named: List[Rule] = - constants.map(a => Rule.create(RSA.Named(a))) + /** General purpose variables used in rule instantiation. + * + * This is done to avoid creating new variables every time. + */ + private val varX = Variable.create("X") + private val varY = Variable.create("Y") + private val varZ = Variable.create("Z") + private val varV = Variable.create("V") + private val varU = Variable.create("U") + private val varW = Variable.create("W") - val nis: Rule = { - val varX = Variable.create("X") - val varY = Variable.create("Y") + /** Rule generating the instances of the predicate `rsa:NI`. + * + * According to the original paper, the set of `rsa:NI` is defined as + * the set of constants that are equal (w.r.t. the congruence + * relation represented by `rsa:Congruent`) to a constant in the + * original ontology. + * + * @note that the set of `rsa:Named` constants is always a subset of + * the set of `rsa:NI`s. + * + * @note in the paper, instances of `rsa:NI` are introduced as facts + * during the canonical model computation. By definition of the + * predicate, this is not feasible, and the instances are instead + * generate in the filtering program using a logic rule. + */ + val nis: Rule = Rule.create(RSA.NI(varX), RSA.Congruent(varX, varY), RSA.Named(varY)) - } + /** Collection of filtering program rules. */ val rules: List[Rule] = - nis :: named ::: this.generateFilteringProgram().map(reifyRule) + nis :: { - /* NOTE: we are restricting to queries that contain conjunctions of - * atoms for the time being. This might need to be reviewed in the - * future. - */ - private def queryToBody(body: GroupGraphPattern): List[TupleTableAtom] = - body match { - case b: ConjunctionPattern => { - val conjuncts = b.getConjuncts.asScala.toList - conjuncts flatMap { conj => - conj match { - case c: TriplePattern => - List( - TupleTableAtom.rdf(c.getSubject, c.getPredicate, c.getObject) - ) - case _ => List() - } - } - } - case _ => List() - } + /** Negates a [[tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom TupleTableAtom]] */ + def not(atom: TupleTableAtom): BodyFormula = Negation.create(atom) - private def generateFilteringProgram(): List[Rule] = { - // General purpose variables - val varU = Variable.create("U") - val varV = Variable.create("V") - val varW = Variable.create("W") - // Query formula as a rule body - val body = queryToBody(query.where) - // Auxiliar predicates/helpers - def not(atom: TupleTableAtom): BodyFormula = Negation.create(atom) - // val predQM = - // TupleTableAtom.create( - // TupleTableName.create(RSA.Qm.getIRI), - // (answer ++ bounded): _* - // ) - // def predID(t1: Term, t2: Term) = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("ID").getIRI), - // (answer ++ bounded).appended(t1).appended(t2): _* - // ) - // def predNAMED(t1: Term): TupleTableAtom = - // TupleTableAtom.rdf(t1, IRI.RDF_TYPE, RSA.rsa("NAMED")) - // def predTQ(t1: Term, t2: Term, sx: RSASuffix) = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("TQ" :: sx).getIRI), - // (answer ++ bounded).appended(t1).appended(t2): _* - // ) - // def predAQ(t1: Term, t2: Term, sx: RSASuffix) = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("AQ" :: sx).getIRI), - // (answer ++ bounded).appended(t1).appended(t2): _* - // ) - // val predFK = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("FK").getIRI), - // (answer ++ bounded): _* - // ) - // val predSP = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("SP").getIRI), - // (answer ++ bounded): _* - // ) - // val predANS = - // TupleTableAtom.create( - // TupleTableName.create(RSA.rsa("ANS").getIRI), - // answer: _* - // ) + /** Generates all possible, unfiltered answers. + * + * @note corresponds to rule 1 in Table 3 in the paper. + */ + val r1 = reifyRule(Rule.create(RSA.QM, query.atoms: _*)) - /* Rule 1 */ - val r1 = Rule.create(RSA.QM, body: _*) + /** Initializes instances of `rsa:Named`. + * + * They represent the set of constants appearing in the original + * ontology. + * + * @note corresponds to rules 2 in Table 3. + */ + val r2 = constants.map(c => Rule.create(RSA.Named(c))) - /* Rules 3x */ - val r3a = - for ((v, i) <- bounded.zipWithIndex) - yield Rule.create(RSA.ID(RSA(i), RSA(i)), RSA.QM, not(RSA.NI(v))) - val r3b = Rule.create(RSA.ID(varV, varU), RSA.ID(varU, varV)) - val r3c = - Rule.create(RSA.ID(varU, varW), RSA.ID(varU, varV), RSA.ID(varV, varW)) + /** Initializes instances of `rsa:ID`. + * + * They are initialized as a minimal congruence relation over the + * positions of the existential variables in the query which are + * mapped to anonymous terms. + * + * @note corresponds to rules 3x in Table 3. + */ + val r3a = + for ((v, i) <- query.bounded.zipWithIndex) + yield Rule.create(RSA.ID(RSA(i), RSA(i)), RSA.QM, not(RSA.NI(v))) + val r3b = Rule.create(RSA.ID(varV, varU), RSA.ID(varU, varV)) + val r3c = + Rule.create(RSA.ID(varU, varW), RSA.ID(varU, varV), RSA.ID(varV, varW)) - /* Rules 4x */ - val r4a = for { - role1 <- body.filter(_.isRoleAssertion) - if bounded contains (role1.getArguments.get(2)) - role2 <- body.filter(_.isRoleAssertion) - if bounded contains (role2.getArguments.get(2)) - } yield Rule.create( - RSA.FK, - role1 << Forward, - role2 << Forward, - RSA.ID( - RSA(bounded.indexOf(role1.getArguments.get(2))), - RSA(bounded.indexOf(role2.getArguments.get(2))) - ), - not(RSA.Congruent(role1.getArguments.get(0), role2.getArguments.get(0))) - ) - val r4b = for { - role1 <- body.filter(_.isRoleAssertion) - if bounded contains (role1.getArguments.get(2)) - role2 <- body.filter(_.isRoleAssertion) - if bounded contains (role2.getArguments.get(0)) - } yield Rule.create( - RSA.FK, - role1 << Forward, - role2 << Backward, - RSA.ID( - RSA(bounded.indexOf(role1.getArguments.get(2))), - RSA(bounded.indexOf(role2.getArguments.get(0))) - ), - not(RSA.Congruent(role1.getArguments.get(0), role2.getArguments.get(2))) - ) - val r4c = for { - role1 <- body.filter(_.isRoleAssertion) - if bounded contains (role1.getArguments.get(0)) - role2 <- body.filter(_.isRoleAssertion) - if bounded contains (role2.getArguments.get(0)) - } yield Rule.create( - RSA.FK, - role1 << Backward, - role2 << Backward, - RSA.ID( - RSA(bounded.indexOf(role1.getArguments.get(0))), - RSA(bounded.indexOf(role2.getArguments.get(0))) - ), - not(RSA.Congruent(role1.getArguments.get(2), role2.getArguments.get(2))) - ) + /** Detects forks in the canonical model. + * + * @note corresponds to rules 4x in Table 3. + */ + val r4a = for { + role1 <- query.atoms filter (_.isRoleAssertion) + index1 = query.bounded indexOf (role1.getArguments get 2) + if index1 >= 0 + role2 <- query.atoms filter (_.isRoleAssertion) + index2 = query.bounded indexOf (role2.getArguments get 2) + if index2 >= 0 + } yield Rule.create( + RSA.FK, + role1 << Forward, + role2 << Forward, + RSA.ID(RSA(index1), RSA(index2)), + not(RSA.Congruent(role1.getArguments get 0, role2.getArguments get 0)) + ) + val r4b = for { + role1 <- query.atoms filter (_.isRoleAssertion) + index1 = query.bounded indexOf (role1.getArguments get 2) + if index1 >= 0 + role2 <- query.atoms filter (_.isRoleAssertion) + index2 = query.bounded indexOf (role2.getArguments get 0) + if index2 >= 0 + } yield Rule.create( + RSA.FK, + role1 << Forward, + role2 << Backward, + RSA.ID(RSA(index1), RSA(index2)), + not(RSA.Congruent(role1.getArguments get 0, role2.getArguments get 2)) + ) + val r4c = for { + role1 <- query.atoms filter (_.isRoleAssertion) + index1 = query.bounded indexOf (role1.getArguments get 0) + if index1 >= 0 + role2 <- query.atoms filter (_.isRoleAssertion) + index2 = query.bounded indexOf (role2.getArguments get 0) + if index2 >= 0 + } yield Rule.create( + RSA.FK, + role1 << Backward, + role2 << Backward, + RSA.ID(RSA(index1), RSA(index2)), + not(RSA.Congruent(role1.getArguments get 2, role2.getArguments get 2)) + ) - /* Rules 5x */ - val r5a = for { - role1 <- body.filter(_.isRoleAssertion) - role1arg0 = role1.getArguments.get(0) - role1arg2 = role1.getArguments.get(2) - if bounded contains role1arg0 - if bounded contains role1arg2 - role2 <- body.filter(_.isRoleAssertion) - role2arg0 = role2.getArguments.get(0) - role2arg2 = role2.getArguments.get(2) - if bounded contains role2arg0 - if bounded contains role2arg2 - } yield Rule.create( - RSA.ID( - RSA(bounded indexOf role1arg0), - RSA(bounded indexOf role2arg0) - ), - role1 << Forward, - role2 << Forward, - RSA.ID( - RSA(bounded indexOf role1arg2), - RSA(bounded indexOf role2arg2) - ), - RSA.Congruent(role1arg0, role2arg0), - not(RSA.NI(role1arg0)) - ) - val r5b = for { - role1 <- body.filter(_.isRoleAssertion) - role1arg0 = role1.getArguments.get(0) - role1arg2 = role1.getArguments.get(2) - if bounded contains role1arg0 - if bounded contains role1arg2 - role2 <- body.filter(_.isRoleAssertion) - role2arg0 = role2.getArguments.get(0) - role2arg2 = role2.getArguments.get(2) - if bounded contains role2arg0 - if bounded contains role2arg2 - } yield Rule.create( - RSA.ID( - RSA(bounded indexOf role1arg0), - RSA(bounded indexOf role2arg2) - ), - role1 << Forward, - role2 << Backward, - RSA.ID( - RSA(bounded indexOf role1arg2), - RSA(bounded indexOf role2arg0) - ), - RSA.Congruent(role1arg0, role2arg2), - not(RSA.NI(role1arg0)) - ) - val r5c = for { - role1 <- body.filter(_.isRoleAssertion) - role1arg0 = role1.getArguments.get(0) - role1arg2 = role1.getArguments.get(2) - if bounded contains role1arg0 - if bounded contains role1arg2 - role2 <- body.filter(_.isRoleAssertion) - role2arg0 = role2.getArguments.get(0) - role2arg2 = role2.getArguments.get(2) - if bounded contains role2arg0 - if bounded contains role2arg2 - } yield Rule.create( - RSA.ID( - RSA(bounded indexOf role1arg2), - RSA(bounded indexOf role2arg2) - ), - role1 << Backward, - role2 << Backward, - RSA.ID( - RSA(bounded indexOf role1arg0), - RSA(bounded indexOf role2arg0) - ), - RSA.Congruent(role1arg2, role2arg2), - not(RSA.NI(role1arg2)) - ) + /** Recursively propagates `rsa:ID` predicate. + * + * @note corresponds to rules 5x in Table 3. + */ + val r5a = for { + role1 <- query.atoms filter (_.isRoleAssertion) + r1arg0 = role1.getArguments get 0 + if query.bounded contains r1arg0 + r1arg2 = role1.getArguments get 2 + if query.bounded contains r1arg2 + role2 <- query.atoms filter (_.isRoleAssertion) + r2arg0 = role2.getArguments get 0 + if query.bounded contains r2arg0 + r2arg2 = role2.getArguments get 2 + if query.bounded contains r2arg2 + } yield Rule.create( + RSA.ID( + RSA(query.bounded indexOf r1arg0), + RSA(query.bounded indexOf r2arg0) + ), + role1 << Forward, + role2 << Forward, + RSA.ID( + RSA(query.bounded indexOf r1arg2), + RSA(query.bounded indexOf r2arg2) + ), + RSA.Congruent(r1arg0, r2arg0), + not(RSA.NI(r1arg0)) + ) + val r5b = for { + role1 <- query.atoms filter (_.isRoleAssertion) + r1arg0 = role1.getArguments get 0 + if query.bounded contains r1arg0 + r1arg2 = role1.getArguments get 2 + if query.bounded contains r1arg2 + role2 <- query.atoms filter (_.isRoleAssertion) + r2arg0 = role2.getArguments get 0 + if query.bounded contains r2arg0 + r2arg2 = role2.getArguments get 2 + if query.bounded contains r2arg2 + } yield Rule.create( + RSA.ID( + RSA(query.bounded indexOf r1arg0), + RSA(query.bounded indexOf r2arg2) + ), + role1 << Forward, + role2 << Backward, + RSA.ID( + RSA(query.bounded indexOf r1arg2), + RSA(query.bounded indexOf r2arg0) + ), + RSA.Congruent(r1arg0, r2arg2), + not(RSA.NI(r1arg0)) + ) + val r5c = for { + role1 <- query.atoms filter (_.isRoleAssertion) + r1arg0 = role1.getArguments get 0 + if query.bounded contains r1arg0 + r1arg2 = role1.getArguments get 2 + if query.bounded contains r1arg2 + role2 <- query.atoms filter (_.isRoleAssertion) + r2arg0 = role2.getArguments get 0 + if query.bounded contains r2arg0 + r2arg2 = role2.getArguments get 2 + if query.bounded contains r2arg2 + } yield Rule.create( + RSA.ID( + RSA(query.bounded indexOf r1arg2), + RSA(query.bounded indexOf r2arg2) + ), + role1 << Backward, + role2 << Backward, + RSA.ID( + RSA(query.bounded indexOf r1arg0), + RSA(query.bounded indexOf r2arg0) + ), + RSA.Congruent(r1arg2, r2arg2), + not(RSA.NI(r1arg2)) + ) - /* Rules 6 */ - val r6 = { - for { - role <- body.filter(_.isRoleAssertion) - arg0 = role.getArguments.get(0) - arg2 = role.getArguments.get(2) - if bounded contains arg0 - if bounded contains arg2 + /** Detect cycles in the canonical model. + * + * Cycles are detected by introducing a new predicate `rsa:AQ` + * and computing its transitive closure. Cycles are computed from + * forward and backward roles separately. + * + * @note corresponds to rules 6,7x in Table 3. + */ + val r6 = for { + role <- query.atoms filter (_.isRoleAssertion) + index0 = query.bounded indexOf (role.getArguments get 0) + if index0 >= 0 + index2 = query.bounded indexOf (role.getArguments get 2) + if index2 >= 0 suffix <- Seq(Forward, Backward) } yield Rule.create( RSA.AQ(varV, varW, suffix), role << suffix, - RSA.ID(RSA(bounded indexOf arg0), varV), - RSA.ID(RSA(bounded indexOf arg2), varW) + RSA.ID(RSA(index0), varV), + RSA.ID(RSA(index2), varW) ) - } + val r7a = + for (suffix <- List(Forward, Backward)) + yield Rule.create( + RSA.TQ(varU, varV, suffix), + RSA.AQ(varU, varV, suffix) + ) + val r7b = + for (suffix <- List(Forward, Backward)) + yield Rule.create( + RSA.TQ(varU, varW, suffix), + RSA.AQ(varU, varV, suffix), + RSA.TQ(varV, varW, suffix) + ) - /* Rules 7x */ - val r7a = { - for (suffix <- List(Forward, Backward)) - yield Rule.create( - RSA.TQ(varU, varV, suffix), - RSA.AQ(varU, varV, suffix) - ) - } - val r7b = { - for (suffix <- List(Forward, Backward)) - yield Rule.create( - RSA.TQ(varU, varW, suffix), - RSA.AQ(varU, varV, suffix), - RSA.TQ(varV, varW, suffix) - ) - } - - /* Rules 8x */ - val r8a = - for (v <- answer) - yield Rule.create(RSA.SP, RSA.QM, not(RSA.Named(v))) - val r8b = - Rule.create(RSA.SP, RSA.FK) - val r8c = - for (suffix <- List(Forward, Backward)) - yield Rule.create( - RSA.SP, - RSA.TQ(varV, varV, suffix) - ) + /** Flag spurious answers. + * + * @note corresponds to rules 8x in Table 3. + */ + val r8a = + for (v <- query.answer) + yield Rule.create(RSA.SP, RSA.QM, not(RSA.Named(v))) + val r8b = Rule.create(RSA.SP, RSA.FK) + val r8c = + for (suffix <- List(Forward, Backward)) + yield Rule.create( + RSA.SP, + RSA.TQ(varV, varV, suffix) + ) - /* Rule 9 */ - val r9 = Rule.create(RSA.Ans, RSA.QM, not(RSA.SP)) + /** Determine answers to the query + * + * Answers are identified by predicate `rsa:Ans`. In case the + * input query is a BCQ (answer is just true/false), we derive + * `rsa:Ans` for a fresh constant `c`. Later on we can query for + * instances of `rsa:Ans` as follows + * + * {{{ + * ASK { ?X a rsa:Ans } + * }}} + * + * to determine whether the query is true or false. + * + * @note corresponds to rule 9 in Table 3. + */ + val r9 = Rule.create(RSA.Ans, RSA.QM, not(RSA.SP)) - r1 :: - r3a ::: r3b :: r3c :: - r4c ::: r4b ::: r4a ::: - r5c ::: r5b ::: r5a ::: - r6 ::: - r7b ::: r7a ::: - r8a ::: r8b :: r8c ::: - r9 :: - List() - } + (r1 :: r2 ::: + r3a ::: r3b :: r3c :: + r4a ::: r4b ::: r4c ::: + // r5c ::: r5b ::: r5a ::: + r6 ::: r7b ::: r7a ::: + r8a ::: r8b :: r8c ::: + r9 :: List()) map reifyRule + } private def reifyAtom(atom: Atom): (Option[BindAtom], List[Atom]) = { 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 import java.util.{Map => JMap, HashMap => JHashMap} import tech.oxfordsemantic.jrdfox.Prefixes import tech.oxfordsemantic.jrdfox.client.DataStoreConnection +import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom import tech.oxfordsemantic.jrdfox.logic.expression.Variable import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{ ConjunctionPattern, @@ -77,32 +78,48 @@ class ConjunctiveQuery( */ val bcq: Boolean = select.isEmpty && !query.getAllPossibleVariables - /** Returns the full set of variables involved in the query. */ - val variables: Set[Variable] = + /** Returns the query body as a sequence of atoms (triples). */ + val atoms: List[TupleTableAtom] = where match { case b: ConjunctionPattern => { - b.getConjuncts.toSet.flatMap { conj: QueryPattern => + b.getConjuncts.toList.flatMap { conj: QueryPattern => conj match { case c: TriplePattern => - Set(c.getSubject, c.getPredicate, c.getObject).collect { - case v: Variable => v - } - case _ => Set() + Seq( + TupleTableAtom.rdf(c.getSubject, c.getPredicate, c.getObject) + ) + case _ => List() } } } - case _ => Set() + case _ => List() } + /** Returns the full collection of variables involved in the query. */ + val variables: List[Variable] = (where match { + case b: ConjunctionPattern => { + b.getConjuncts.toList.flatMap { conj: QueryPattern => + conj match { + case c: TriplePattern => + Set(c.getSubject, c.getPredicate, c.getObject).collect { + case v: Variable => v + } + case _ => List() + } + } + } + case _ => List() + }).distinct + /** Returns the collection of answer variables in the query. */ - val answer: Set[Variable] = + val answer: List[Variable] = if (query.getAllPossibleVariables) variables else - select.map(_.getVariable).toSet + select.map(_.getVariable).toList.distinct /** Returns the collection of bounded (existential) variables in the query. */ - val bounded: Set[Variable] = variables &~ answer + val bounded: List[Variable] = variables diff answer override def toString(): String = query.toString } 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.{ OWLObjectPropertyExpression } +import uk.ac.ox.cs.rsacomb.sparql.ConjunctiveQuery import uk.ac.ox.cs.rsacomb.suffix.RSASuffix // Debug only @@ -28,7 +29,7 @@ object RSA { val Prefixes: Prefixes = new Prefixes() Prefixes.declarePrefix("rsa:", "http://www.cs.ox.ac.uk/isg/rsa/") - private def atom(name: IRI, vars: List[Term]) = + private def atom(name: IRI, vars: List[Term]): TupleTableAtom = TupleTableAtom.create(TupleTableName.create(name.getIRI), vars: _*) def PE(t1: Term, t2: Term) = @@ -45,14 +46,11 @@ object RSA { def Congruent(t1: Term, t2: Term) = TupleTableAtom.rdf(t1, RSA("congruent"), t2) - def QM(implicit variables: (List[Term], List[Term])) = { - val (answer, bounded) = variables - atom(RSA("QM"), answer ::: bounded) - } + def QM(implicit q: ConjunctiveQuery) = + atom(RSA("QM"), q.answer ::: q.bounded) - def ID(t1: Term, t2: Term)(implicit variables: (List[Term], List[Term])) = { - val (answer, bounded) = variables - atom(RSA("ID"), (answer ::: bounded) :+ t1 :+ t2) + def ID(t1: Term, t2: Term)(implicit q: ConjunctiveQuery) = { + atom(RSA("ID"), (q.answer ::: q.bounded) :+ t1 :+ t2) } def Named(t: Term) = @@ -64,33 +62,23 @@ object RSA { def NI(t: Term) = TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("NI")) - def TQ(t1: Term, t2: Term, sx: RSASuffix)(implicit - variables: (List[Term], List[Term]) - ) = { - val (answer, bounded) = variables - atom(RSA("TQ" :: sx), (answer ::: bounded) :+ t1 :+ t2) - } + def TQ(t1: Term, t2: Term, sx: RSASuffix)(implicit q: ConjunctiveQuery) = + atom(RSA("TQ" :: sx), (q.answer ::: q.bounded) :+ t1 :+ t2) - def AQ(t1: Term, t2: Term, sx: RSASuffix)(implicit - variables: (List[Term], List[Term]) - ) = { - val (answer, bounded) = variables - atom(RSA("AQ" :: sx), (answer ::: bounded) :+ t1 :+ t2) - } + def AQ(t1: Term, t2: Term, sx: RSASuffix)(implicit q: ConjunctiveQuery) = + atom(RSA("AQ" :: sx), (q.answer ::: q.bounded) :+ t1 :+ t2) - def FK(implicit variables: (List[Term], List[Term])) = { - val (answer, bounded) = variables - atom(RSA("FK"), answer ::: bounded) - } + def FK(implicit q: ConjunctiveQuery) = + atom(RSA("FK"), q.answer ::: q.bounded) - def SP(implicit variables: (List[Term], List[Term])) = { - val (answer, bounded) = variables - atom(RSA("SP"), answer ::: bounded) - } + def SP(implicit q: ConjunctiveQuery) = + atom(RSA("SP"), q.answer ::: q.bounded) - def Ans(implicit variables: (List[Term], List[Term])) = { - val (answer, _) = variables - atom(RSA("Ans"), answer) + def Ans(implicit q: ConjunctiveQuery) = { + if (q.bcq) + TupleTableAtom.rdf(RSA("blank"), IRI.RDF_TYPE, RSA("Ans")) + else + atom(RSA("Ans"), q.answer) } def apply(name: Any): IRI = -- cgit v1.2.3