diff options
Diffstat (limited to 'src/main/scala/rsacomb/CanonicalModel.scala')
-rw-r--r-- | src/main/scala/rsacomb/CanonicalModel.scala | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/main/scala/rsacomb/CanonicalModel.scala b/src/main/scala/rsacomb/CanonicalModel.scala new file mode 100644 index 0000000..f7a45a7 --- /dev/null +++ b/src/main/scala/rsacomb/CanonicalModel.scala | |||
@@ -0,0 +1,292 @@ | |||
1 | package rsacomb | ||
2 | |||
3 | import org.semanticweb.owlapi.model.{OWLObjectInverseOf, OWLObjectProperty} | ||
4 | import org.semanticweb.owlapi.model.{ | ||
5 | OWLClass, | ||
6 | // OWLObjectProperty, | ||
7 | OWLSubObjectPropertyOfAxiom, | ||
8 | // OWLObjectPropertyExpression, | ||
9 | OWLObjectSomeValuesFrom, | ||
10 | OWLSubClassOfAxiom | ||
11 | } | ||
12 | |||
13 | import tech.oxfordsemantic.jrdfox.logic.datalog.{ | ||
14 | Rule, | ||
15 | BodyFormula, | ||
16 | TupleTableAtom, | ||
17 | Negation | ||
18 | } | ||
19 | import tech.oxfordsemantic.jrdfox.logic.expression.{ | ||
20 | Term, | ||
21 | Variable, | ||
22 | // Resource, | ||
23 | IRI | ||
24 | } | ||
25 | |||
26 | import suffix.{Empty, Forward, Backward, Inverse} | ||
27 | |||
28 | class CanonicalModel(val ontology: RSAOntology) extends RSAAxiom { | ||
29 | |||
30 | // Makes working with IRIs less painful | ||
31 | import RDFoxUtil._ | ||
32 | |||
33 | val named: List[Rule] = | ||
34 | ontology.individuals.map(a => | ||
35 | Rule.create(TupleTableAtom.rdf(a, IRI.RDF_TYPE, RSA.Named)) | ||
36 | ) | ||
37 | |||
38 | val rolesAdditionalRules: List[Rule] = { | ||
39 | // Given a role (predicate) compute additional logic rules | ||
40 | def additional(pred: String): Seq[Rule] = { | ||
41 | val varX = Variable.create("X") | ||
42 | val varY = Variable.create("Y") | ||
43 | for ( | ||
44 | (hSuffix, bSuffix) <- List( | ||
45 | (Empty, Forward), | ||
46 | (Empty, Backward), | ||
47 | (Inverse, Forward + Inverse), | ||
48 | (Inverse, Backward + Inverse), | ||
49 | (Backward + Inverse, Forward), | ||
50 | (Forward + Inverse, Backward), | ||
51 | (Backward, Forward + Inverse), | ||
52 | (Forward, Backward + Inverse) | ||
53 | ) | ||
54 | ) | ||
55 | yield Rule.create( | ||
56 | TupleTableAtom.rdf(varX, pred :: hSuffix, varY), | ||
57 | TupleTableAtom.rdf(varX, pred :: bSuffix, varY) | ||
58 | ) | ||
59 | } | ||
60 | // Compute additional rules per role | ||
61 | ontology.roles | ||
62 | .collect { case prop: OWLObjectProperty => prop } | ||
63 | .map(_.getIRI.getIRIString) | ||
64 | .flatMap(additional) | ||
65 | } | ||
66 | |||
67 | private lazy val topAxioms: List[Rule] = { | ||
68 | val varX = Variable.create("X") | ||
69 | val varY = Variable.create("Y") | ||
70 | val concepts = ontology.concepts.map(c => { | ||
71 | Rule.create( | ||
72 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, IRI.THING), | ||
73 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, c.getIRI) | ||
74 | ) | ||
75 | }) | ||
76 | val roles = ontology.roles.map(r => { | ||
77 | val name = r match { | ||
78 | case x: OWLObjectProperty => x.getIRI.getIRIString | ||
79 | case x: OWLObjectInverseOf => | ||
80 | x.getInverse.getNamedProperty.getIRI.getIRIString :: Inverse | ||
81 | } | ||
82 | Rule.create( | ||
83 | List( | ||
84 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, IRI.THING), | ||
85 | TupleTableAtom.rdf(varY, IRI.RDF_TYPE, IRI.THING) | ||
86 | ), | ||
87 | List(TupleTableAtom.rdf(varX, name, varY)) | ||
88 | ) | ||
89 | }) | ||
90 | concepts ::: roles | ||
91 | } | ||
92 | |||
93 | private val equalityAxioms: List[Rule] = { | ||
94 | val varX = Variable.create("X") | ||
95 | val varY = Variable.create("Y") | ||
96 | val varZ = Variable.create("Z") | ||
97 | List( | ||
98 | Rule.create( | ||
99 | TupleTableAtom.rdf(varX, RSA.EquivTo, varX), | ||
100 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, IRI.THING) | ||
101 | ), | ||
102 | Rule.create( | ||
103 | TupleTableAtom.rdf(varY, RSA.EquivTo, varX), | ||
104 | TupleTableAtom.rdf(varX, RSA.EquivTo, varY) | ||
105 | ), | ||
106 | Rule.create( | ||
107 | TupleTableAtom.rdf(varX, RSA.EquivTo, varZ), | ||
108 | TupleTableAtom.rdf(varX, RSA.EquivTo, varY), | ||
109 | TupleTableAtom.rdf(varY, RSA.EquivTo, varZ) | ||
110 | ) | ||
111 | ) | ||
112 | } | ||
113 | |||
114 | val rules: List[Rule] = { | ||
115 | // Compute rules from ontology axioms | ||
116 | val rules = ontology.axioms.flatMap(_.accept(RuleGenerator)) | ||
117 | // Return full set of rules | ||
118 | rules ::: rolesAdditionalRules ::: topAxioms ::: equalityAxioms ::: named | ||
119 | } | ||
120 | |||
121 | object RuleGenerator | ||
122 | extends RDFoxAxiomConverter( | ||
123 | Variable.create("X"), | ||
124 | ontology.unsafeRoles, | ||
125 | SkolemStrategy.None, | ||
126 | Empty | ||
127 | ) { | ||
128 | |||
129 | private def rules1(axiom: OWLSubClassOfAxiom): List[Rule] = { | ||
130 | val unfold = ontology.unfold(axiom).toList | ||
131 | // Fresh Variables | ||
132 | val v0 = RSA.rsa("v0_" ++ RSA.hashed(axiom)) | ||
133 | val varX = Variable.create("X") | ||
134 | // Predicates | ||
135 | val atomA: TupleTableAtom = { | ||
136 | val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI | ||
137 | TupleTableAtom.rdf(varX, IRI.RDF_TYPE, cls) | ||
138 | } | ||
139 | def predIN(t: Term): TupleTableAtom = { | ||
140 | TupleTableAtom.rdf( | ||
141 | t, | ||
142 | RSA.rsa("IN"), | ||
143 | RSA.rsa(unfold.hashCode.toString) | ||
144 | ) | ||
145 | } | ||
146 | def notIn(t: Term): Negation = Negation.create(predIN(t)) | ||
147 | val roleRf: TupleTableAtom = { | ||
148 | val visitor = | ||
149 | new RDFoxPropertyExprConverter(varX, v0, Forward) | ||
150 | axiom.getSuperClass | ||
151 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
152 | .getProperty | ||
153 | .accept(visitor) | ||
154 | .head | ||
155 | } | ||
156 | val atomB: TupleTableAtom = { | ||
157 | val cls = axiom.getSuperClass | ||
158 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
159 | .getFiller | ||
160 | .asInstanceOf[OWLClass] | ||
161 | .getIRI | ||
162 | TupleTableAtom.rdf(v0, IRI.RDF_TYPE, cls) | ||
163 | } | ||
164 | // TODO: To be consistent with the specifics of the visitor we are | ||
165 | // returning facts as `Rule`s with true body. While this is correct | ||
166 | // there is an easier way to import facts into RDFox. Are we able to | ||
167 | // do that? | ||
168 | val facts = unfold.map(x => Rule.create(predIN(x))) | ||
169 | val rules = List( | ||
170 | Rule.create(roleRf, atomA, notIn(varX)), | ||
171 | Rule.create(atomB, atomA, notIn(varX)) | ||
172 | ) | ||
173 | facts ++ rules | ||
174 | } | ||
175 | |||
176 | private def rules2(axiom: OWLSubClassOfAxiom): List[Rule] = { | ||
177 | val roleR = | ||
178 | axiom.getSuperClass | ||
179 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
180 | .getProperty | ||
181 | if (ontology.confl(roleR) contains roleR) { | ||
182 | // Fresh Variables | ||
183 | val v0 = RSA.rsa("v0_" ++ RSA.hashed(axiom)) | ||
184 | val v1 = RSA.rsa("v1_" ++ RSA.hashed(axiom)) | ||
185 | val v2 = RSA.rsa("v2_" ++ RSA.hashed(axiom)) | ||
186 | // Predicates | ||
187 | def atomA(t: Term): TupleTableAtom = { | ||
188 | val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI | ||
189 | TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls) | ||
190 | } | ||
191 | def roleRf(t1: Term, t2: Term): TupleTableAtom = { | ||
192 | val visitor = | ||
193 | new RDFoxPropertyExprConverter(t1, t2, Forward) | ||
194 | roleR.accept(visitor).head | ||
195 | } | ||
196 | def atomB(t: Term): TupleTableAtom = { | ||
197 | val cls = axiom.getSuperClass | ||
198 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
199 | .getFiller | ||
200 | .asInstanceOf[OWLClass] | ||
201 | .getIRI | ||
202 | TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls) | ||
203 | } | ||
204 | //Rules | ||
205 | List( | ||
206 | Rule.create(roleRf(v0, v1), atomA(v0)), | ||
207 | Rule.create(atomB(v1), atomA(v0)), | ||
208 | Rule.create(roleRf(v1, v2), atomA(v1)), | ||
209 | Rule.create(atomB(v2), atomA(v1)) | ||
210 | ) | ||
211 | } else { | ||
212 | List() | ||
213 | } | ||
214 | } | ||
215 | |||
216 | private def rules3(axiom: OWLSubClassOfAxiom): List[Rule] = { | ||
217 | val cycle = ontology.cycle(axiom).toList | ||
218 | val roleR = | ||
219 | axiom.getSuperClass | ||
220 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
221 | .getProperty | ||
222 | // Fresh Variables | ||
223 | val v1 = RSA.rsa("v1_" ++ RSA.hashed(axiom)) | ||
224 | // Predicates | ||
225 | def atomA(t: Term): TupleTableAtom = { | ||
226 | val cls = axiom.getSubClass.asInstanceOf[OWLClass].getIRI | ||
227 | TupleTableAtom.rdf(t, IRI.RDF_TYPE, cls) | ||
228 | } | ||
229 | def roleRf(t: Term): TupleTableAtom = { | ||
230 | val visitor = | ||
231 | new RDFoxPropertyExprConverter(t, v1, Forward) | ||
232 | roleR.accept(visitor).head | ||
233 | } | ||
234 | val atomB: TupleTableAtom = { | ||
235 | val cls = axiom.getSuperClass | ||
236 | .asInstanceOf[OWLObjectSomeValuesFrom] | ||
237 | .getFiller | ||
238 | .asInstanceOf[OWLClass] | ||
239 | .getIRI | ||
240 | TupleTableAtom.rdf(v1, IRI.RDF_TYPE, cls) | ||
241 | } | ||
242 | cycle.flatMap { x => | ||
243 | List( | ||
244 | Rule.create(roleRf(x), atomA(x)), | ||
245 | Rule.create(atomB, atomA(x)) | ||
246 | ) | ||
247 | } | ||
248 | } | ||
249 | |||
250 | override def visit(axiom: OWLSubClassOfAxiom): List[Rule] = { | ||
251 | if (axiom.isT5) { | ||
252 | // TODO: get role in T5 axiom | ||
253 | // Assuming one role here | ||
254 | val role = axiom.objectPropertyExpressionsInSignature(0) | ||
255 | if (ontology.unsafeRoles contains role) { | ||
256 | val visitor = | ||
257 | new RDFoxAxiomConverter( | ||
258 | Variable.create("X"), | ||
259 | ontology.unsafeRoles, | ||
260 | SkolemStrategy.Standard(axiom.toString), | ||
261 | Forward | ||
262 | ) | ||
263 | axiom.accept(visitor) | ||
264 | } else { | ||
265 | rules1(axiom) ::: rules2(axiom) ::: rules3(axiom) | ||
266 | } | ||
267 | } else { | ||
268 | // Fallback to standard OWL to LP translation | ||
269 | super.visit(axiom) | ||
270 | } | ||
271 | } | ||
272 | |||
273 | override def visit(axiom: OWLSubObjectPropertyOfAxiom): List[Rule] = { | ||
274 | val varX = Variable.create("X") | ||
275 | val visitorF = new RDFoxAxiomConverter( | ||
276 | varX, | ||
277 | ontology.unsafeRoles, | ||
278 | SkolemStrategy.None, | ||
279 | Forward | ||
280 | ) | ||
281 | val visitorB = new RDFoxAxiomConverter( | ||
282 | varX, | ||
283 | ontology.unsafeRoles, | ||
284 | SkolemStrategy.None, | ||
285 | Backward | ||
286 | ) | ||
287 | axiom.accept(visitorB) ::: axiom.accept(visitorF) | ||
288 | } | ||
289 | |||
290 | } | ||
291 | |||
292 | } | ||