aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/rsacomb/FilteringProgram.scala295
-rw-r--r--src/main/scala/rsacomb/Main.scala18
-rw-r--r--src/main/scala/rsacomb/RDFTriple.scala60
-rw-r--r--src/main/scala/rsacomb/RDFoxAxiomConverter.scala54
-rw-r--r--src/main/scala/rsacomb/RDFoxClassExprConverter.scala12
-rw-r--r--src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala2
-rw-r--r--src/main/scala/rsacomb/RDFoxUtil.scala6
-rw-r--r--src/main/scala/rsacomb/RSA.scala37
-rw-r--r--src/main/scala/rsacomb/RSAAxiom.scala92
-rw-r--r--src/main/scala/rsacomb/RSAOntology.scala105
10 files changed, 598 insertions, 83 deletions
diff --git a/src/main/scala/rsacomb/FilteringProgram.scala b/src/main/scala/rsacomb/FilteringProgram.scala
new file mode 100644
index 0000000..91c98d8
--- /dev/null
+++ b/src/main/scala/rsacomb/FilteringProgram.scala
@@ -0,0 +1,295 @@
1package rsacomb
2
3import tech.oxfordsemantic.jrdfox.logic._
4import scala.collection.JavaConverters._
5
6class FilteringProgram(query: Query, constants: List[Term]) extends RDFTriple {
7
8 private val bounded: List[Term] = this.getBoundedVariables
9 private val answer: List[Term] = query.getAnswerVariables.asScala.toList
10
11 val facts: List[Atom] = constants.map(named)
12 val rules: List[Rule] = this.generateFilteringProgram()
13
14 private def named(t: Term): Atom =
15 Atom.rdf(t, IRI.RDF_TYPE, RSA.internal("NAMED"))
16
17 private def getBoundedVariables: List[Variable] = List()
18
19 private def generateFilteringProgram(): List[Rule] = {
20 // Query formula as a rule body
21 val body = queryToBody(query.getQueryFormula)
22 // Auxiliar predicates/helpers
23 def not(atom: Atom): BodyFormula = Negation.create(atom)
24 val predQM =
25 Atom.create(
26 TupleTableName.create(RSA.internal("QM").getIRI),
27 (answer ++ bounded): _*
28 )
29 def predID(t1: Term, t2: Term) =
30 Atom.create(
31 TupleTableName.create(RSA.internal("ID").getIRI),
32 (answer ++ bounded).appended(t1).appended(t2): _*
33 )
34 def predNI(t1: Term): Atom =
35 Atom.rdf(t1, IRI.RDF_TYPE, RSA.internal("NI"))
36 def predTQ(sx: String, t1: Term, t2: Term) =
37 Atom.create(
38 TupleTableName.create(RSA.internal(s"TQ_$sx").getIRI),
39 (answer ++ bounded).appended(t1).appended(t2): _*
40 )
41 def predAQ(sx: String, t1: Term, t2: Term) =
42 Atom.create(
43 TupleTableName.create(RSA.internal(s"AQ_$sx").getIRI),
44 (answer ++ bounded).appended(t1).appended(t2): _*
45 )
46 val predFK =
47 Atom.create(
48 TupleTableName.create(RSA.internal("FK").getIRI),
49 (answer ++ bounded): _*
50 )
51 val predSP =
52 Atom.create(
53 TupleTableName.create(RSA.internal("SP").getIRI),
54 (answer ++ bounded): _*
55 )
56 val predANS =
57 Atom.create(
58 TupleTableName.create(RSA.internal("ANS").getIRI),
59 answer: _*
60 )
61
62 /* Rule 1 */
63 val r1 = Rule.create(predQM, body: _*)
64
65 /* Rules 3x */
66 val r3a =
67 for ((v, i) <- bounded.zipWithIndex)
68 yield Rule.create(
69 predID(RSA.internal(i), RSA.internal(i)),
70 predQM,
71 not(predNI(v))
72 )
73 val r3b = Rule.create(
74 predID(Variable.create("V"), Variable.create("U")),
75 predID(Variable.create("U"), Variable.create("V"))
76 )
77 val r3c = Rule.create(
78 predID(Variable.create("U"), Variable.create("W")),
79 predID(Variable.create("U"), Variable.create("V")),
80 predID(Variable.create("V"), Variable.create("W"))
81 )
82
83 /* Rules 4x */
84 val r4a = for {
85 role1 <- body.filter(_.isRoleAssertion)
86 if bounded contains (role1 getArgument 2)
87 role2 <- body.filter(_.isRoleAssertion)
88 if bounded contains (role2 getArgument 2)
89 } yield Rule.create(
90 predFK,
91 role1 suffix "_f",
92 role2 suffix "_f",
93 predID(
94 RSA.internal(bounded.indexOf(role1 getArgument 2)),
95 RSA.internal(bounded.indexOf(role2 getArgument 2))
96 ),
97 not(Atom.rdf(role1 getArgument 0, IRI.SAME_AS, role2 getArgument 0))
98 )
99 val r4b = for {
100 role1 <- body.filter(_.isRoleAssertion)
101 if bounded contains (role1 getArgument 2)
102 role2 <- body.filter(_.isRoleAssertion)
103 if bounded contains (role2 getArgument 0)
104 } yield Rule.create(
105 predFK,
106 role1 suffix "_f",
107 role2 suffix "_b",
108 predID(
109 RSA.internal(bounded.indexOf(role1 getArgument 2)),
110 RSA.internal(bounded.indexOf(role2 getArgument 0))
111 ),
112 not(Atom.rdf(role1 getArgument 0, IRI.SAME_AS, role2 getArgument 2))
113 )
114 val r4c = for {
115 role1 <- body.filter(_.isRoleAssertion)
116 if bounded contains (role1 getArgument 0)
117 role2 <- body.filter(_.isRoleAssertion)
118 if bounded contains (role2 getArgument 0)
119 } yield Rule.create(
120 predFK,
121 role1 suffix "_b",
122 role2 suffix "_b",
123 predID(
124 RSA.internal(bounded.indexOf(role1 getArgument 0)),
125 RSA.internal(bounded.indexOf(role2 getArgument 0))
126 ),
127 not(Atom.rdf(role1 getArgument 2, IRI.SAME_AS, role2 getArgument 2))
128 )
129
130 /* Rules 5x */
131 val r5a = for {
132 role1 <- body.filter(_.isRoleAssertion)
133 role1arg0 = role1 getArgument 0
134 role1arg2 = role1 getArgument 2
135 if bounded contains role1arg0
136 if bounded contains role1arg2
137 role2 <- body.filter(_.isRoleAssertion)
138 role2arg0 = role2 getArgument 0
139 role2arg2 = role2 getArgument 2
140 if bounded contains role2arg0
141 if bounded contains role2arg2
142 } yield Rule.create(
143 predID(
144 RSA.internal(bounded indexOf role1arg0),
145 RSA.internal(bounded indexOf role2arg0)
146 ),
147 role1 suffix "_f",
148 role2 suffix "_f",
149 predID(
150 RSA.internal(bounded indexOf role1arg2),
151 RSA.internal(bounded indexOf role2arg2)
152 ),
153 Atom.rdf(role1arg0, IRI.SAME_AS, role2arg0),
154 not(predNI(role1arg0))
155 )
156 val r5b = for {
157 role1 <- body.filter(_.isRoleAssertion)
158 role1arg0 = role1 getArgument 0
159 role1arg2 = role1 getArgument 2
160 if bounded contains role1arg0
161 if bounded contains role1arg2
162 role2 <- body.filter(_.isRoleAssertion)
163 role2arg0 = role2 getArgument 0
164 role2arg2 = role2 getArgument 2
165 if bounded contains role2arg0
166 if bounded contains role2arg2
167 } yield Rule.create(
168 predID(
169 RSA.internal(bounded indexOf role1arg0),
170 RSA.internal(bounded indexOf role2arg2)
171 ),
172 role1 suffix "_f",
173 role2 suffix "_b",
174 predID(
175 RSA.internal(bounded indexOf role1arg2),
176 RSA.internal(bounded indexOf role2arg0)
177 ),
178 Atom.rdf(role1arg0, IRI.SAME_AS, role2arg2),
179 not(predNI(role1arg0))
180 )
181 val r5c = for {
182 role1 <- body.filter(_.isRoleAssertion)
183 role1arg0 = role1 getArgument 0
184 role1arg2 = role1 getArgument 2
185 if bounded contains role1arg0
186 if bounded contains role1arg2
187 role2 <- body.filter(_.isRoleAssertion)
188 role2arg0 = role2 getArgument 0
189 role2arg2 = role2 getArgument 2
190 if bounded contains role2arg0
191 if bounded contains role2arg2
192 } yield Rule.create(
193 predID(
194 RSA.internal(bounded indexOf role1arg2),
195 RSA.internal(bounded indexOf role2arg2)
196 ),
197 role1 suffix "_b",
198 role2 suffix "_b",
199 predID(
200 RSA.internal(bounded indexOf role1arg0),
201 RSA.internal(bounded indexOf role2arg0)
202 ),
203 Atom.rdf(role1arg2, IRI.SAME_AS, role2arg2),
204 not(predNI(role1arg2))
205 )
206
207 /* Rules 6 */
208 val r6 = for {
209 role <- body.filter(_.isRoleAssertion)
210 arg0 = role getArgument 0
211 arg2 = role getArgument 2
212 if bounded contains arg0
213 if bounded contains arg2
214 sx <- List("_f", "_b")
215 } yield Rule.create(
216 predAQ(sx, Variable.create("V"), Variable.create("W")),
217 role suffix sx,
218 predID(
219 RSA.internal(bounded indexOf arg0),
220 RSA.internal(Variable.create("V"))
221 ),
222 predID(
223 RSA.internal(bounded indexOf arg2),
224 RSA.internal(Variable.create("W"))
225 )
226 )
227
228 /* Rules 7x */
229 val r7a =
230 for (sx <- List("f", "b"))
231 yield Rule.create(
232 predTQ(sx, Variable.create("U"), Variable.create("V")),
233 predAQ(sx, Variable.create("U"), Variable.create("V"))
234 )
235 val r7b =
236 for (r <- List("f", "b"))
237 yield Rule.create(
238 predTQ(r, Variable.create("U"), Variable.create("W")),
239 predAQ(r, Variable.create("U"), Variable.create("V")),
240 predTQ(r, Variable.create("V"), Variable.create("W"))
241 )
242
243 /* Rules 8x */
244 val r8a =
245 for (v <- answer) yield Rule.create(predSP, predQM, not(named(v)))
246 val r8b =
247 Rule.create(predSP, predFK)
248 val r8c =
249 for (sx <- List("_f", "_b"))
250 yield Rule.create(
251 predSP,
252 predTQ(sx, Variable.create("V"), Variable.create("V"))
253 )
254
255 /* Rule 9 */
256 val r9 = Rule.create(predANS, predQM, not(predSP))
257
258 List.empty
259 .prepended(r9)
260 .prependedAll(r8c)
261 .prepended(r8b)
262 .prependedAll(r8a)
263 .prependedAll(r7b)
264 .prependedAll(r7a)
265 .prependedAll(r6)
266 .prependedAll(r5c)
267 .prependedAll(r5b)
268 .prependedAll(r5a)
269 .prependedAll(r4c)
270 .prependedAll(r4b)
271 .prependedAll(r4a)
272 .prepended(r3c)
273 .prepended(r3b)
274 .prependedAll(r3a)
275 .prepended(r1)
276 }
277
278 /* NOTE: we are restricting to queries that contain conjunctions of
279 * atoms for the time being. This might need to be reviewed in the
280 * future.
281 */
282 private def queryToBody(body: Formula): List[Atom] =
283 body match {
284 case a: Atom => List(a);
285 case a: Conjunction =>
286 a.getConjuncts.asScala.toList.flatMap(queryToBody);
287 case _ => List()
288 }
289
290} // class FilteringProgram
291
292object FilteringProgram {
293 def apply(query: Query, constants: List[Term]): FilteringProgram =
294 new FilteringProgram(query, constants)
295}
diff --git a/src/main/scala/rsacomb/Main.scala b/src/main/scala/rsacomb/Main.scala
index 9cd6680..d2bc2a8 100644
--- a/src/main/scala/rsacomb/Main.scala
+++ b/src/main/scala/rsacomb/Main.scala
@@ -48,18 +48,20 @@ object RSAComb {
48 */ 48 */
49 49
50 val ontology = RSA.loadOntology(ontoPath) 50 val ontology = RSA.loadOntology(ontoPath)
51 ontology.isRSA 51 if (ontology.isRSA) {
52 52
53 /* Build canonical model */ 53 /* Build canonical model */
54 //val tboxCanon = rsa.canonicalModel() 54 //val tboxCanon = rsa.canonicalModel()
55 55
56 /* Load query */ 56 /* Load query */
57 //val query = ... 57 val query = RSA.test_query
58 58
59 /* Compute the filtering program from the given query */ 59 /* Compute the filtering program from the given query */
60 //val tboxFilter = rsa.filteringProgram(query) 60 val filter = ontology.getFilteringProgram(query)
61 61
62 /* ... */ 62 /* ... */
63
64 }
63 65
64 /* DEBUG ONLY */ 66 /* DEBUG ONLY */
65 println("Ok!") 67 println("Ok!")
diff --git a/src/main/scala/rsacomb/RDFTriple.scala b/src/main/scala/rsacomb/RDFTriple.scala
new file mode 100644
index 0000000..11ad6d4
--- /dev/null
+++ b/src/main/scala/rsacomb/RDFTriple.scala
@@ -0,0 +1,60 @@
1package rsacomb
2
3import tech.oxfordsemantic.jrdfox.logic.{Atom, IRI, TupleTableName}
4
5trait RDFTriple {
6
7 implicit class RDFTriple(atom: Atom) {
8
9 /* Is this the best way to determine if an atom is an RDF triple?
10 * Note that we can't use `getNumberOfArguments()` because is not
11 * "consistent":
12 * - for an atom created with `rdf(<term1>, <term2>, <term3>)`,
13 * `getNumberOfArguments` returns 3
14 * - for an atom created with `Atom.create(<tupletablename>, <term1>,
15 * <term2>, <term3>)`, `getNumberOfArguments()` returns 3
16 *
17 * This is probably because `Atom.rdf(...) is implemented as:
18 * ```scala
19 * def rdf(term1: Term, term2: Term, term3: Term): Atom =
20 * Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3)
21 * ```
22 */
23 def isRdfTriple: Boolean =
24 atom.getTupleTableName.getIRI.equals("internal:triple")
25
26 def isClassAssertion: Boolean =
27 atom.isRdfTriple && atom.getArgument(1).equals(IRI.RDF_TYPE)
28
29 def isRoleAssertion: Boolean =
30 atom.isRdfTriple && !atom.getArgument(1).equals(IRI.RDF_TYPE)
31
32 def suffix(sx: String): Atom =
33 if (this.isClassAssertion) {
34 val newclass = atom.getArgument(2) match {
35 case iri: IRI => IRI.create(iri.getIRI.appendedAll(sx))
36 case other => other
37 }
38 Atom.rdf(
39 atom getArgument 0,
40 atom getArgument 1,
41 newclass
42 )
43 } else if (this.isRoleAssertion) {
44 val newrole = atom.getArgument(1) match {
45 case iri: IRI => IRI.create(iri.getIRI.appendedAll(sx))
46 case other => other
47 }
48 Atom.rdf(
49 atom getArgument 0,
50 newrole,
51 atom getArgument 2
52 )
53 } else {
54 val newname =
55 TupleTableName.create(atom.getTupleTableName.getIRI.appendedAll(sx))
56 Atom.create(newname, atom.getArguments())
57 }
58 }
59
60}
diff --git a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala b/src/main/scala/rsacomb/RDFoxAxiomConverter.scala
index 0a79823..d90c966 100644
--- a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala
+++ b/src/main/scala/rsacomb/RDFoxAxiomConverter.scala
@@ -1,6 +1,11 @@
1package rsacomb 1package rsacomb
2 2
3import org.semanticweb.owlapi.model.{OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom, OWLObjectPropertyExpression} 3import org.semanticweb.owlapi.model.{
4 OWLAxiom,
5 OWLSubClassOfAxiom,
6 OWLEquivalentClassesAxiom,
7 OWLObjectPropertyExpression
8}
4import org.semanticweb.owlapi.model.OWLAxiomVisitorEx 9import org.semanticweb.owlapi.model.OWLAxiomVisitorEx
5 10
6import tech.oxfordsemantic.jrdfox.logic.{Rule, BodyFormula} 11import tech.oxfordsemantic.jrdfox.logic.{Rule, BodyFormula}
@@ -17,22 +22,24 @@ import org.semanticweb.owlapi.model.OWLObjectProperty
17object RDFoxAxiomConverter { 22object RDFoxAxiomConverter {
18 23
19 def apply( 24 def apply(
20 term : Term = Variable.create("x"), 25 term: Term,
21 skolem : SkolemStrategy = SkolemStrategy.None, 26 skolem: SkolemStrategy = SkolemStrategy.None,
22 unsafe : List[OWLObjectPropertyExpression] = List() 27 unsafe: List[OWLObjectPropertyExpression] = List()
23 ) : RDFoxAxiomConverter = 28 ): RDFoxAxiomConverter =
24 new RDFoxAxiomConverter(term, skolem, unsafe) 29 new RDFoxAxiomConverter(term, skolem, unsafe)
25 30
26} // object RDFoxAxiomConverter 31} // object RDFoxAxiomConverter
27 32
28class RDFoxAxiomConverter(term : Term, skolem : SkolemStrategy, unsafe : List[OWLObjectPropertyExpression]) 33class RDFoxAxiomConverter(
29 extends OWLAxiomVisitorEx[List[Rule]] 34 term: Term,
30{ 35 skolem: SkolemStrategy,
36 unsafe: List[OWLObjectPropertyExpression]
37) extends OWLAxiomVisitorEx[List[Rule]] {
31 38
32 override 39 override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = {
33 def visit(axiom : OWLSubClassOfAxiom) : List[Rule] = {
34 // Skolemization is needed only for the head of an axiom 40 // Skolemization is needed only for the head of an axiom
35 val subVisitor = new RDFoxClassExprConverter(term,SkolemStrategy.None, unsafe) 41 val subVisitor =
42 new RDFoxClassExprConverter(term, SkolemStrategy.None, unsafe)
36 val superVisitor = new RDFoxClassExprConverter(term, skolem, unsafe) 43 val superVisitor = new RDFoxClassExprConverter(term, skolem, unsafe)
37 // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext): 44 // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext):
38 // - the `res` List is a list of atoms resulting from the conversion 45 // - the `res` List is a list of atoms resulting from the conversion
@@ -45,30 +52,27 @@ class RDFoxAxiomConverter(term : Term, skolem : SkolemStrategy, unsafe : List[OW
45 val sup = axiom.getSuperClass.accept(superVisitor) 52 val sup = axiom.getSuperClass.accept(superVisitor)
46 val head = sup.res.asJava 53 val head = sup.res.asJava
47 val body = (sub.res ++ sup.ext).asJava 54 val body = (sub.res ++ sup.ext).asJava
48 List(Rule.create(head,body)) 55 List(Rule.create(head, body))
49 } 56 }
50 57
51 override 58 override def visit(axiom: OWLEquivalentClassesAxiom): List[Rule] = {
52 def visit(axiom : OWLEquivalentClassesAxiom) : List[Rule] = {
53 for { 59 for {
54 axiom1 <- axiom.asPairwiseAxioms.asScala.toList 60 axiom1 <- axiom.asPairwiseAxioms.asScala.toList
55 axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList 61 axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList
56 rule <- axiom2.accept(this) 62 rule <- axiom2.accept(this)
57 } yield rule 63 } yield rule
58 } 64 }
59 65
60 override 66 override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = {
61 def visit(axiom : OWLSubObjectPropertyOfAxiom) : List[Rule] = { 67 val term1 = RSA.getFreshVariable()
62 // TODO: variables needs to be handled at visitor level. Hardcoding 68 val subVisitor =
63 // the name of the varibles might lead to errors for complex cases. 69 new RDFoxPropertyExprConverter(term, term1, SkolemStrategy.None)
64 val term1 = Variable.create("y") 70 val superVisitor = new RDFoxPropertyExprConverter(term, term1, skolem)
65 val subVisitor = new RDFoxPropertyExprConverter(term,term1,SkolemStrategy.None)
66 val superVisitor = new RDFoxPropertyExprConverter(term,term1,skolem)
67 val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor) 71 val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor)
68 val head: List[Atom] = axiom.getSuperProperty.accept(superVisitor) 72 val head: List[Atom] = axiom.getSuperProperty.accept(superVisitor)
69 List(Rule.create(head.asJava,body.asJava)) 73 List(Rule.create(head.asJava, body.asJava))
70 } 74 }
71 75
72 def doDefault(axiom : OWLAxiom) : List[Rule] = List() 76 def doDefault(axiom: OWLAxiom): List[Rule] = List()
73 77
74} // class RDFoxAxiomConverter 78} // class RDFoxAxiomConverter
diff --git a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala
index b9d3ea2..a319d86 100644
--- a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala
+++ b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala
@@ -34,7 +34,7 @@ import org.semanticweb.owlapi.model.OWLObjectProperty
34object RDFoxClassExprConverter { 34object RDFoxClassExprConverter {
35 35
36 def apply( 36 def apply(
37 term: Term = Variable.create("x"), 37 term: Term,
38 skolem: SkolemStrategy = SkolemStrategy.None, 38 skolem: SkolemStrategy = SkolemStrategy.None,
39 unsafe: List[OWLObjectPropertyExpression] = List() 39 unsafe: List[OWLObjectPropertyExpression] = List()
40 ): RDFoxClassExprConverter = 40 ): RDFoxClassExprConverter =
@@ -61,7 +61,8 @@ class RDFoxClassExprConverter(
61 61
62 // OWLClass 62 // OWLClass
63 override def visit(expr: OWLClass): RDFoxRuleShards = { 63 override def visit(expr: OWLClass): RDFoxRuleShards = {
64 val atom = List(Atom.rdf(term, IRI.RDF_TYPE, expr.getIRI())) 64 val iri: IRI = if (expr.isTopEntity()) IRI.THING else expr.getIRI()
65 val atom = List(Atom.rdf(term, IRI.RDF_TYPE, iri))
65 RDFoxRuleShards(atom, List()) 66 RDFoxRuleShards(atom, List())
66 } 67 }
67 68
@@ -94,9 +95,8 @@ class RDFoxClassExprConverter(
94 95
95 // OWLObjectSomeValuesFrom 96 // OWLObjectSomeValuesFrom
96 override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = { 97 override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = {
97 // TODO: variables needs to be handled at visitor level. Hardcoding 98 val y = RSA.getFreshVariable()
98 // the name of the varibles might lead to errors for complex cases. 99 // Here we are assuming a role name
99 val y = Variable.create("y")
100 val prop = expr.getProperty() 100 val prop = expr.getProperty()
101 // Computes the result of rule skolemization. Depending on the used 101 // Computes the result of rule skolemization. Depending on the used
102 // technique it might involve the introduction of additional atoms, 102 // technique it might involve the introduction of additional atoms,
@@ -139,7 +139,7 @@ class RDFoxClassExprConverter(
139 // OWLObjectMaxCardinality 139 // OWLObjectMaxCardinality
140 override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = { 140 override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = {
141 // TODO: again, no hardcoded variables 141 // TODO: again, no hardcoded variables
142 val vars = List(Variable.create("y"), Variable.create("z")) 142 val vars = List(RSA.getFreshVariable(), RSA.getFreshVariable())
143 val classResult = RDFoxClassExprConverter.merge( 143 val classResult = RDFoxClassExprConverter.merge(
144 vars 144 vars
145 .map(new RDFoxClassExprConverter(_, skolem, unsafe)) 145 .map(new RDFoxClassExprConverter(_, skolem, unsafe))
diff --git a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala b/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala
index 340fa90..6eecdbf 100644
--- a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala
+++ b/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala
@@ -3,7 +3,7 @@ package rsacomb
3import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty} 3import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty}
4import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx 4import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx
5 5
6import tech.oxfordsemantic.jrdfox.logic.{Atom, Term, IRI, Variable, Literal} 6import tech.oxfordsemantic.jrdfox.logic.{Atom, Term, IRI, Literal}
7 7
8import rsacomb.SkolemStrategy 8import rsacomb.SkolemStrategy
9import org.semanticweb.owlapi.model.OWLObjectInverseOf 9import org.semanticweb.owlapi.model.OWLObjectInverseOf
diff --git a/src/main/scala/rsacomb/RDFoxUtil.scala b/src/main/scala/rsacomb/RDFoxUtil.scala
index 4cefd83..9699fb4 100644
--- a/src/main/scala/rsacomb/RDFoxUtil.scala
+++ b/src/main/scala/rsacomb/RDFoxUtil.scala
@@ -15,6 +15,10 @@ object RDFoxUtil {
15 IRI.create(iri.getIRIString()) 15 IRI.create(iri.getIRIString())
16 } 16 }
17 17
18 implicit def owlapi2rdfox(iri: String): IRI = {
19 IRI.create(iri)
20 }
21
18 def openConnection( 22 def openConnection(
19 dataStore: String 23 dataStore: String
20 ): (ServerConnection, DataStoreConnection) = { 24 ): (ServerConnection, DataStoreConnection) = {
@@ -37,7 +41,7 @@ object RDFoxUtil {
37 (server, data) 41 (server, data)
38 } 42 }
39 43
40 def query( 44 def submitQuery(
41 data: DataStoreConnection, 45 data: DataStoreConnection,
42 prefixes: Prefixes, 46 prefixes: Prefixes,
43 query: String 47 query: String
diff --git a/src/main/scala/rsacomb/RSA.scala b/src/main/scala/rsacomb/RSA.scala
index 79617c7..9125f9f 100644
--- a/src/main/scala/rsacomb/RSA.scala
+++ b/src/main/scala/rsacomb/RSA.scala
@@ -8,6 +8,18 @@ import tech.oxfordsemantic.jrdfox.Prefixes
8import tech.oxfordsemantic.jrdfox.logic.IRI 8import tech.oxfordsemantic.jrdfox.logic.IRI
9import org.semanticweb.owlapi.apibinding.OWLManager 9import org.semanticweb.owlapi.apibinding.OWLManager
10import org.semanticweb.owlapi.model.OWLOntology 10import org.semanticweb.owlapi.model.OWLOntology
11import rsacomb.RSAOntology
12
13// Debug only
14import tech.oxfordsemantic.jrdfox.logic.{
15 Formula,
16 Atom,
17 Variable,
18 Query,
19 QueryType,
20 Conjunction
21}
22import scala.collection.JavaConverters._
11 23
12object RSA extends RSAOntology { 24object RSA extends RSAOntology {
13 25
@@ -18,9 +30,30 @@ object RSA extends RSAOntology {
18 Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") 30 Prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#")
19 Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") 31 Prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#")
20 32
21 def internal(name: String): IRI = 33 // Counter used to implement a simple fresh variable generator
34 private var counter = -1;
35
36 def getFreshVariable(): Variable = {
37 counter += 1
38 Variable.create(f"I$counter%03d")
39 }
40
41 val varX = Variable.create("X")
42 val varY = Variable.create("Y")
43 val varZ = Variable.create("Z")
44 val testAnswerVars = List[Variable](varX, varY, varZ).asJava;
45 val testFormula: Formula =
46 Conjunction.create(
47 Atom.rdf(varX, IRI.TOP_OBJECT_PROPERTY, varY),
48 Atom.rdf(varY, IRI.TOP_OBJECT_PROPERTY, varZ)
49 )
50 val test_query =
51 Query.create(QueryType.SELECT, false, testAnswerVars, testFormula)
52
53 def internal(name: Any): IRI =
22 IRI.create( 54 IRI.create(
23 Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI + name 55 Prefixes.getPrefixIRIsByPrefixName.get("internal:").getIRI
56 + name.toString
24 ) 57 )
25 58
26 // TODO: move this somewhere else... maybe an OntoUtils class or something. 59 // TODO: move this somewhere else... maybe an OntoUtils class or something.
diff --git a/src/main/scala/rsacomb/RSAAxiom.scala b/src/main/scala/rsacomb/RSAAxiom.scala
index 9e9a016..50dc37e 100644
--- a/src/main/scala/rsacomb/RSAAxiom.scala
+++ b/src/main/scala/rsacomb/RSAAxiom.scala
@@ -1,10 +1,23 @@
1package rsacomb 1package rsacomb
2 2
3/* Java imports */ 3/* Java imports */
4import org.semanticweb.owlapi.model.{OWLAxiom,OWLSubClassOfAxiom, OWLEquivalentClassesAxiom} 4import org.semanticweb.owlapi.model.{
5import org.semanticweb.owlapi.model.{OWLObjectPropertyExpression,OWLClass,OWLClassExpression,OWLObjectSomeValuesFrom,OWLObjectMaxCardinality} 5 OWLAxiom,
6 OWLSubClassOfAxiom,
7 OWLEquivalentClassesAxiom
8}
9import org.semanticweb.owlapi.model.{
10 OWLObjectPropertyExpression,
11 OWLClass,
12 OWLClassExpression,
13 OWLObjectSomeValuesFrom,
14 OWLObjectMaxCardinality
15}
6import org.semanticweb.owlapi.model.ClassExpressionType 16import org.semanticweb.owlapi.model.ClassExpressionType
7import org.semanticweb.owlapi.model.{OWLAxiomVisitorEx,OWLClassExpressionVisitorEx} 17import org.semanticweb.owlapi.model.{
18 OWLAxiomVisitorEx,
19 OWLClassExpressionVisitorEx
20}
8 21
9/* Wrapper trait for the implicit class `RSAAxiom`. 22/* Wrapper trait for the implicit class `RSAAxiom`.
10 */ 23 */
@@ -16,10 +29,10 @@ trait RSAAxiom {
16 */ 29 */
17 private sealed trait RSAAxiomType 30 private sealed trait RSAAxiomType
18 private object RSAAxiomType { 31 private object RSAAxiomType {
19 case object T3 extends RSAAxiomType // ∃R.A ⊑ B 32 case object T3 extends RSAAxiomType // ∃R.A ⊑ B
20 case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B 33 case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B
21 case object T4 extends RSAAxiomType // A ⊑ ≤1R.B 34 case object T4 extends RSAAxiomType // A ⊑ ≤1R.B
22 case object T5 extends RSAAxiomType // A ⊑ ∃R.B 35 case object T5 extends RSAAxiomType // A ⊑ ∃R.B
23 } 36 }
24 37
25 /* Implements additional features on top of `OWLAxiom` from 38 /* Implements additional features on top of `OWLAxiom` from
@@ -32,20 +45,22 @@ trait RSAAxiom {
32 * In order to reason about role unsafety in Horn-ALCHOIQ 45 * In order to reason about role unsafety in Horn-ALCHOIQ
33 * ontologies we need to detect and filter axioms by their 46 * ontologies we need to detect and filter axioms by their
34 * "type". 47 * "type".
35 * 48 *
36 * This is a simple implementation following the Visitor 49 * This is a simple implementation following the Visitor
37 * pattern imposed by the OWLAPI. 50 * pattern imposed by the OWLAPI.
38 */ 51 */
39 private class RSAAxiomTypeDetector(t: RSAAxiomType) 52 private class RSAAxiomTypeDetector(t: RSAAxiomType)
40 extends OWLAxiomVisitorEx[Boolean] 53 extends OWLAxiomVisitorEx[Boolean] {
41 { 54 override def visit(axiom: OWLSubClassOfAxiom): Boolean = {
42 override
43 def visit(axiom: OWLSubClassOfAxiom): Boolean = {
44 val sub = axiom.getSubClass().getClassExpressionType() 55 val sub = axiom.getSubClass().getClassExpressionType()
45 val sup = axiom.getSuperClass().getClassExpressionType() 56 val sup = axiom.getSuperClass().getClassExpressionType()
46 t match { 57 t match {
47 case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B 58 case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B
48 axiom.isT5 && axiom.getSubClass().asInstanceOf[OWLObjectSomeValuesFrom].getFiller.isOWLThing 59 axiom.isT3 && axiom
60 .getSubClass()
61 .asInstanceOf[OWLObjectSomeValuesFrom]
62 .getFiller
63 .isOWLThing
49 case RSAAxiomType.T3 => // ∃R.A ⊑ B 64 case RSAAxiomType.T3 => // ∃R.A ⊑ B
50 sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS 65 sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS
51 case RSAAxiomType.T4 => // A ⊑ ≤1R.B 66 case RSAAxiomType.T4 => // A ⊑ ≤1R.B
@@ -55,13 +70,12 @@ trait RSAAxiom {
55 } 70 }
56 } 71 }
57 72
58 override 73 override def visit(axiom: OWLEquivalentClassesAxiom): Boolean = {
59 def visit(axiom: OWLEquivalentClassesAxiom): Boolean = {
60 // TODO 74 // TODO
61 false 75 false
62 } 76 }
63 77
64 def doDefault(axiom : OWLAxiom): Boolean = false 78 def doDefault(axiom: OWLAxiom): Boolean = false
65 } 79 }
66 80
67 private def isOfType(t: RSAAxiomType): Boolean = { 81 private def isOfType(t: RSAAxiomType): Boolean = {
@@ -74,13 +88,13 @@ trait RSAAxiom {
74 def isT3: Boolean = isOfType(RSAAxiomType.T3) 88 def isT3: Boolean = isOfType(RSAAxiomType.T3)
75 def isT4: Boolean = isOfType(RSAAxiomType.T4) 89 def isT4: Boolean = isOfType(RSAAxiomType.T4)
76 def isT5: Boolean = isOfType(RSAAxiomType.T5) 90 def isT5: Boolean = isOfType(RSAAxiomType.T5)
77 91
78 /* Extracting ObjectPropertyExpressions from axioms 92 /* Extracting ObjectPropertyExpressions from axioms
79 * 93 *
80 * This extracts all ObjectPropertyExpressions from a given 94 * This extracts all ObjectPropertyExpressions from a given
81 * axiom. While the implementation is generic we use it on axioms 95 * axiom. While the implementation is generic we use it on axioms
82 * of specific types (see above). 96 * of specific types (see above).
83 * 97 *
84 * NOTE: it is not possible to use the `objectPropertyInSignature` 98 * NOTE: it is not possible to use the `objectPropertyInSignature`
85 * method of `OWLAxiom` because it returns all "role names" involved 99 * method of `OWLAxiom` because it returns all "role names" involved
86 * in the signature of an axiom. In particular we won't get the inverse 100 * in the signature of an axiom. In particular we won't get the inverse
@@ -88,55 +102,61 @@ trait RSAAxiom {
88 * itself instead). 102 * itself instead).
89 */ 103 */
90 private class RSAAxiomRoleExtractor() 104 private class RSAAxiomRoleExtractor()
91 extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] 105 extends OWLAxiomVisitorEx[List[OWLObjectPropertyExpression]] {
92 {
93 106
94 private class RSAExprRoleExtractor() 107 private class RSAExprRoleExtractor()
95 extends OWLClassExpressionVisitorEx[List[OWLObjectPropertyExpression]] 108 extends OWLClassExpressionVisitorEx[
96 { 109 List[OWLObjectPropertyExpression]
97 override 110 ] {
98 def visit(expr: OWLObjectSomeValuesFrom): List[OWLObjectPropertyExpression] = 111 override def visit(
112 expr: OWLObjectSomeValuesFrom
113 ): List[OWLObjectPropertyExpression] =
99 List(expr.getProperty) 114 List(expr.getProperty)
100 115
101 override 116 override def visit(
102 def visit(expr: OWLObjectMaxCardinality): List[OWLObjectPropertyExpression] = 117 expr: OWLObjectMaxCardinality
118 ): List[OWLObjectPropertyExpression] =
103 List(expr.getProperty) 119 List(expr.getProperty)
104 120
105 /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However 121 /* NOTE: this instance of `visit` for `OWLClass` shouldn't be necessary. However
106 * if missing, the code throws a `NullPointerException`. It seems like, for some 122 * if missing, the code throws a `NullPointerException`. It seems like, for some
107 * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as 123 * reason, `OWLClass` is not really a subinterface of `OWLClassExpression`, as
108 * stated in the JavaDocs. 124 * stated in the JavaDocs.
109 */ 125 */
110 override 126 override def visit(expr: OWLClass): List[OWLObjectPropertyExpression] =
111 def visit(expr: OWLClass): List[OWLObjectPropertyExpression] =
112 List() 127 List()
113 128
114 def doDefault(expr: OWLClassExpression): List[OWLObjectPropertyExpression] = 129 def doDefault(
130 expr: OWLClassExpression
131 ): List[OWLObjectPropertyExpression] =
115 List() 132 List()
116 } 133 }
117 134
118 override 135 override def visit(
119 def visit(axiom: OWLSubClassOfAxiom): List[OWLObjectPropertyExpression] = { 136 axiom: OWLSubClassOfAxiom
137 ): List[OWLObjectPropertyExpression] = {
120 val visitor = new RSAExprRoleExtractor() 138 val visitor = new RSAExprRoleExtractor()
121 val sub = axiom.getSubClass.accept(visitor) 139 val sub = axiom.getSubClass.accept(visitor)
122 val sup = axiom.getSuperClass.accept(visitor) 140 val sup = axiom.getSuperClass.accept(visitor)
123 sub ++ sup 141 sub ++ sup
124 } 142 }
125 143
126 override 144 override def visit(
127 def visit(axiom: OWLEquivalentClassesAxiom): List[OWLObjectPropertyExpression] = { 145 axiom: OWLEquivalentClassesAxiom
146 ): List[OWLObjectPropertyExpression] = {
128 // TODO 147 // TODO
129 List() 148 List()
130 } 149 }
131 150
132 def doDefault(axiom : OWLAxiom): List[OWLObjectPropertyExpression] = List() 151 def doDefault(axiom: OWLAxiom): List[OWLObjectPropertyExpression] = List()
133 } 152 }
134 153
135 /* Exposed methods */ 154 /* Exposed methods */
136 def objectPropertyExpressionsInSignature: List[OWLObjectPropertyExpression] = { 155 def objectPropertyExpressionsInSignature
156 : List[OWLObjectPropertyExpression] = {
137 val visitor = new RSAAxiomRoleExtractor() 157 val visitor = new RSAAxiomRoleExtractor()
138 axiom.accept(visitor) 158 axiom.accept(visitor)
139 } 159 }
140 } 160 }
141 161
142} // trait RSAAxiom \ No newline at end of file 162} // trait RSAAxiom
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala
index 3d9210e..efd6e7f 100644
--- a/src/main/scala/rsacomb/RSAOntology.scala
+++ b/src/main/scala/rsacomb/RSAOntology.scala
@@ -19,6 +19,7 @@ import scalax.collection.GraphEdge.UnDiEdge
19 19
20/* Debug only */ 20/* Debug only */
21import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer 21import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer
22import tech.oxfordsemantic.jrdfox.logic._
22 23
23/* Wrapper trait for the implicit class `RSAOntology`. 24/* Wrapper trait for the implicit class `RSAOntology`.
24 */ 25 */
@@ -27,7 +28,9 @@ trait RSAOntology {
27 /* Implements additional features to reason about RSA ontologies 28 /* Implements additional features to reason about RSA ontologies
28 * on top of `OWLOntology` from the OWLAPI. 29 * on top of `OWLOntology` from the OWLAPI.
29 */ 30 */
30 implicit class RSAOntology(ontology: OWLOntology) extends RSAAxiom { 31 implicit class RSAOntology(ontology: OWLOntology)
32 extends RSAAxiom
33 with RDFTriple {
31 34
32 /* Steps for RSA check 35 /* Steps for RSA check
33 * 1) convert ontology axioms into LP rules 36 * 1) convert ontology axioms into LP rules
@@ -60,7 +63,7 @@ trait RSAOntology {
60 val datalog = for { 63 val datalog = for {
61 axiom <- axioms 64 axiom <- axioms
62 visitor = new RDFoxAxiomConverter( 65 visitor = new RDFoxAxiomConverter(
63 Variable.create("x"), 66 RSA.getFreshVariable(),
64 SkolemStrategy.ConstantRSA(axiom.toString), 67 SkolemStrategy.ConstantRSA(axiom.toString),
65 unsafe 68 unsafe
66 ) 69 )
@@ -163,8 +166,9 @@ trait RSAOntology {
163 } yield role1 166 } yield role1
164 167
165 /* TODO: We should be able to avoid this last conversion to List. 168 /* TODO: We should be able to avoid this last conversion to List.
166 * Maybe we should just move everything to Sets instead of Lists, since 169 * Maybe we should just move everything to Sets instead of Lists,
167 * they have a more straightforward conversion from Java collections. 170 * since they have a more straightforward conversion from Java
171 * collections.
168 */ 172 */
169 (unsafe1 ++ unsafe2).toList 173 (unsafe1 ++ unsafe2).toList
170 } 174 }
@@ -184,6 +188,99 @@ trait RSAOntology {
184 Graph(edges: _*) 188 Graph(edges: _*)
185 } 189 }
186 190
191 def getFilteringProgram(query: Query): List[Rule] = {
192
193 // Import implicit conversion to RDFox IRI
194 import RDFoxUtil._
195
196 sealed trait Reified;
197 case class ReifiedHead(bind: BindAtom, atoms: List[Atom]) extends Reified
198 case class ReifiedBody(atoms: List[Atom]) extends Reified
199 case class Unaltered(formula: BodyFormula) extends Reified
200
201 def getBindAtom(atom: Atom): BindAtom = {
202 // TODO: We need to implement another way to introduce fresh
203 // variables.
204 val newvar = RSA.getFreshVariable()
205 val name =
206 Literal.create(atom.getTupleTableName.getIRI, Datatype.XSD_STRING)
207 val args = atom
208 .getArguments()
209 .asScala
210 .toSeq
211 .prepended(name)
212 BindAtom.create(
213 BuiltinFunctionCall
214 .create("SKOLEM", args: _*),
215 newvar
216 )
217 }
218
219 def reifyAtom(atom: Atom, variable: Variable): List[Atom] = {
220 def iri(i: Int) = atom.getTupleTableName().getIRI() ++ s"_$i"
221 atom
222 .getArguments()
223 .asScala
224 .zipWithIndex
225 .map { case (t, i) => Atom.rdf(variable, iri(i), t) }
226 .toList
227 }
228
229 def reify(
230 formula: BodyFormula,
231 head: Boolean
232 ): Reified = {
233 def default[A <: BodyFormula](x: A) = Unaltered(x)
234 formula match {
235 case a: Atom => {
236 if (!a.isRdfTriple) {
237 if (head) {
238 val b = getBindAtom(a)
239 ReifiedHead(b, reifyAtom(a, b.getBoundVariable))
240 } else {
241 val varA = RSA.getFreshVariable()
242 ReifiedBody(reifyAtom(a, varA))
243 }
244 } else {
245 default(a)
246 }
247 }
248 case a => default(a)
249 }
250 }
251
252 def skolemizeRule(rule: Rule): Rule = {
253 // Rule body
254 val body =
255 rule.getBody.asScala.map(reify(_, false)).flatMap {
256 case ReifiedHead(_, _) => List(); /* handle impossible case */
257 case ReifiedBody(x) => x;
258 case Unaltered(x) => List(x)
259 }
260 // Rule head
261 val reified = rule.getHead.asScala.map(reify(_, true))
262 val skols = reified.flatMap {
263 case ReifiedHead(x, _) => Some(x);
264 case ReifiedBody(_) => None; /* handle impossible case */
265 case Unaltered(_) => None
266 }
267 val head = reified.flatMap {
268 case ReifiedHead(_, x) => x;
269 case ReifiedBody(_) => List(); /* handle impossible case */
270 case Unaltered(x) =>
271 List(x.asInstanceOf[Atom]) /* Can we do better that a cast? */
272 }
273 Rule.create(head.asJava, (skols ++ body).asJava)
274 }
275
276 // DEBUG
277 val rules = FilteringProgram(query, List()).rules
278 println("FILTERING PROGRAM:")
279 rules.map(skolemizeRule(_)).foreach(println(_))
280
281 List()
282 }
283
187 } // implicit class RSAOntology 284 } // implicit class RSAOntology
188 285
189} // trait RSAOntology 286} // trait RSAOntology