aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/rsacomb
diff options
context:
space:
mode:
authorFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-18 19:13:25 +0000
committerFederico Igne <federico.igne@cs.ox.ac.uk>2020-11-18 19:13:25 +0000
commit1efc189e90240c162b54cbc50362b46786643dad (patch)
tree9beabe0a2af7ba1674aea0060787782aa72e8a83 /src/main/scala/rsacomb
parenta45aeff72b82bbc9a52f10929bf15b414c868525 (diff)
downloadRSAComb-1efc189e90240c162b54cbc50362b46786643dad.tar.gz
RSAComb-1efc189e90240c162b54cbc50362b46786643dad.zip
Reorganize project with Java-like folder structure
Diffstat (limited to 'src/main/scala/rsacomb')
-rw-r--r--src/main/scala/rsacomb/CanonicalModel.scala278
-rw-r--r--src/main/scala/rsacomb/FilteringProgram.scala366
-rw-r--r--src/main/scala/rsacomb/Main.scala249
-rw-r--r--src/main/scala/rsacomb/RDFoxAxiomConverter.scala106
-rw-r--r--src/main/scala/rsacomb/RDFoxClassExprConverter.scala159
-rw-r--r--src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala35
-rw-r--r--src/main/scala/rsacomb/RDFoxRuleShards.scala5
-rw-r--r--src/main/scala/rsacomb/RSA.scala100
-rw-r--r--src/main/scala/rsacomb/RSAAtom.scala88
-rw-r--r--src/main/scala/rsacomb/RSAAxiom.scala155
-rw-r--r--src/main/scala/rsacomb/RSAOntology.scala378
-rw-r--r--src/main/scala/rsacomb/RSASuffix.scala31
-rw-r--r--src/main/scala/rsacomb/SkolemStrategy.scala78
-rw-r--r--src/main/scala/rsacomb/implicits/JavaCollections.scala13
-rw-r--r--src/main/scala/rsacomb/implicits/RDFox.scala20
-rw-r--r--src/main/scala/rsacomb/util/RDFoxHelpers.scala100
16 files changed, 0 insertions, 2161 deletions
diff --git a/src/main/scala/rsacomb/CanonicalModel.scala b/src/main/scala/rsacomb/CanonicalModel.scala
deleted file mode 100644
index d9e1641..0000000
--- a/src/main/scala/rsacomb/CanonicalModel.scala
+++ /dev/null
@@ -1,278 +0,0 @@
1package rsacomb
2
3import org.semanticweb.owlapi.model.{OWLObjectInverseOf, OWLObjectProperty}
4import org.semanticweb.owlapi.model.{
5 OWLClass,
6 // OWLObjectProperty,
7 OWLSubObjectPropertyOfAxiom,
8 // OWLObjectPropertyExpression,
9 OWLObjectSomeValuesFrom,
10 OWLSubClassOfAxiom
11}
12
13import tech.oxfordsemantic.jrdfox.logic.datalog.{
14 Rule,
15 BodyFormula,
16 TupleTableAtom,
17 Negation
18}
19import tech.oxfordsemantic.jrdfox.logic.expression.{
20 Term,
21 Variable,
22 // Resource,
23 IRI
24}
25
26import suffix.{Empty, Forward, Backward, Inverse}
27import util.RSA
28
29class CanonicalModel(val ontology: RSAOntology) extends RSAAxiom {
30
31 import implicits.RDFox._
32 import implicits.JavaCollections._
33
34 val named: List[Rule] =
35 ontology.individuals.map(a => Rule.create(RSA.Named(a)))
36
37 val rolesAdditionalRules: List[Rule] = {
38 // Given a role (predicate) compute additional logic rules
39 def additional(pred: String): Seq[Rule] = {
40 val varX = Variable.create("X")
41 val varY = Variable.create("Y")
42 for (
43 (hSuffix, bSuffix) <- List(
44 (Empty, Forward),
45 (Empty, Backward),
46 (Inverse, Forward + Inverse),
47 (Inverse, Backward + Inverse),
48 (Backward + Inverse, Forward),
49 (Forward + Inverse, Backward),
50 (Backward, Forward + Inverse),
51 (Forward, Backward + Inverse)
52 )
53 )
54 yield Rule.create(
55 TupleTableAtom.rdf(varX, pred :: hSuffix, varY),
56 TupleTableAtom.rdf(varX, pred :: bSuffix, varY)
57 )
58 }
59 // Compute additional rules per role
60 ontology.roles
61 .collect { case prop: OWLObjectProperty => prop }
62 .map(_.getIRI.getIRIString)
63 .flatMap(additional)
64 }
65
66 private lazy val topAxioms: List[Rule] = {
67 val varX = Variable.create("X")
68 val varY = Variable.create("Y")
69 val concepts = ontology.concepts.map(c => {
70 Rule.create(
71 RSA.Thing(varX),
72 TupleTableAtom.rdf(varX, IRI.RDF_TYPE, c.getIRI)
73 )
74 })
75 val roles = ontology.roles.map(r => {
76 val name = r match {
77 case x: OWLObjectProperty => x.getIRI.getIRIString
78 case x: OWLObjectInverseOf =>
79 x.getInverse.getNamedProperty.getIRI.getIRIString :: Inverse
80 }
81 Rule.create(
82 List(RSA.Thing(varX), RSA.Thing(varY)),
83 List(TupleTableAtom.rdf(varX, name, varY))
84 )
85 })
86 concepts ::: roles
87 }
88
89 private val equalityAxioms: List[Rule] = {
90 val varX = Variable.create("X")
91 val varY = Variable.create("Y")
92 val varZ = Variable.create("Z")
93 List(
94 // Reflexivity
95 Rule.create(RSA.congruent(varX, varX), RSA.Thing(varX)),
96 // Simmetry
97 Rule.create(RSA.congruent(varY, varX), RSA.congruent(varX, varY)),
98 // Transitivity
99 Rule.create(
100 RSA.congruent(varX, varZ),
101 RSA.congruent(varX, varY),
102 RSA.congruent(varY, varZ)
103 )
104 )
105 }
106
107 val rules: List[Rule] = {
108 // Compute rules from ontology axioms
109 val rules = ontology.axioms.flatMap(_.accept(RuleGenerator))
110 // Return full set of rules
111 rules ::: rolesAdditionalRules ::: topAxioms ::: equalityAxioms ::: named
112 }
113
114 object RuleGenerator
115 extends RDFoxAxiomConverter(
116 Variable.create("X"),
117 ontology.unsafeRoles,
118 SkolemStrategy.None,
119 Empty
120 ) {
121
122 private def rules1(axiom: OWLSubClassOfAxiom): List[Rule] = {
123 val unfold = ontology.unfold(axiom).toList
124 // Fresh Variables
125 val v0 = RSA("v0_" ++ axiom.hashed)
126 val varX = Variable.create("X")
127 implicit val unfoldTerm = RSA(unfold.hashCode.toString)
128 // TODO: use axiom.toTriple instead
129 val atomA: TupleTableAtom = {
130 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
131 TupleTableAtom.rdf(varX, IRI.RDF_TYPE, cls)
132 }
133 val roleRf: TupleTableAtom = {
134 val visitor =
135 new RDFoxPropertyExprConverter(varX, v0, Forward)
136 axiom.getSuperClass
137 .asInstanceOf[OWLObjectSomeValuesFrom]
138 .getProperty
139 .accept(visitor)
140 .head
141 }
142 val atomB: TupleTableAtom = {
143 val cls = axiom.getSuperClass
144 .asInstanceOf[OWLObjectSomeValuesFrom]
145 .getFiller
146 .asInstanceOf[OWLClass]
147 .getIRI
148 TupleTableAtom.rdf(v0, IRI.RDF_TYPE, cls)
149 }
150 // TODO: To be consistent with the specifics of the visitor we are
151 // returning facts as `Rule`s with true body. While this is correct
152 // there is an easier way to import facts into RDFox. Are we able to
153 // do that?
154 val facts = unfold.map(x => Rule.create(RSA.In(x)))
155 val rules = List(
156 Rule.create(roleRf, atomA, RSA.notIn(varX)),
157 Rule.create(atomB, atomA, RSA.notIn(varX))
158 )
159 facts ++ rules
160 }
161
162 private def rules2(axiom: OWLSubClassOfAxiom): List[Rule] = {
163 val roleR =
164 axiom.getSuperClass
165 .asInstanceOf[OWLObjectSomeValuesFrom]
166 .getProperty
167 if (ontology.confl(roleR) contains roleR) {
168 // Fresh Variables
169 val v0 = RSA("v0_" ++ axiom.hashed)
170 val v1 = RSA("v1_" ++ axiom.hashed)
171 val v2 = RSA("v2_" ++ axiom.hashed)
172 // Predicates
173 def atomA(t: Term): TupleTableAtom = {
174 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
175 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
176 }
177 def roleRf(t1: Term, t2: Term): TupleTableAtom = {
178 val visitor =
179 new RDFoxPropertyExprConverter(t1, t2, Forward)
180 roleR.accept(visitor).head
181 }
182 def atomB(t: Term): TupleTableAtom = {
183 val cls = axiom.getSuperClass
184 .asInstanceOf[OWLObjectSomeValuesFrom]
185 .getFiller
186 .asInstanceOf[OWLClass]
187 .getIRI
188 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
189 }
190 //Rules
191 List(
192 Rule.create(roleRf(v0, v1), atomA(v0)),
193 Rule.create(atomB(v1), atomA(v0)),
194 Rule.create(roleRf(v1, v2), atomA(v1)),
195 Rule.create(atomB(v2), atomA(v1))
196 )
197 } else {
198 List()
199 }
200 }
201
202 private def rules3(axiom: OWLSubClassOfAxiom): List[Rule] = {
203 val cycle = ontology.cycle(axiom).toList
204 val roleR =
205 axiom.getSuperClass
206 .asInstanceOf[OWLObjectSomeValuesFrom]
207 .getProperty
208 // Fresh Variables
209 val v1 = RSA("v1_" ++ axiom.hashed)
210 // Predicates
211 def atomA(t: Term): TupleTableAtom = {
212 val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI
213 TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls)
214 }
215 def roleRf(t: Term): TupleTableAtom = {
216 val visitor =
217 new RDFoxPropertyExprConverter(t, v1, Forward)
218 roleR.accept(visitor).head
219 }
220 val atomB: TupleTableAtom = {
221 val cls = axiom.getSuperClass
222 .asInstanceOf[OWLObjectSomeValuesFrom]
223 .getFiller
224 .asInstanceOf[OWLClass]
225 .getIRI
226 TupleTableAtom.rdf(v1, IRI.RDF_TYPE, cls)
227 }
228 cycle.flatMap { x =>
229 List(
230 Rule.create(roleRf(x), atomA(x)),
231 Rule.create(atomB, atomA(x))
232 )
233 }
234 }
235
236 override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = {
237 if (axiom.isT5) {
238 // TODO: get role in T5 axiom
239 // Assuming one role here
240 val role = axiom.objectPropertyExpressionsInSignature(0)
241 if (ontology.unsafeRoles contains role) {
242 val visitor =
243 new RDFoxAxiomConverter(
244 Variable.create("X"),
245 ontology.unsafeRoles,
246 SkolemStrategy.Standard(axiom.toString),
247 Forward
248 )
249 axiom.accept(visitor)
250 } else {
251 rules1(axiom) ::: rules2(axiom) ::: rules3(axiom)
252 }
253 } else {
254 // Fallback to standard OWL to LP translation
255 super.visit(axiom)
256 }
257 }
258
259 override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = {
260 val varX = Variable.create("X")
261 val visitorF = new RDFoxAxiomConverter(
262 varX,
263 ontology.unsafeRoles,
264 SkolemStrategy.None,
265 Forward
266 )
267 val visitorB = new RDFoxAxiomConverter(
268 varX,
269 ontology.unsafeRoles,
270 SkolemStrategy.None,
271 Backward
272 )
273 axiom.accept(visitorB) ::: axiom.accept(visitorF)
274 }
275
276 }
277
278}
diff --git a/src/main/scala/rsacomb/FilteringProgram.scala b/src/main/scala/rsacomb/FilteringProgram.scala
deleted file mode 100644
index c82dcb6..0000000
--- a/src/main/scala/rsacomb/FilteringProgram.scala
+++ /dev/null
@@ -1,366 +0,0 @@
1package rsacomb
2
3import tech.oxfordsemantic.jrdfox.logic.Datatype
4import tech.oxfordsemantic.jrdfox.logic.expression.{
5 Term,
6 IRI,
7 Variable,
8 Literal,
9 FunctionCall
10}
11import tech.oxfordsemantic.jrdfox.logic.datalog.{
12 Rule,
13 TupleTableAtom,
14 BindAtom,
15 TupleTableName,
16 Atom,
17 BodyFormula,
18 Negation
19}
20import tech.oxfordsemantic.jrdfox.logic.sparql.statement.{SelectQuery}
21import tech.oxfordsemantic.jrdfox.logic.sparql.pattern.{
22 GroupGraphPattern,
23 ConjunctionPattern,
24 TriplePattern,
25 QueryPattern
26}
27
28import scala.collection.JavaConverters._
29
30import implicits.RSAAtom
31import suffix.{RSASuffix, Forward, Backward}
32import util.RSA
33
34class FilteringProgram(query: SelectQuery, constants: List[Term])
35 extends RSAAtom {
36
37 /* Makes mplicit conversion OWLAPI IRI <-> RDFox IRI available */
38 // import implicits.RDFox._
39
40 implicit val variables: (List[Term], List[Term]) = {
41 val all: Set[Variable] = query.getQueryBody.getWherePattern match {
42 case b: ConjunctionPattern => {
43 b.getConjuncts.asScala.toSet.flatMap { conj: QueryPattern =>
44 conj match {
45 case c: TriplePattern =>
46 Set(c.getSubject, c.getPredicate, c.getObject).collect {
47 case v: Variable => v
48 }
49 case _ => Set()
50 }
51 }
52 }
53 case _ => Set()
54 }
55 if (query.getAllPossibleVariables) {
56 (all.toList, List())
57 } else {
58 val answer = query.getSelection.asScala.map(_.getVariable).toSet
59 (answer.toList, (all &~ answer).toList)
60 }
61 }
62
63 val (answer, bounded) = variables
64
65 val facts: List[Rule] = constants.map(c => Rule.create(RSA.NI(c)))
66 val rules: List[Rule] =
67 this.generateFilteringProgram().map(reifyRule) ++ facts
68
69 /* NOTE: we are restricting to queries that contain conjunctions of
70 * atoms for the time being. This might need to be reviewed in the
71 * future.
72 */
73 private def queryToBody(body: GroupGraphPattern): List[TupleTableAtom] =
74 body match {
75 case b: ConjunctionPattern => {
76 val conjuncts = b.getConjuncts.asScala.toList
77 conjuncts flatMap { conj =>
78 conj match {
79 case c: TriplePattern =>
80 List(
81 TupleTableAtom.rdf(c.getSubject, c.getPredicate, c.getObject)
82 )
83 case _ => List()
84 }
85 }
86 }
87 case _ => List()
88 }
89
90 private def generateFilteringProgram(): List[Rule] = {
91 // General purpose variables
92 val varU = Variable.create("U")
93 val varV = Variable.create("V")
94 val varW = Variable.create("W")
95 // Query formula as a rule body
96 val body = queryToBody(query.getQueryBody.getWherePattern)
97 // Auxiliar predicates/helpers
98 def not(atom: TupleTableAtom): BodyFormula = Negation.create(atom)
99 // val predQM =
100 // TupleTableAtom.create(
101 // TupleTableName.create(RSA.Qm.getIRI),
102 // (answer ++ bounded): _*
103 // )
104 // def predID(t1: Term, t2: Term) =
105 // TupleTableAtom.create(
106 // TupleTableName.create(RSA.rsa("ID").getIRI),
107 // (answer ++ bounded).appended(t1).appended(t2): _*
108 // )
109 // def predNAMED(t1: Term): TupleTableAtom =
110 // TupleTableAtom.rdf(t1, IRI.RDF_TYPE, RSA.rsa("NAMED"))
111 // def predTQ(t1: Term, t2: Term, sx: RSASuffix) =
112 // TupleTableAtom.create(
113 // TupleTableName.create(RSA.rsa("TQ" :: sx).getIRI),
114 // (answer ++ bounded).appended(t1).appended(t2): _*
115 // )
116 // def predAQ(t1: Term, t2: Term, sx: RSASuffix) =
117 // TupleTableAtom.create(
118 // TupleTableName.create(RSA.rsa("AQ" :: sx).getIRI),
119 // (answer ++ bounded).appended(t1).appended(t2): _*
120 // )
121 // val predFK =
122 // TupleTableAtom.create(
123 // TupleTableName.create(RSA.rsa("FK").getIRI),
124 // (answer ++ bounded): _*
125 // )
126 // val predSP =
127 // TupleTableAtom.create(
128 // TupleTableName.create(RSA.rsa("SP").getIRI),
129 // (answer ++ bounded): _*
130 // )
131 // val predANS =
132 // TupleTableAtom.create(
133 // TupleTableName.create(RSA.rsa("ANS").getIRI),
134 // answer: _*
135 // )
136
137 /* Rule 1 */
138 val r1 = Rule.create(RSA.QM, body: _*)
139
140 /* Rules 3x */
141 val r3a =
142 for ((v, i) <- bounded.zipWithIndex)
143 yield Rule.create(RSA.ID(RSA(i), RSA(i)), RSA.QM, not(RSA.NI(v)))
144 val r3b = Rule.create(RSA.ID(varV, varU), RSA.ID(varU, varV))
145 val r3c =
146 Rule.create(RSA.ID(varU, varW), RSA.ID(varU, varV), RSA.ID(varV, varW))
147
148 /* Rules 4x */
149 val r4a = for {
150 role1 <- body.filter(_.isRoleAssertion)
151 if bounded contains (role1.getArguments.get(2))
152 role2 <- body.filter(_.isRoleAssertion)
153 if bounded contains (role2.getArguments.get(2))
154 } yield Rule.create(
155 RSA.FK,
156 role1 << Forward,
157 role2 << Forward,
158 RSA.ID(
159 RSA(bounded.indexOf(role1.getArguments.get(2))),
160 RSA(bounded.indexOf(role2.getArguments.get(2)))
161 ),
162 not(RSA.congruent(role1.getArguments.get(0), role2.getArguments.get(0)))
163 )
164 val r4b = for {
165 role1 <- body.filter(_.isRoleAssertion)
166 if bounded contains (role1.getArguments.get(2))
167 role2 <- body.filter(_.isRoleAssertion)
168 if bounded contains (role2.getArguments.get(0))
169 } yield Rule.create(
170 RSA.FK,
171 role1 << Forward,
172 role2 << Backward,
173 RSA.ID(
174 RSA(bounded.indexOf(role1.getArguments.get(2))),
175 RSA(bounded.indexOf(role2.getArguments.get(0)))
176 ),
177 not(RSA.congruent(role1.getArguments.get(0), role2.getArguments.get(2)))
178 )
179 val r4c = for {
180 role1 <- body.filter(_.isRoleAssertion)
181 if bounded contains (role1.getArguments.get(0))
182 role2 <- body.filter(_.isRoleAssertion)
183 if bounded contains (role2.getArguments.get(0))
184 } yield Rule.create(
185 RSA.FK,
186 role1 << Backward,
187 role2 << Backward,
188 RSA.ID(
189 RSA(bounded.indexOf(role1.getArguments.get(0))),
190 RSA(bounded.indexOf(role2.getArguments.get(0)))
191 ),
192 not(RSA.congruent(role1.getArguments.get(2), role2.getArguments.get(2)))
193 )
194
195 /* Rules 5x */
196 val r5a = for {
197 role1 <- body.filter(_.isRoleAssertion)
198 role1arg0 = role1.getArguments.get(0)
199 role1arg2 = role1.getArguments.get(2)
200 if bounded contains role1arg0
201 if bounded contains role1arg2
202 role2 <- body.filter(_.isRoleAssertion)
203 role2arg0 = role2.getArguments.get(0)
204 role2arg2 = role2.getArguments.get(2)
205 if bounded contains role2arg0
206 if bounded contains role2arg2
207 } yield Rule.create(
208 RSA.ID(
209 RSA(bounded indexOf role1arg0),
210 RSA(bounded indexOf role2arg0)
211 ),
212 role1 << Forward,
213 role2 << Forward,
214 RSA.ID(
215 RSA(bounded indexOf role1arg2),
216 RSA(bounded indexOf role2arg2)
217 ),
218 RSA.congruent(role1arg0, role2arg0),
219 not(RSA.NI(role1arg0))
220 )
221 val r5b = for {
222 role1 <- body.filter(_.isRoleAssertion)
223 role1arg0 = role1.getArguments.get(0)
224 role1arg2 = role1.getArguments.get(2)
225 if bounded contains role1arg0
226 if bounded contains role1arg2
227 role2 <- body.filter(_.isRoleAssertion)
228 role2arg0 = role2.getArguments.get(0)
229 role2arg2 = role2.getArguments.get(2)
230 if bounded contains role2arg0
231 if bounded contains role2arg2
232 } yield Rule.create(
233 RSA.ID(
234 RSA(bounded indexOf role1arg0),
235 RSA(bounded indexOf role2arg2)
236 ),
237 role1 << Forward,
238 role2 << Backward,
239 RSA.ID(
240 RSA(bounded indexOf role1arg2),
241 RSA(bounded indexOf role2arg0)
242 ),
243 RSA.congruent(role1arg0, role2arg2),
244 not(RSA.NI(role1arg0))
245 )
246 val r5c = for {
247 role1 <- body.filter(_.isRoleAssertion)
248 role1arg0 = role1.getArguments.get(0)
249 role1arg2 = role1.getArguments.get(2)
250 if bounded contains role1arg0
251 if bounded contains role1arg2
252 role2 <- body.filter(_.isRoleAssertion)
253 role2arg0 = role2.getArguments.get(0)
254 role2arg2 = role2.getArguments.get(2)
255 if bounded contains role2arg0
256 if bounded contains role2arg2
257 } yield Rule.create(
258 RSA.ID(
259 RSA(bounded indexOf role1arg2),
260 RSA(bounded indexOf role2arg2)
261 ),
262 role1 << Backward,
263 role2 << Backward,
264 RSA.ID(
265 RSA(bounded indexOf role1arg0),
266 RSA(bounded indexOf role2arg0)
267 ),
268 RSA.congruent(role1arg2, role2arg2),
269 not(RSA.NI(role1arg2))
270 )
271
272 /* Rules 6 */
273 val r6 = {
274 for {
275 role <- body.filter(_.isRoleAssertion)
276 arg0 = role.getArguments.get(0)
277 arg2 = role.getArguments.get(2)
278 if bounded contains arg0
279 if bounded contains arg2
280 suffix <- Seq(Forward, Backward)
281 } yield Rule.create(
282 RSA.AQ(varV, varW, suffix),
283 role << suffix,
284 RSA.ID(RSA(bounded indexOf arg0), varV),
285 RSA.ID(RSA(bounded indexOf arg2), varW)
286 )
287 }
288
289 /* Rules 7x */
290 val r7a = {
291 for (suffix <- List(Forward, Backward))
292 yield Rule.create(
293 RSA.TQ(varU, varV, suffix),
294 RSA.AQ(varU, varV, suffix)
295 )
296 }
297 val r7b = {
298 for (suffix <- List(Forward, Backward))
299 yield Rule.create(
300 RSA.TQ(varU, varW, suffix),
301 RSA.AQ(varU, varV, suffix),
302 RSA.TQ(varV, varW, suffix)
303 )
304 }
305
306 /* Rules 8x */
307 val r8a =
308 for (v <- answer) yield Rule.create(RSA.SP, RSA.QM, not(RSA.Named(v)))
309 val r8b =
310 Rule.create(RSA.SP, RSA.FK)
311 val r8c =
312 for (suffix <- List(Forward, Backward))
313 yield Rule.create(
314 RSA.SP,
315 RSA.TQ(varV, varV, suffix)
316 )
317
318 /* Rule 9 */
319 val r9 = Rule.create(RSA.Ans, RSA.QM, not(RSA.SP))
320
321 r1 ::
322 r3a ::: r3b :: r3c ::
323 r4c ::: r4b ::: r4a :::
324 r5c ::: r5b ::: r5a :::
325 r6 :::
326 r7b ::: r7a :::
327 r8a ::: r8b :: r8c :::
328 r9 ::
329 List()
330 }
331
332 private def reifyAtom(atom: Atom): (Option[BindAtom], List[Atom]) = {
333 atom match {
334 case atom: TupleTableAtom => atom.reified
335 case other => (None, List(other))
336 }
337 }
338
339 private def reifyBodyFormula(formula: BodyFormula): List[BodyFormula] = {
340 formula match {
341 case atom: TupleTableAtom => atom.reified._2
342 case neg: Negation => {
343 val (bs, as) = neg.getNegatedAtoms.asScala.toList.map(reifyAtom).unzip
344 val bind = bs.flatten.map(_.getBoundVariable).asJava
345 val atoms = as.flatten.asJava
346 List(Negation.create(bind, atoms))
347 }
348 case other => List(other)
349 }
350 }
351
352 private def reifyRule(rule: Rule): Rule = {
353 val (bs, hs) = rule.getHead.asScala.toList.map(_.reified).unzip
354 val head: List[TupleTableAtom] = hs.flatten
355 val bind: List[BodyFormula] = bs.flatten
356 val body: List[BodyFormula] =
357 rule.getBody.asScala.toList.map(reifyBodyFormula).flatten
358 Rule.create(head.asJava, (body ++ bind).asJava)
359 }
360
361} // class FilteringProgram
362
363object FilteringProgram {
364 def apply(query: SelectQuery, constants: List[Term]): FilteringProgram =
365 new FilteringProgram(query, constants)
366} // object FilteringProgram
diff --git a/src/main/scala/rsacomb/Main.scala b/src/main/scala/rsacomb/Main.scala
deleted file mode 100644
index 31dd5a0..0000000
--- a/src/main/scala/rsacomb/Main.scala
+++ /dev/null
@@ -1,249 +0,0 @@
1package rsacomb
2
3/* Java imports */
4import java.io.File
5import java.util.HashMap
6import scala.collection.JavaConverters._
7
8import tech.oxfordsemantic.jrdfox.client.UpdateType
9import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI, Term}
11
12/* Local imports */
13import util.{RDFoxHelpers, RSA}
14
15object RSAComb extends App {
16
17 val help: String = """
18 rsacomb - combined approach for CQ answering for RSA ontologies.
19
20 USAGE
21 rsacomb <path/to/ontology.owl> <path/to/query.sparql>
22
23 where
24 the ontology is expected to be an OWL file and the (single)
25 query a SPARQL query file.
26
27 """
28
29 /* Simple arguments handling
30 *
31 * TODO: use something better later on
32 */
33
34 if (args.length < 2) {
35 println(help)
36 sys.exit;
37 }
38
39 val ontoPath = new File(args(0))
40 val queryPath = new File(args(1))
41
42 if (!ontoPath.isFile || !queryPath.isFile) {
43 println("The provided arguments are not regular files.\n\n")
44 println(help)
45 sys.exit;
46 }
47
48 /* Create RSA object from generic OWLOntology
49 *
50 * TODO: It might be required to check if the ontology in input is
51 * Horn-ALCHOIQ. At the moment we are assuming this is always the
52 * case.
53 */
54
55 val ontology = RSAOntology(ontoPath)
56 if (ontology.isRSA) {
57
58 /* Load query */
59 val query = RDFoxHelpers.parseSelectQuery(
60 """
61 PREFIX : <http://example.com/rsa_example.owl#>
62
63 SELECT ?X
64 WHERE {
65 ?X a :D ;
66 :R ?Y .
67 ?Y :S ?Z .
68 ?Z a :D .
69 }
70 """
71 )
72
73 /* Compute answers to query */
74 query match {
75 case Some(query) => {
76
77 import implicits.JavaCollections._
78
79 // Open connection to RDFox
80 val (server, data) = RDFoxHelpers.openConnection("AnswerComputation")
81
82 {
83 println("\nQuery")
84 println(query)
85 }
86
87 // Step 1. Computing the canonical model
88 val canon = ontology.canonicalModel
89 data.addRules(canon.rules)
90
91 {
92 println("\nCanonical Model rules:")
93 canon.rules.foreach(println)
94 }
95
96 // Step 2. Computing the canonical model
97 val nis = {
98 val query = "SELECT ?Y WHERE { ?X rsa:EquivTo ?Y ; a rsa:Named . }"
99 RDFoxHelpers.submitSelectQuery(data, query, RSA.Prefixes).flatten
100 }
101 val filter = ontology.filteringProgram(query, nis)
102 data.addRules(filter.rules)
103
104 {
105 println("\nFiltering rules")
106 filter.rules.foreach(println)
107 }
108
109 // Retrieve answers
110 println("\nAnswers:")
111 val ans =
112 RDFoxHelpers.queryInternalPredicate(data, "Ans", filter.answer.length)
113 println(ans)
114
115 /* DEBUG: adding additional checks
116 */
117 {
118 import suffix.{Forward, Backward}
119
120 val arity = filter.answer.length + filter.bounded.length
121
122 println("\nIndividuals:")
123 ontology.individuals.foreach(println)
124
125 println("\nThings:")
126 val things = RDFoxHelpers.submitSelectQuery(
127 data,
128 """
129 PREFIX owl: <http://www.w3.org/2002/07/owl#>
130
131 SELECT ?X {
132 ?X a owl:Thing
133 }
134 """
135 )
136 println(things)
137
138 println("\nNAMEDs:")
139 val named = RDFoxHelpers.submitSelectQuery(
140 data,
141 """
142 SELECT ?X {
143 ?X a rsa:Named
144 }
145 """,
146 RSA.Prefixes
147 )
148 println(named)
149
150 println("\nNIs:")
151 val nis = RDFoxHelpers.submitSelectQuery(
152 data,
153 """
154 SELECT ?X {
155 ?X a rsa:NI
156 }
157 """,
158 RSA.Prefixes
159 )
160 println(nis)
161
162 // ID instances
163 println("\nIDs:")
164 val ids = RDFoxHelpers.queryInternalPredicate(
165 data,
166 "ID",
167 arity + 2
168 )
169 println(ids)
170
171 println("\nEquivTo:")
172 val equivs = RDFoxHelpers.submitSelectQuery(
173 data,
174 """
175 SELECT ?X ?Y {
176 ?X rsa:EquivTo ?Y
177 }
178 """,
179 RSA.Prefixes
180 )
181 println(equivs)
182
183 // Unfiltered answers
184 println("\nPossible answers:")
185 val qms = RDFoxHelpers.queryInternalPredicate(
186 data,
187 "QM",
188 arity
189 )
190 println(qms)
191
192 // Cycle detected
193 println("\nCycle detection:")
194 val aqf = RDFoxHelpers.queryInternalPredicate(
195 data,
196 "AQ" :: Forward,
197 arity + 2
198 )
199 val aqb = RDFoxHelpers.queryInternalPredicate(
200 data,
201 "AQ" :: Backward,
202 arity + 2
203 )
204 println(aqf)
205 println(aqb)
206
207 // Forks detected
208 println("\nForks:")
209 val fk = RDFoxHelpers.queryInternalPredicate(
210 data,
211 "FK",
212 arity
213 )
214 println(fk)
215
216 // Spurious answers
217 println("\nSpurious answers")
218 val sp = RDFoxHelpers.queryInternalPredicate(
219 data,
220 "SP",
221 arity
222 )
223 println(sp)
224 }
225
226 // Close connection to RDFox
227 RDFoxHelpers.closeConnection(server, data)
228 }
229 case None => {}
230 }
231 }
232}
233
234/* Notes:
235 *
236 * To establish a connection with a local RDFox instance, do the
237 * following:
238 *
239 * ```
240 * val serverConnection : ServerConnection = ConnectionFactory.newServerConnection("rdfox:local", "", "")
241 * serverConnection.createDataStore("test","seq",new HashMap())
242 * val dataStoreConnection : DataStoreConnection = serverConnection.newDataStoreConnection("test")
243 * dataStoreConnection.importData(
244 * UpdateType.ADDITION,
245 * Prefixes.s_emptyPrefixes,
246 * new File("./path/to/file")
247 * )
248 * ```
249 */
diff --git a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala b/src/main/scala/rsacomb/RDFoxAxiomConverter.scala
deleted file mode 100644
index 9b78e8e..0000000
--- a/src/main/scala/rsacomb/RDFoxAxiomConverter.scala
+++ /dev/null
@@ -1,106 +0,0 @@
1package rsacomb
2
3import org.semanticweb.owlapi.model.{
4 OWLAxiom,
5 OWLSubClassOfAxiom,
6 OWLEquivalentClassesAxiom,
7 OWLObjectPropertyExpression
8}
9import org.semanticweb.owlapi.model.OWLAxiomVisitorEx
10
11import tech.oxfordsemantic.jrdfox.logic.datalog.{
12 Rule,
13 BodyFormula,
14 TupleTableAtom,
15 TupleTableName
16}
17import tech.oxfordsemantic.jrdfox.logic.expression.{
18 Term,
19 IRI,
20 Variable,
21 Literal
22}
23
24import scala.collection.JavaConverters._
25
26import rsacomb.SkolemStrategy
27import rsacomb.RDFoxRuleShards
28import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom
29import org.semanticweb.owlapi.model.OWLObjectProperty
30import org.semanticweb.owlapi.model.OWLClassAssertionAxiom
31
32import suffix.{RSASuffix, Empty}
33
34object RDFoxAxiomConverter {
35
36 def apply(
37 term: Term,
38 unsafe: List[OWLObjectPropertyExpression],
39 skolem: SkolemStrategy = SkolemStrategy.None,
40 suffix: RSASuffix = Empty
41 ): RDFoxAxiomConverter =
42 new RDFoxAxiomConverter(term, unsafe, skolem, suffix)
43
44} // object RDFoxAxiomConverter
45
46class RDFoxAxiomConverter(
47 term: Term,
48 unsafe: List[OWLObjectPropertyExpression],
49 skolem: SkolemStrategy,
50 suffix: RSASuffix
51) extends OWLAxiomVisitorEx[List[Rule]] {
52
53 override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = {
54 // Skolemization is needed only for the head of an axiom
55 val subVisitor =
56 new RDFoxClassExprConverter(term, unsafe, SkolemStrategy.None, suffix)
57 val superVisitor = new RDFoxClassExprConverter(term, unsafe, skolem, suffix)
58 // Each visitor returns a `RDFoxRuleShards`, a tuple (res,ext):
59 // - the `res` List is a list of atoms resulting from the conversion
60 // of the axiom.
61 // - for some Class Expressions appearing in the head of an Axiom,
62 // the conversion might produce atoms that need to appear in the
63 // body (and not in the head) of the rule. This is what the `ext`
64 // List is for.
65 val sub = axiom.getSubClass.accept(subVisitor)
66 val sup = axiom.getSuperClass.accept(superVisitor)
67 val head = sup.res.asJava
68 val body = (sub.res ++ sup.ext).asJava
69 List(Rule.create(head, body))
70 }
71
72 override def visit(axiom: OWLEquivalentClassesAxiom): List[Rule] = {
73 for {
74 axiom1 <- axiom.asPairwiseAxioms.asScala.toList
75 axiom2 <- axiom1.asOWLSubClassOfAxioms.asScala.toList
76 rule <- axiom2.accept(this)
77 } yield rule
78 }
79
80 override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = {
81 val term1 = RSAOntology.genFreshVariable()
82 val subVisitor =
83 new RDFoxPropertyExprConverter(term, term1, suffix)
84 val superVisitor = new RDFoxPropertyExprConverter(term, term1, suffix)
85 val body: List[BodyFormula] = axiom.getSubProperty.accept(subVisitor)
86 val head: List[TupleTableAtom] = axiom.getSuperProperty.accept(superVisitor)
87 List(Rule.create(head.asJava, body.asJava))
88 }
89
90 override def visit(axiom: OWLClassAssertionAxiom): List[Rule] = {
91 val ind = axiom.getIndividual
92 if (ind.isNamed) {
93 val term = IRI.create(ind.asOWLNamedIndividual().getIRI.getIRIString)
94 val cls = axiom.getClassExpression
95 val visitor =
96 new RDFoxClassExprConverter(term, unsafe, SkolemStrategy.None, suffix)
97 val shard = cls.accept(visitor)
98 List(Rule.create(shard.res.asJava, shard.ext.asJava))
99 } else {
100 List()
101 }
102 }
103
104 def doDefault(axiom: OWLAxiom): List[Rule] = List()
105
106} // class RDFoxAxiomConverter
diff --git a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala b/src/main/scala/rsacomb/RDFoxClassExprConverter.scala
deleted file mode 100644
index f4187ed..0000000
--- a/src/main/scala/rsacomb/RDFoxClassExprConverter.scala
+++ /dev/null
@@ -1,159 +0,0 @@
1package rsacomb
2
3import scala.collection.JavaConverters._
4import java.util.stream.{Stream, Collectors}
5
6import org.semanticweb.owlapi.model.{
7 OWLClassExpression,
8 OWLClass,
9 OWLObjectSomeValuesFrom,
10 OWLObjectIntersectionOf,
11 OWLObjectOneOf,
12 OWLObjectMaxCardinality
13}
14import org.semanticweb.owlapi.model.OWLClassExpressionVisitorEx
15import tech.oxfordsemantic.jrdfox.logic.Datatype
16import tech.oxfordsemantic.jrdfox.logic.datalog.{
17 BindAtom,
18 TupleTableName,
19 TupleTableAtom
20}
21import tech.oxfordsemantic.jrdfox.logic.expression.{
22 Term,
23 Literal,
24 Variable,
25 FunctionCall,
26 IRI
27}
28
29import org.semanticweb.owlapi.model.OWLObjectPropertyExpression
30import org.semanticweb.owlapi.model.OWLObjectProperty
31
32import suffix.{RSASuffix, Empty}
33import util.RSA
34
35object RDFoxClassExprConverter {
36
37 def apply(
38 term: Term,
39 unsafe: List[OWLObjectPropertyExpression] = List(),
40 skolem: SkolemStrategy = SkolemStrategy.None,
41 suffix: RSASuffix = Empty
42 ): RDFoxClassExprConverter =
43 new RDFoxClassExprConverter(term, unsafe, skolem, suffix)
44
45 def merge(rules: List[RDFoxRuleShards]): RDFoxRuleShards = {
46 rules.foldLeft(RDFoxRuleShards(List(), List())) { (r1, r2) =>
47 RDFoxRuleShards(
48 r1.res ++ r2.res,
49 r1.ext ++ r2.ext
50 )
51 }
52 }
53
54} // object RDFoxClassExprConverter
55
56class RDFoxClassExprConverter(
57 term: Term,
58 unsafe: List[OWLObjectPropertyExpression],
59 skolem: SkolemStrategy,
60 suffix: RSASuffix
61) extends OWLClassExpressionVisitorEx[RDFoxRuleShards] {
62
63 import implicits.RDFox._
64
65 // OWLClass
66 override def visit(expr: OWLClass): RDFoxRuleShards = {
67 val iri: IRI = if (expr.isTopEntity()) IRI.THING else expr.getIRI()
68 val atom = List(TupleTableAtom.rdf(term, IRI.RDF_TYPE, iri))
69 RDFoxRuleShards(atom, List())
70 }
71
72 // OWLObjectIntersectionOf
73 override def visit(expr: OWLObjectIntersectionOf): RDFoxRuleShards = {
74 val visitor = new RDFoxClassExprConverter(term, unsafe, skolem, suffix)
75 // TODO: maybe using `flatMap` instead of `merge` + `map` works as well
76 RDFoxClassExprConverter.merge(
77 expr.asConjunctSet.asScala.toList
78 .map((e: OWLClassExpression) => e.accept(visitor))
79 )
80 }
81
82 // OWLObjectOneOf
83 override def visit(expr: OWLObjectOneOf): RDFoxRuleShards = {
84 val visitor = RDFoxClassExprConverter(term, unsafe, skolem, suffix)
85 // TODO: review nominal handling. Here we are taking "just" one
86 val ind = expr.individuals
87 .collect(Collectors.toList())
88 .asScala
89 .filter(_.isOWLNamedIndividual)
90 .head // restricts to proper "nominals"
91 .asOWLNamedIndividual
92 .getIRI
93 val atom = List(
94 TupleTableAtom.rdf(term, IRI.SAME_AS, ind)
95 )
96 RDFoxRuleShards(atom, List())
97 }
98
99 // OWLObjectSomeValuesFrom
100 override def visit(expr: OWLObjectSomeValuesFrom): RDFoxRuleShards = {
101 val y = RSAOntology.genFreshVariable()
102 // Here we are assuming a role name
103 val prop = expr.getProperty()
104 // Computes the result of rule skolemization. Depending on the used
105 // technique it might involve the introduction of additional atoms,
106 // and/or fresh constants and variables.
107 val (head, body, term1) = skolem match {
108 case SkolemStrategy.None => (List(), List(), y)
109 case SkolemStrategy.Constant(c) => (List(), List(), c)
110 case SkolemStrategy.ConstantRSA(c) => {
111 if (unsafe.contains(prop))
112 (List(RSA.PE(term, c), RSA.U(c)), List(), c)
113 else
114 (List(), List(), c)
115 }
116 case SkolemStrategy.Standard(f) => {
117 (
118 List(),
119 List(BindAtom.create(FunctionCall.create("SKOLEM", f, term), y)),
120 y
121 )
122 }
123 }
124 val classVisitor =
125 new RDFoxClassExprConverter(term1, unsafe, skolem, suffix)
126 val classResult = expr.getFiller.accept(classVisitor)
127 val propertyVisitor = new RDFoxPropertyExprConverter(term, term1, suffix)
128 val propertyResult = expr.getProperty.accept(propertyVisitor)
129 RDFoxRuleShards(
130 classResult.res ++ propertyResult ++ head,
131 classResult.ext ++ body
132 )
133 }
134
135 // OWLObjectMaxCardinality
136 override def visit(expr: OWLObjectMaxCardinality): RDFoxRuleShards = {
137 // TODO: again, no hardcoded variables
138 val vars =
139 List(RSAOntology.genFreshVariable(), RSAOntology.genFreshVariable())
140 val classResult = RDFoxClassExprConverter.merge(
141 vars
142 .map(new RDFoxClassExprConverter(_, unsafe, skolem, suffix))
143 .map(expr.getFiller.accept(_))
144 )
145 val propertyResult =
146 vars
147 .map(new RDFoxPropertyExprConverter(term, _, suffix))
148 .map(expr.getProperty.accept(_))
149 .flatten
150 RDFoxRuleShards(
151 List(TupleTableAtom.rdf(vars(0), IRI.SAME_AS, vars(1))),
152 classResult.res ++ propertyResult
153 )
154 }
155
156 def doDefault(expr: OWLClassExpression): RDFoxRuleShards =
157 RDFoxRuleShards(List(), List())
158
159} // class RDFoxClassExprConverter
diff --git a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala b/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala
deleted file mode 100644
index 826e965..0000000
--- a/src/main/scala/rsacomb/RDFoxPropertyExprConverter.scala
+++ /dev/null
@@ -1,35 +0,0 @@
1package rsacomb
2
3import org.semanticweb.owlapi.model.{OWLPropertyExpression, OWLObjectProperty}
4import org.semanticweb.owlapi.model.OWLPropertyExpressionVisitorEx
5
6import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom
7import tech.oxfordsemantic.jrdfox.logic.expression.{Term, IRI, Literal}
8
9import org.semanticweb.owlapi.model.OWLObjectInverseOf
10
11import suffix.{RSASuffix, Inverse}
12
13class RDFoxPropertyExprConverter(
14 term1: Term,
15 term2: Term,
16 suffix: RSASuffix
17) extends OWLPropertyExpressionVisitorEx[List[TupleTableAtom]] {
18
19 // Automatically converts OWLAPI types into RDFox equivalent types.
20 import implicits.RDFox._
21
22 override def visit(expr: OWLObjectProperty): List[TupleTableAtom] = {
23 val base = expr.getIRI.getIRIString
24 val pred = IRI.create(base :: suffix)
25 List(TupleTableAtom.rdf(term1, pred, term2))
26 }
27
28 override def visit(expr: OWLObjectInverseOf): List[TupleTableAtom] = {
29 val visitor = new RDFoxPropertyExprConverter(term1, term2, suffix + Inverse)
30 expr.getInverse.accept(visitor)
31 }
32
33 def doDefault(expr: OWLPropertyExpression): List[TupleTableAtom] = List()
34
35} // class RDFoxPropertyExprConverter
diff --git a/src/main/scala/rsacomb/RDFoxRuleShards.scala b/src/main/scala/rsacomb/RDFoxRuleShards.scala
deleted file mode 100644
index b61cc38..0000000
--- a/src/main/scala/rsacomb/RDFoxRuleShards.scala
+++ /dev/null
@@ -1,5 +0,0 @@
1package rsacomb
2
3import tech.oxfordsemantic.jrdfox.logic.datalog.{TupleTableAtom, BodyFormula}
4
5case class RDFoxRuleShards(res: List[TupleTableAtom], ext: List[BodyFormula])
diff --git a/src/main/scala/rsacomb/RSA.scala b/src/main/scala/rsacomb/RSA.scala
deleted file mode 100644
index ab4f539..0000000
--- a/src/main/scala/rsacomb/RSA.scala
+++ /dev/null
@@ -1,100 +0,0 @@
1package rsacomb.util
2
3/* Java imports */
4import java.util.Map
5
6import tech.oxfordsemantic.jrdfox.formats.SPARQLParser
7import tech.oxfordsemantic.jrdfox.Prefixes
8import tech.oxfordsemantic.jrdfox.logic.datalog.{
9 TupleTableAtom,
10 TupleTableName,
11 Negation
12}
13import tech.oxfordsemantic.jrdfox.logic.expression.{Term, Variable, IRI}
14import org.semanticweb.owlapi.model.OWLOntology
15import org.semanticweb.owlapi.model.{
16 OWLAxiom,
17 OWLClass,
18 OWLObjectPropertyExpression
19}
20
21import rsacomb.suffix.RSASuffix
22
23// Debug only
24import scala.collection.JavaConverters._
25
26object RSA {
27
28 val Prefixes: Prefixes = new Prefixes()
29 Prefixes.declarePrefix("rsa:", "http://www.cs.ox.ac.uk/isg/rsa/")
30
31 private def atom(name: IRI, vars: List[Term]) =
32 TupleTableAtom.create(TupleTableName.create(name.getIRI), vars: _*)
33
34 def PE(t1: Term, t2: Term) =
35 TupleTableAtom.rdf(t1, RSA("PE"), t2)
36
37 def U(t: Term) =
38 TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("U"))
39
40 def In(t: Term)(implicit set: Term) =
41 TupleTableAtom.rdf(t, RSA("In"), set)
42
43 def notIn(t: Term)(implicit set: Term) = Negation.create(In(t)(set))
44
45 def congruent(t1: Term, t2: Term) =
46 TupleTableAtom.rdf(t1, RSA("congruent"), t2)
47
48 def QM(implicit variables: (List[Term], List[Term])) = {
49 val (answer, bounded) = variables
50 atom(RSA("QM"), answer ::: bounded)
51 }
52
53 def ID(t1: Term, t2: Term)(implicit variables: (List[Term], List[Term])) = {
54 val (answer, bounded) = variables
55 atom(RSA("ID"), (answer ::: bounded) :+ t1 :+ t2)
56 }
57
58 def Named(t: Term) =
59 TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("Named"))
60
61 def Thing(t: Term) =
62 TupleTableAtom.rdf(t, IRI.RDF_TYPE, IRI.THING)
63
64 def NI(t: Term) =
65 TupleTableAtom.rdf(t, IRI.RDF_TYPE, RSA("NI"))
66
67 def TQ(t1: Term, t2: Term, sx: RSASuffix)(implicit
68 variables: (List[Term], List[Term])
69 ) = {
70 val (answer, bounded) = variables
71 atom(RSA("TQ" :: sx), (answer ::: bounded) :+ t1 :+ t2)
72 }
73
74 def AQ(t1: Term, t2: Term, sx: RSASuffix)(implicit
75 variables: (List[Term], List[Term])
76 ) = {
77 val (answer, bounded) = variables
78 atom(RSA("AQ" :: sx), (answer ::: bounded) :+ t1 :+ t2)
79 }
80
81 def FK(implicit variables: (List[Term], List[Term])) = {
82 val (answer, bounded) = variables
83 atom(RSA("FK"), answer ::: bounded)
84 }
85
86 def SP(implicit variables: (List[Term], List[Term])) = {
87 val (answer, bounded) = variables
88 atom(RSA("SP"), answer ::: bounded)
89 }
90
91 def Ans(implicit variables: (List[Term], List[Term])) = {
92 val (answer, _) = variables
93 atom(RSA("Ans"), answer)
94 }
95
96 def apply(name: Any): IRI =
97 IRI.create(
98 Prefixes.getPrefixIRIsByPrefixName.get("rsa:").getIRI + name.toString
99 )
100}
diff --git a/src/main/scala/rsacomb/RSAAtom.scala b/src/main/scala/rsacomb/RSAAtom.scala
deleted file mode 100644
index 8832226..0000000
--- a/src/main/scala/rsacomb/RSAAtom.scala
+++ /dev/null
@@ -1,88 +0,0 @@
1package rsacomb.implicits
2
3import tech.oxfordsemantic.jrdfox.logic.Datatype
4import tech.oxfordsemantic.jrdfox.logic.expression.{Literal, FunctionCall}
5import tech.oxfordsemantic.jrdfox.logic.datalog.{
6 BindAtom,
7 TupleTableAtom,
8 TupleTableName
9}
10import tech.oxfordsemantic.jrdfox.logic.expression.{IRI}
11import scala.collection.JavaConverters._
12
13import rsacomb.suffix.{RSASuffix, Nth}
14import rsacomb.RSAOntology
15
16/* Is this the best way to determine if an atom is an RDF triple?
17 * Note that we can't use `getNumberOfArguments()` because is not
18 * "consistent":
19 * - for an atom created with `rdf(<term1>, <term2>, <term3>)`,
20 * `getNumberOfArguments` returns 3
21 * - for an atom created with `Atom.create(<tupletablename>, <term1>,
22 * <term2>, <term3>)`, `getNumberOfArguments()` returns 3
23 *
24 * This is probably because `Atom.rdf(...) is implemented as:
25 * ```scala
26 * def rdf(term1: Term, term2: Term, term3: Term): Atom =
27 * Atom.create(TupleTableName.create("internal:triple"), term1, term2, term3)
28 * ```
29 */
30
31trait RSAAtom {
32
33 implicit class RSAAtom(val atom: TupleTableAtom) {
34
35 import RDFox._
36
37 val name: String = atom.getTupleTableName.getName
38
39 val isRDF: Boolean = name == "internal:triple"
40
41 val isClassAssertion: Boolean = {
42 isRDF && {
43 val pred = atom.getArguments.get(1)
44 pred == IRI.RDF_TYPE
45 }
46 }
47
48 val isRoleAssertion: Boolean = isRDF && !isClassAssertion
49
50 def <<(suffix: RSASuffix): TupleTableAtom =
51 if (isRDF) {
52 val subj = atom.getArguments.get(0)
53 val pred = atom.getArguments.get(1)
54 val obj = atom.getArguments.get(2)
55 if (isClassAssertion) {
56 val obj1 = obj match {
57 case iri: IRI => IRI.create(iri.getIRI :: suffix)
58 case other => other
59 }
60 TupleTableAtom.rdf(subj, pred, obj1)
61 } else {
62 val pred1 = pred match {
63 case iri: IRI => IRI.create(iri.getIRI :: suffix)
64 case other => other
65 }
66 TupleTableAtom.rdf(subj, pred1, obj)
67 }
68 } else {
69 val ttname = TupleTableName.create(name :: suffix)
70 TupleTableAtom.create(ttname, atom.getArguments())
71 }
72
73 lazy val reified: (Option[BindAtom], List[TupleTableAtom]) =
74 if (isRDF) {
75 (None, List(atom))
76 } else {
77 val bvar = RSAOntology.genFreshVariable()
78 val str = Literal.create(name, Datatype.XSD_STRING)
79 val args = atom.getArguments.asScala.toList
80 val skolem = FunctionCall.create("SKOLEM", str :: args: _*)
81 val bind = BindAtom.create(skolem, bvar)
82 val atoms = args.zipWithIndex
83 .map { case (t, i) => TupleTableAtom.rdf(bvar, name :: Nth(i), t) }
84 (Some(bind), atoms)
85 }
86 }
87
88}
diff --git a/src/main/scala/rsacomb/RSAAxiom.scala b/src/main/scala/rsacomb/RSAAxiom.scala
deleted file mode 100644
index 08de5b7..0000000
--- a/src/main/scala/rsacomb/RSAAxiom.scala
+++ /dev/null
@@ -1,155 +0,0 @@
1package rsacomb
2
3/* Java imports */
4import org.semanticweb.owlapi.model.{
5 OWLAxiom,
6 OWLSubClassOfAxiom,
7 OWLEquivalentClassesAxiom
8}
9import org.semanticweb.owlapi.model.{
10 OWLObjectPropertyExpression,
11 OWLSubObjectPropertyOfAxiom,
12 OWLClass,
13 OWLClassExpression,
14 OWLObjectSomeValuesFrom,
15 OWLObjectMaxCardinality
16}
17import org.semanticweb.owlapi.model.ClassExpressionType
18import org.semanticweb.owlapi.model.{
19 OWLAxiomVisitorEx,
20 OWLClassExpressionVisitorEx
21}
22import org.semanticweb.owlapi.model.OWLObjectProperty
23import scala.collection.JavaConverters._
24
25/* Wrapper trait for the implicit class `RSAAxiom`.
26 */
27trait RSAAxiom {
28
29 /* Identifies some of the axiom types in a Horn-ALCHOIQ ontology
30 * in normal form. Refer to the paper for more details on the
31 * chosen names.
32 */
33 private sealed trait RSAAxiomType
34 private object RSAAxiomType {
35 case object T3 extends RSAAxiomType // ∃R.A ⊑ B
36 case object T3top extends RSAAxiomType // ∃R.⊤ ⊑ B
37 case object T4 extends RSAAxiomType // A ⊑ ≤1R.B
38 case object T5 extends RSAAxiomType // A ⊑ ∃R.B
39 }
40
41 object RSAAxiom {
42
43 def hashed(
44 cls1: OWLClass,
45 prop: OWLObjectPropertyExpression,
46 cls2: OWLClass
47 ): String =
48 (cls1, prop, cls2).hashCode.toString
49
50 }
51
52 /* Implements additional features on top of `OWLAxiom` from
53 * the OWLAPI.
54 */
55 implicit class RSAAxiom(axiom: OWLAxiom) {
56
57 /* Detecting axiom types:
58 *
59 * In order to reason about role unsafety in Horn-ALCHOIQ
60 * ontologies we need to detect and filter axioms by their
61 * "type".
62 *
63 * This is a simple implementation following the Visitor
64 * pattern imposed by the OWLAPI.
65 */
66 private class RSAAxiomTypeDetector(t: RSAAxiomType)
67 extends OWLAxiomVisitorEx[Boolean] {
68 override def visit(axiom: OWLSubClassOfAxiom): Boolean = {
69 val sub = axiom.getSubClass().getClassExpressionType()
70 val sup = axiom.getSuperClass().getClassExpressionType()
71 t match {
72 case RSAAxiomType.T3top => // ∃R.⊤ ⊑ B
73 axiom.isT3 && axiom
74 .getSubClass()
75 .asInstanceOf[OWLObjectSomeValuesFrom]
76 .getFiller
77 .isOWLThing
78 case RSAAxiomType.T3 => // ∃R.A ⊑ B
79 sub == ClassExpressionType.OBJECT_SOME_VALUES_FROM && sup == ClassExpressionType.OWL_CLASS
80 case RSAAxiomType.T4 => // A ⊑ ≤1R.B
81 sub == ClassExpressionType.OWL_CLASS && sup == ClassExpressionType.OBJECT_MAX_CARDINALITY
82 case RSAAxiomType.T5 => // A ⊑ ∃R.B
83 sub == ClassExpressionType.OWL_CLASS && sup == ClassExpressionType.OBJECT_SOME_VALUES_FROM
84 }
85 }
86
87 override def visit(axiom: OWLEquivalentClassesAxiom): Boolean = {
88 // TODO
89 false
90 }
91
92 def doDefault(axiom: OWLAxiom): Boolean = false
93 }
94
95 private def isOfType(t: RSAAxiomType): Boolean = {
96 val visitor = new RSAAxiomTypeDetector(t)
97 axiom.accept(visitor)
98 }
99
100 /* Exposed methods */
101 def isT3top: Boolean = isOfType(RSAAxiomType.T3top)
102 def isT3: Boolean = isOfType(RSAAxiomType.T3)
103 def isT4: Boolean = isOfType(RSAAxiomType.T4)
104 def isT5: Boolean = isOfType(RSAAxiomType.T5)
105
106 /* Extracting ObjectPropertyExpressions from axioms
107 *
108 * This extracts all ObjectPropertyExpressions from a given
109 * axiom. While the implementation is generic we use it on axioms
110 * of specific types (see above).
111 *
112 * NOTE: it is not possible to use the `objectPropertyInSignature`
113 * method of `OWLAxiom` because it returns all "role names" involved
114 * in the signature of an axiom. In particular we won't get the inverse
115 * of a role if this appears in the axiom (but we will get the role
116 * itself instead).
117 */
118 lazy val objectPropertyExpressionsInSignature
119 : List[OWLObjectPropertyExpression] =
120 axiom match {
121 case a: OWLSubClassOfAxiom =>
122 rolesInExpr(a.getSubClass) ++ rolesInExpr(a.getSuperClass)
123 case a: OWLEquivalentClassesAxiom =>
124 a.getClassExpressions.asScala.toList.flatMap(rolesInExpr(_))
125 case a: OWLSubObjectPropertyOfAxiom =>
126 List(a.getSubProperty, a.getSuperProperty)
127 case _ => List()
128 }
129
130 private def rolesInExpr(
131 expr: OWLClassExpression
132 ): List[OWLObjectPropertyExpression] =
133 expr match {
134 case e: OWLObjectSomeValuesFrom => List(e.getProperty)
135 case e: OWLObjectMaxCardinality => List(e.getProperty)
136 case _ => List()
137 }
138
139 lazy val toTriple: Option[(OWLClass, OWLObjectProperty, OWLClass)] =
140 for {
141 subClass <- Some(axiom) collect { case a: OWLSubClassOfAxiom => a }
142 cls1 <- Some(subClass.getSubClass) collect { case a: OWLClass => a }
143 someValues <- Some(subClass.getSuperClass) collect {
144 case a: OWLObjectSomeValuesFrom => a
145 }
146 prop <- Some(someValues.getProperty) collect {
147 case a: OWLObjectProperty => a
148 }
149 cls2 <- Some(someValues.getFiller) collect { case a: OWLClass => a }
150 } yield (cls1, prop, cls2)
151
152 lazy val hashed: String = (RSAAxiom.hashed _) tupled toTriple.get
153 }
154
155} // trait RSAAxiom
diff --git a/src/main/scala/rsacomb/RSAOntology.scala b/src/main/scala/rsacomb/RSAOntology.scala
deleted file mode 100644
index 53bc560..0000000
--- a/src/main/scala/rsacomb/RSAOntology.scala
+++ /dev/null
@@ -1,378 +0,0 @@
1package rsacomb
2
3/* Java imports */
4import java.util.HashMap
5import java.util.stream.{Collectors, Stream}
6import java.io.File
7import org.semanticweb.owlapi.apibinding.OWLManager
8
9import org.semanticweb.owlapi.model.{OWLOntology, OWLAxiom}
10import org.semanticweb.owlapi.model.{
11 OWLClass,
12 OWLObjectProperty,
13 OWLSubObjectPropertyOfAxiom,
14 OWLObjectPropertyExpression,
15 OWLObjectSomeValuesFrom,
16 OWLSubClassOfAxiom
17}
18import org.semanticweb.owlapi.model.parameters.Imports
19import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory
20import org.semanticweb.owlapi.model.{IRI => OWLIRI}
21import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl
22
23import tech.oxfordsemantic.jrdfox.client.{UpdateType, DataStoreConnection}
24import tech.oxfordsemantic.jrdfox.logic.datalog.{
25 Rule,
26 TupleTableAtom,
27 Negation,
28 BodyFormula
29}
30import tech.oxfordsemantic.jrdfox.logic.expression.{
31 Term,
32 Variable,
33 IRI,
34 Resource
35}
36import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
37
38/* Scala imports */
39import scala.collection.JavaConverters._
40import scala.collection.mutable.Set
41import scalax.collection.immutable.Graph
42import scalax.collection.GraphEdge.UnDiEdge
43
44/* Debug only */
45import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer
46import tech.oxfordsemantic.jrdfox.logic._
47import org.semanticweb.owlapi.model.OWLObjectInverseOf
48
49import suffix.{Empty, Forward, Backward, Inverse}
50import util.{RDFoxHelpers, RSA}
51
52object RSAOntology {
53
54 // Counter used to implement a simple fresh variable generator
55 private var counter = -1;
56
57 def apply(ontology: OWLOntology): RSAOntology = new RSAOntology(ontology)
58
59 def apply(ontology: File): RSAOntology =
60 new RSAOntology(loadOntology(ontology))
61
62 def genFreshVariable(): Variable = {
63 counter += 1
64 Variable.create(f"I$counter%03d")
65 }
66
67 private def loadOntology(onto: File): OWLOntology = {
68 val manager = OWLManager.createOWLOntologyManager()
69 manager.loadOntologyFromOntologyDocument(onto)
70 }
71}
72
73class RSAOntology(val ontology: OWLOntology) extends RSAAxiom {
74
75 // Gather TBox/RBox/ABox from original ontology
76 val tbox: List[OWLAxiom] =
77 ontology
78 .tboxAxioms(Imports.INCLUDED)
79 .collect(Collectors.toList())
80 .asScala
81 .toList
82
83 val rbox: List[OWLAxiom] =
84 ontology
85 .rboxAxioms(Imports.INCLUDED)
86 .collect(Collectors.toList())
87 .asScala
88 .toList
89
90 val abox: List[OWLAxiom] =
91 ontology
92 .aboxAxioms(Imports.INCLUDED)
93 .collect(Collectors.toList())
94 .asScala
95 .toList
96
97 val axioms: List[OWLAxiom] = abox ::: tbox ::: rbox
98
99 /* Retrieve individuals in the original ontology
100 */
101 val individuals: List[IRI] =
102 ontology
103 .getIndividualsInSignature()
104 .asScala
105 .map(_.getIRI)
106 .map(implicits.RDFox.owlapiToRdfoxIri)
107 .toList
108
109 val concepts: List[OWLClass] =
110 ontology.getClassesInSignature().asScala.toList
111
112 val roles: List[OWLObjectPropertyExpression] =
113 axioms
114 .flatMap(_.objectPropertyExpressionsInSignature)
115 .distinct
116
117 // OWLAPI reasoner for same easier tasks
118 private val reasoner =
119 (new StructuralReasonerFactory()).createReasoner(ontology)
120
121 /* Steps for RSA check
122 * 1) convert ontology axioms into LP rules
123 * 2) call RDFox on the onto and compute materialization
124 * 3) build graph from E(x,y) facts
125 * 4) check if the graph is tree-like
126 * ideally this annotates the graph with info about the reasons
127 * why the ontology might not be RSA. This could help a second
128 * step of approximation of an Horn-ALCHOIQ to RSA
129 */
130 lazy val isRSA: Boolean = {
131
132 val unsafe = this.unsafeRoles
133
134 /* DEBUG: print rules in DL syntax and unsafe roles */
135 //val renderer = new DLSyntaxObjectRenderer()
136 //println("\nDL rules:")
137 //axioms.foreach(x => println(renderer.render(x)))
138 //println("\nUnsafe roles:")
139 //println(unsafe)
140
141 /* Ontology convertion into LP rules */
142 val datalog = for {
143 axiom <- axioms
144 visitor = new RDFoxAxiomConverter(
145 RSAOntology.genFreshVariable(),
146 unsafe,
147 SkolemStrategy.ConstantRSA(axiom.toString),
148 Empty
149 )
150 rule <- axiom.accept(visitor)
151 } yield rule
152
153 /* DEBUG: print datalog rules */
154 //println("\nDatalog roles:")
155 //datalog.foreach(println)
156
157 // Open connection with RDFox
158 val (server, data) = RDFoxHelpers.openConnection("RSACheck")
159 // Add Data (hardcoded for now)
160 //data.importData(UpdateType.ADDITION, RSA.Prefixes, ":a a :A .")
161
162 /* Add built-in rules
163 */
164 data.importData(
165 UpdateType.ADDITION,
166 RSA.Prefixes,
167 "rsa:E[?X,?Y] :- rsa:PE[?X,?Y], rsa:U[?X], rsa:U[?Y] ."
168 )
169
170 /* Add built-in rules
171 */
172 // data.importData(
173 // UpdateType.ADDITION,
174 // RSA.Prefixes,
175 // "[?entity, a, ?superClass] :- [?entity, a, ?class], [?class, rdfs:subClassOf, ?superClass] ."
176 // )
177
178 /* Add ontology rules
179 */
180 data.addRules(datalog.asJava)
181
182 /* Build graph
183 */
184 val graph = this.rsaGraph(data);
185 //println(graph)
186
187 // Close connection to RDFox
188 RDFoxHelpers.closeConnection(server, data)
189
190 /* To check if the graph is tree-like we check for acyclicity in a
191 * undirected graph.
192 *
193 * TODO: Implement additional checks (taking into account equality)
194 */
195 graph.isAcyclic
196 }
197
198 lazy val unsafeRoles: List[OWLObjectPropertyExpression] = {
199
200 /* DEBUG: print rules in DL syntax */
201 //val renderer = new DLSyntaxObjectRenderer()
202
203 /* Checking for (1) unsafety condition:
204 *
205 * For all roles r1 appearing in an axiom of type T5, r1 is unsafe
206 * if there exists a role r2 (different from top) appearing in an axiom
207 * of type T3 and r1 is a subproperty of the inverse of r2.
208 */
209 val unsafe1 = for {
210 axiom <- tbox
211 if axiom.isT5
212 role1 <- axiom.objectPropertyExpressionsInSignature
213 roleSuper =
214 role1 +: reasoner
215 .superObjectProperties(role1)
216 .collect(Collectors.toList())
217 .asScala
218 roleSuperInv = roleSuper.map(_.getInverseProperty)
219 axiom <- tbox
220 if axiom.isT3 && !axiom.isT3top
221 role2 <- axiom.objectPropertyExpressionsInSignature
222 if roleSuperInv.contains(role2)
223 } yield role1
224
225 /* Checking for (2) unsafety condition:
226 *
227 * For all roles p1 appearing in an axiom of type T5, p1 is unsafe if
228 * there exists a role p2 appearing in an axiom of type T4 and p1 is a
229 * subproperty of either p2 or the inverse of p2.
230 *
231 */
232 val unsafe2 = for {
233 axiom <- tbox
234 if axiom.isT5
235 role1 <- axiom.objectPropertyExpressionsInSignature
236 roleSuper =
237 role1 +: reasoner
238 .superObjectProperties(role1)
239 .collect(Collectors.toList())
240 .asScala
241 roleSuperInv = roleSuper.map(_.getInverseProperty)
242 axiom <- tbox
243 if axiom.isT4
244 role2 <- axiom.objectPropertyExpressionsInSignature
245 if roleSuper.contains(role2) || roleSuperInv.contains(role2)
246 } yield role1
247
248 (unsafe1 ++ unsafe2).toList
249 }
250
251 private def rsaGraph(
252 data: DataStoreConnection
253 ): Graph[Resource, UnDiEdge] = {
254 val query = "SELECT ?X ?Y WHERE { ?X rsa:E ?Y }"
255 val answers = RDFoxHelpers.submitSelectQuery(data, query, RSA.Prefixes)
256 var edges: List[UnDiEdge[Resource]] = answers.map {
257 case n1 :: n2 :: _ => UnDiEdge(n1, n2)
258 }
259 Graph(edges: _*)
260 }
261
262 def filteringProgram(
263 query: SelectQuery,
264 nis: List[Term]
265 ): FilteringProgram =
266 new FilteringProgram(query, nis)
267
268 lazy val canonicalModel = new CanonicalModel(this)
269
270 // TODO: the following functions needs testing
271 def confl(
272 role: OWLObjectPropertyExpression
273 ): Set[OWLObjectPropertyExpression] = {
274
275 val invSuperRoles = reasoner
276 .superObjectProperties(role)
277 .collect(Collectors.toSet())
278 .asScala
279 .addOne(role)
280 .map(_.getInverseProperty)
281
282 invSuperRoles
283 .flatMap(x =>
284 reasoner
285 .subObjectProperties(x)
286 .collect(Collectors.toSet())
287 .asScala
288 .addOne(x)
289 )
290 .filterNot(_.isOWLBottomObjectProperty())
291 .filterNot(_.getInverseProperty.isOWLTopObjectProperty())
292 }
293
294 def self(axiom: OWLSubClassOfAxiom): Set[Term] = {
295 // Assuming just one role in the signature of a T5 axiom
296 val role = axiom.objectPropertyExpressionsInSignature(0)
297 if (this.confl(role).contains(role)) {
298 Set(
299 RSA("v0_" ++ axiom.hashed),
300 RSA("v1_" ++ axiom.hashed)
301 )
302 } else {
303 Set()
304 }
305 }
306
307 // def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = {
308 // // Assuming just one role in the signature of a T5 axiom
309 // val roleR = axiom.objectPropertyExpressionsInSignature(0)
310 // val conflR = this.confl(roleR)
311 // // We just need the TBox to find
312 // val tbox = ontology
313 // .tboxAxioms(Imports.INCLUDED)
314 // .collect(Collectors.toSet())
315 // .asScala
316 // for {
317 // axiom1 <- tbox
318 // // TODO: is this an optimization or an error?
319 // if axiom1.isT5
320 // // We expect only one role coming out of a T5 axiom
321 // roleS <- axiom1.objectPropertyExpressionsInSignature
322 // // Triples ordering is among triples involving safe roles.
323 // if !unsafeRoles.contains(roleS)
324 // if conflR.contains(roleS)
325 // individual =
326 // if (axiom.hashCode < axiom1.hashCode) {
327 // RSA.rsa("v0_" ++ axiom1.hashCode.toString())
328 // } else {
329 // RSA.rsa("v1_" ++ axiom1.hashCode.toString())
330 // }
331 // } yield individual
332 // }
333
334 def cycle(axiom: OWLSubClassOfAxiom): Set[Term] = {
335 // TODO: we can actually use `toTriple` from `RSAAxiom`
336 val classes =
337 axiom.classesInSignature.collect(Collectors.toList()).asScala
338 val classA = classes(0)
339 val roleR = axiom
340 .objectPropertyExpressionsInSignature(0)
341 .asInstanceOf[OWLObjectProperty]
342 val classB = classes(1)
343 cycle_aux(classA, roleR, classB)
344 }
345
346 def cycle_aux(
347 classA: OWLClass,
348 roleR: OWLObjectProperty,
349 classB: OWLClass
350 ): Set[Term] = {
351 val conflR = this.confl(roleR)
352 val classes = ontology
353 .classesInSignature(Imports.INCLUDED)
354 .collect(Collectors.toSet())
355 .asScala
356 for {
357 classD <- classes
358 roleS <- conflR
359 classC <- classes
360 // Keeping this check for now
361 if !unsafeRoles.contains(roleS)
362 tripleARB = RSAAxiom.hashed(classA, roleR, classB)
363 tripleDSC = RSAAxiom.hashed(classD, roleS, classC)
364 individual =
365 if (tripleARB > tripleDSC) {
366 RSA("v1_" ++ tripleDSC)
367 } else {
368 // Note that this is also the case for
369 // `tripleARB == tripleDSC`
370 RSA("v0_" ++ tripleDSC)
371 }
372 } yield individual
373 }
374
375 def unfold(axiom: OWLSubClassOfAxiom): Set[Term] =
376 this.self(axiom) | this.cycle(axiom)
377
378} // implicit class RSAOntology
diff --git a/src/main/scala/rsacomb/RSASuffix.scala b/src/main/scala/rsacomb/RSASuffix.scala
deleted file mode 100644
index ce36b10..0000000
--- a/src/main/scala/rsacomb/RSASuffix.scala
+++ /dev/null
@@ -1,31 +0,0 @@
1package rsacomb.suffix
2
3import org.semanticweb.owlapi.model.{
4 OWLPropertyExpression,
5 OWLObjectInverseOf,
6 OWLObjectProperty
7}
8
9import tech.oxfordsemantic.jrdfox.logic.expression.{IRI}
10import tech.oxfordsemantic.jrdfox.logic.datalog.{TupleTableAtom, TupleTableName}
11
12object RSASuffix {
13
14 def apply(suffix: String => String): RSASuffix = new RSASuffix(suffix)
15
16}
17
18class RSASuffix(val suffix: String => String) {
19
20 def +(that: RSASuffix): RSASuffix =
21 new RSASuffix(this.suffix andThen that.suffix)
22
23 def ::(str: String): String = this suffix str
24
25}
26
27case object Empty extends RSASuffix(identity)
28case object Forward extends RSASuffix((s) => s"${s}_f")
29case object Backward extends RSASuffix((s) => s"${s}_b")
30case object Inverse extends RSASuffix((s) => s"${s}_inv")
31case class Nth(n: Int) extends RSASuffix((s) => s"${s}_$n")
diff --git a/src/main/scala/rsacomb/SkolemStrategy.scala b/src/main/scala/rsacomb/SkolemStrategy.scala
deleted file mode 100644
index a2ad0a1..0000000
--- a/src/main/scala/rsacomb/SkolemStrategy.scala
+++ /dev/null
@@ -1,78 +0,0 @@
1package rsacomb
2
3import tech.oxfordsemantic.jrdfox.logic.Datatype
4import tech.oxfordsemantic.jrdfox.logic.expression.{Literal, IRI}
5
6sealed trait SkolemStrategy
7
8object SkolemStrategy {
9 // TODO: might want to use something else other than `hashCode` as a
10 // function to generate a fresh function/constant
11
12 /* No skolemization at all.
13 *
14 * From
15 * ∃R.A ⊑ B
16 * to
17 * R(x,y), B(y) -> B(x)
18 */
19 case object None extends SkolemStrategy
20
21 /* Functional skolemization
22 *
23 * From
24 * A ⊑ ∃R.B
25 * to
26 * A(x) -> R(x,f(x)), B(f(x))
27 * for f, fresh function associated with the input axiom
28 *
29 * In RDFox this can represented combining the BIND operator with the
30 * SKOLEM operator as such:
31 * A(x), BIND(y, SKOLEM("f", x)) -> R(x,y), B(y)
32 * The first argument of a SKOLEM call is a literal string (ideally
33 * identifing the simulated function name).
34 *
35 * NOTE: this requirement for the SKOLEM operator is not enforced by
36 * RDFox, that will fail silently if omitted.
37 */
38 case class Standard(func: Literal) extends SkolemStrategy
39 object Standard {
40 def apply(axiom: String) =
41 new Standard(
42 Literal.create(genFunctionString(axiom), Datatype.XSD_STRING)
43 )
44 def genFunctionString(str: String) = "f_" ++ str.hashCode.toString
45 }
46
47 /* Constant skolemization
48 *
49 * From
50 * A ⊑ ∃R.B
51 * to
52 * A(y) -> R(x,c), B(c)
53 * for c, fresh constant associated with the input axiom
54 */
55 case class Constant(const: IRI) extends SkolemStrategy
56 object Constant {
57 def apply(axiom: String) =
58 new Constant(IRI.create(genConstantString(axiom)))
59 def genConstantString(str: String) = "c_" ++ str.hashCode.toString
60 }
61
62 /* (RSA) Constant skolemization
63 * This is a special skolemization option to introduce additional atoms for RSA
64 * checking algorithm.
65 *
66 * From
67 * A ⊑ ∃R.B
68 * to
69 * A(y) -> R(x,c), PE(x,c), B(c)
70 * for c, fresh constant associated with the input axiom and PE an internal predicate.
71 */
72 case class ConstantRSA(const: IRI) extends SkolemStrategy
73 object ConstantRSA {
74 def apply(axiom: String) =
75 new ConstantRSA(IRI.create(genConstantString(axiom)))
76 def genConstantString(str: String) = "c_" ++ str.hashCode.toString
77 }
78}
diff --git a/src/main/scala/rsacomb/implicits/JavaCollections.scala b/src/main/scala/rsacomb/implicits/JavaCollections.scala
deleted file mode 100644
index 69e825b..0000000
--- a/src/main/scala/rsacomb/implicits/JavaCollections.scala
+++ /dev/null
@@ -1,13 +0,0 @@
1package rsacomb.implicits
2
3import scala.collection.JavaConverters._
4
5object JavaCollections {
6
7 implicit def javaToScalaList[A](list: java.util.List[A]): List[A] =
8 list.asScala.toList
9
10 implicit def scalaToJavaList[A](list: List[A]): java.util.List[A] =
11 list.asJava
12
13}
diff --git a/src/main/scala/rsacomb/implicits/RDFox.scala b/src/main/scala/rsacomb/implicits/RDFox.scala
deleted file mode 100644
index 44b7c01..0000000
--- a/src/main/scala/rsacomb/implicits/RDFox.scala
+++ /dev/null
@@ -1,20 +0,0 @@
1package rsacomb.implicits
2
3import tech.oxfordsemantic.jrdfox.logic.expression.{IRI => RDFoxIRI}
4import org.semanticweb.owlapi.model.{IRI => OWLIRI}
5
6object RDFox {
7
8 implicit def rdfoxToOwlapiIri(iri: RDFoxIRI): OWLIRI = {
9 OWLIRI.create(iri.getIRI)
10 }
11
12 implicit def owlapiToRdfoxIri(iri: OWLIRI): RDFoxIRI = {
13 RDFoxIRI.create(iri.getIRIString())
14 }
15
16 implicit def stringToRdfoxIri(iri: String): RDFoxIRI = {
17 RDFoxIRI.create(iri)
18 }
19
20}
diff --git a/src/main/scala/rsacomb/util/RDFoxHelpers.scala b/src/main/scala/rsacomb/util/RDFoxHelpers.scala
deleted file mode 100644
index 9856e27..0000000
--- a/src/main/scala/rsacomb/util/RDFoxHelpers.scala
+++ /dev/null
@@ -1,100 +0,0 @@
1package rsacomb.util
2
3import java.util.{Map => JMap, HashMap => JHashMap}
4import java.io.StringReader
5import tech.oxfordsemantic.jrdfox.Prefixes
6import tech.oxfordsemantic.jrdfox.client.{
7 ConnectionFactory,
8 ServerConnection,
9 DataStoreConnection
10}
11import tech.oxfordsemantic.jrdfox.formats.SPARQLParser
12import tech.oxfordsemantic.jrdfox.logic.expression.Resource
13import tech.oxfordsemantic.jrdfox.logic.sparql.statement.SelectQuery
14
15import rsacomb.suffix.Nth
16
17object RDFoxHelpers {
18
19 def openConnection(
20 dataStore: String,
21 opts: JMap[String, String] = new JHashMap[String, String]()
22 ): (ServerConnection, DataStoreConnection) = {
23 /* Create local server connection
24 */
25 val serverUrl = "rdfox:local"
26 val role = ""
27 val password = ""
28 val server =
29 ConnectionFactory.newServerConnection(serverUrl, role, password)
30
31 /* Create datastore connection
32 */
33 // parameters.put("owl-in-rdf-support", "relaxed")
34 // parameters.put("equality", "noUNA")
35 server.createDataStore(dataStore, "par-complex-nn", opts)
36 val data = server.newDataStoreConnection(dataStore)
37
38 (server, data)
39 }
40
41 def parseSelectQuery(
42 query: String,
43 prefixes: Prefixes = new Prefixes()
44 ): Option[SelectQuery] = {
45 val parser = new SPARQLParser(
46 prefixes,
47 new StringReader(query)
48 )
49 parser.parseSingleQuery() match {
50 case q: SelectQuery => Some(q)
51 case _ => None
52 }
53 }
54
55 def submitSelectQuery(
56 data: DataStoreConnection,
57 query: String,
58 prefixes: Prefixes = new Prefixes(),
59 opts: JMap[String, String] = new JHashMap[String, String]()
60 ): List[List[Resource]] = {
61 val cursor = data.createCursor(prefixes, query, opts)
62 var answers: List[List[Resource]] = List()
63 var mul = cursor.open()
64 while (mul > 0) {
65 val answer =
66 (0 until cursor.getArity).map(cursor.getResource(_)).toList
67 answers = answer :: answers
68 mul = cursor.advance()
69 }
70 cursor.close();
71 answers
72 }
73
74 def queryInternalPredicate(
75 data: DataStoreConnection,
76 pred: String,
77 arity: Int,
78 opts: JMap[String, String] = new JHashMap[String, String]()
79 ): List[List[Resource]] = {
80 var query = "SELECT"
81 for (i <- 0 until arity) {
82 query ++= s" ?X$i"
83 }
84 query ++= " WHERE {"
85 for (i <- 0 until arity) {
86 query ++= s" ?S rsa:${pred :: Nth(i)} ?X$i ."
87 }
88 query ++= " }"
89 submitSelectQuery(data, query, RSA.Prefixes, opts)
90 }
91
92 def closeConnection(
93 server: ServerConnection,
94 data: DataStoreConnection
95 ): Unit = {
96 server.close();
97 data.close();
98 }
99
100}