| Home | Trees | Indices | Help |
|---|
|
|
1 #!/d/Bin/Python/python.exe
2 # -*- coding: utf-8 -*-
3 #
4 """
5 This module is a C{brute force} implementation of the OWL 2 RL profile.
6
7 RDFLib works with 'generalized' RDF, meaning that triples with a BNode predicate are I{allowed}. This is good because, eg, some of the
8 triples generated for RDF from an OWL 2 functional syntax might look like '[ owl:inverseOf p]', and the RL rules would then operate
9 on such generalized triple. However, as a last, post processing steps, these triples are removed from the graph before serialization
10 to produce 'standard' RDF (which is o.k. for RL, too, because the consequent triples are all right, generalized triples might
11 have had a role in the deduction steps only).
12
13 @requires: U{RDFLib<https://github.com/RDFLib/rdflib>}, 4.0.0 and higher
14 @license: This software is available for use under the U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>}
15 @organization: U{World Wide Web Consortium<http://www.w3.org>}
16 @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">}
17
18 """
19
20 __author__ = 'Ivan Herman'
21 __contact__ = 'Ivan Herman, ivan@w3.org'
22 __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231'
23
24 import rdflib
25 from rdflib import BNode
26
27 from RDFClosure.RDFS import Property, type
28 from RDFClosure.RDFS import subClassOf, subPropertyOf, comment, label, domain, range
29 from RDFClosure.RDFS import seeAlso, isDefinedBy, Datatype
30
31 from RDFClosure.OWL import *
32 from RDFClosure.Closure import Core
33 from RDFClosure.AxiomaticTriples import OWLRL_Axiomatic_Triples, OWLRL_D_Axiomatic_Triples
34 from RDFClosure.AxiomaticTriples import OWLRL_Datatypes_Disjointness
35
36 OWLRL_Annotation_properties = [label, comment, seeAlso, isDefinedBy, deprecated, versionInfo, priorVersion, backwardCompatibleWith, incompatibleWith]
37
38 from .XsdDatatypes import OWL_RL_Datatypes, OWL_Datatype_Subsumptions
39
40 ###########################################################################################################################
41
42
43 ## OWL-R Semantics class
44 #
45 #
46 # As an editing help: each rule is prefixed by RULE XXXX where XXXX is the acronym given in the profile document.
47 # This helps in referring back to the spec...
48 # noinspection PyPep8Naming,PyPep8Naming,PyBroadException
50 """OWL 2 RL Semantics class, ie, implementation of the OWL 2 RL closure graph.
51
52 Note that the module does I{not} implement the so called Datatype entailment rules, simply because the underlying RDFLib does
53 not implement the datatypes (ie, RDFLib will not make the literal "1.00" and "1.00000" identical, although
54 even with all the ambiguities on datatypes, this I{should} be made equal...). Also, the so-called extensional entailment rules
55 (Section 7.3.1 in the RDF Semantics document) have not been implemented either.
56
57 The comments and references to the various rule follow the names as used in the U{OWL 2 RL document<http://www.w3.org/TR/owl2-profiles/#Reasoning_in_OWL_2_RL_and_RDF_Graphs_using_Rules>}.
58 """
60 """
61 @param graph: the RDF graph to be extended
62 @type graph: rdflib.Graph
63 @param axioms: whether (non-datatype) axiomatic triples should be added or not
64 @type axioms: bool
65 @param daxioms: whether datatype axiomatic triples should be added or not
66 @type daxioms: bool
67 @param rdfs: whether RDFS inference is also done (used in subclassed only)
68 @type rdfs: boolean
69 """
70 Core.__init__(self, graph, axioms, daxioms, rdfs)
71 self.bnodes = []
72
74 """
75 Shorthand to get a list of values (ie, from an rdf:List structure) starting at a head
76
77 @param l: RDFLib resource, should be the head of an rdf:List
78 @return: array of resources
79 """
80 return [ch for ch in self.graph.items(l)]
81
83 if node in self.literal_proxies.bnode_to_lit:
84 return "'" + self.literal_proxies.bnode_to_lit[node].lex + "'"
85 else :
86 return node
87
89 """
90 Remove triples with bnode predicates. The Bnodes in the graph are collected in the first cycle run.
91 """
92 to_be_removed = []
93 for b in self.bnodes:
94 for t in self.graph.triples((None, b, None)):
95 if t not in to_be_removed:
96 to_be_removed.append(t)
97 for t in to_be_removed:
98 self.graph.remove(t)
99
106
108 """
109 Add the datatype axioms
110 """
111 for t in OWLRL_D_Axiomatic_Triples:
112 self.graph.add(t)
113
115 """Helping method to check whether a type statement is in line with a possible
116 restriction. This method is invoked by rule "cls-avf" before setting a type
117 on an allValuesFrom restriction.
118
119 The method is a placeholder at this level. It is typically implemented by subclasses for
120 extra checks, eg, for datatype facet checks.
121 @param v: the resource that is to be 'typed'
122 @param t: the targeted type (ie, Class)
123 @return: boolean
124 """
125 return True
126
128 """
129 Some of the rules in the rule set are axiomatic in nature, meaning that they really have to be added only
130 once, there is no reason to add these in a cycle. These are performed by this method that is invoked only once
131 at the beginning of the process.
132
133 These are: cls-thing, cls-nothing1, prp-ap, dt-types1, dt-types2, dt-eq, dt-diff.
134
135 Note, however, that the dt-* are executed only partially, limited by the possibilities offered by RDFLib. These may not
136 cover all the edge cases of OWL RL. Especially, dt-not-type has not (yet?) been implemented (I wonder whether RDFLib should not raise
137 exception for those anyway...
138 """
139 # noinspection PyShadowingNames
140 def _add_to_explicit(s, o):
141 if s not in explicit:
142 explicit[s] = []
143 if o not in explicit[s]:
144 explicit[s].append(o)
145
146 # noinspection PyShadowingNames
147 def _append_to_explicit(s, o):
148 if s not in explicit:
149 explicit[s] = []
150 for d in explicit[o]:
151 if d not in explicit[s]:
152 explicit[s].append(d)
153
154 def _add_to_used_datatypes(d):
155 used_datatypes.add(d)
156
157 # noinspection PyShadowingNames
158 def _handle_subsumptions(r, dt):
159 if dt in OWL_Datatype_Subsumptions:
160 for new_dt in OWL_Datatype_Subsumptions[dt]:
161 self.store_triple((r, type, new_dt))
162 self.store_triple((new_dt, type, Datatype))
163 _add_to_used_datatypes(new_dt)
164
165
166 # For processing later:
167 # implicit object->datatype relationships: these come from real literals which are represented by
168 # an internal bnode
169 implicit = {}
170
171 # explicit object->datatype relationships: those that came from an object being typed as a datatype
172 # or a sameAs. The values are arrays of datatypes to which the resource belong
173 explicit = {}
174
175 # datatypes in use by the graph (directly or indirectly). This will be used at the end to add the
176 # necessary disjointness statements (but not more
177 used_datatypes = set()
178
179 # the real literals from the original graph:
180 # literals = self.literal_proxies.lit_to_bnode.keys()
181
182 # RULE dt-type2: for all explicit literals the corresponding bnode should get the right type
183 # definition. The 'implicit' dictionary is also filled on the fly
184 # RULE dt-not-type: see whether an explicit literal is valid in terms of the defined datatype
185 for lt in self.literal_proxies.lit_to_bnode.keys():
186 # note that all non-RL datatypes are ignored
187 if lt.dt is not None and lt.dt in OWL_RL_Datatypes:
188 bn = self.literal_proxies.lit_to_bnode[lt]
189 # add the explicit typing triple
190 self.store_triple((bn, type, lt.dt))
191 if bn not in implicit:
192 implicit[bn] = lt.dt
193 _add_to_used_datatypes(lt.dt)
194
195 # for dt-not-type
196 # This is a dirty trick: rdflib's Literal includes a method that raises an exception if the
197 # lexical value cannot be mapped on the value space.
198 try :
199 val = lt.lit.toPython()
200 except:
201 self.add_error("Literal's lexical value and datatype do not match: (%s,%s)" % (lt.lex, lt.dt))
202
203 # RULE dt-diff
204 # RULE dt-eq
205 # Try to compare literals whether they are different or not. If they are different, then an explicit
206 # different from statement should be added, if they are identical, then an equality should be added
207 for lt1 in self.literal_proxies.lit_to_bnode.keys():
208 for lt2 in self.literal_proxies.lit_to_bnode.keys() :
209 if lt1 != lt2:
210 try :
211 lt1_d = lt1.lit.toPython()
212 lt2_d = lt2.lit.toPython()
213 #if lt1_d != lt2_d :
214 # self.store_triple((self.literal_proxies.lit_to_bnode[lt1], differentFrom, self.literal_proxies.lit_to_bnode[lt2]))
215 #else :
216 # self.store_triple((self.literal_proxies.lit_to_bnode[lt1], sameAs, self.literal_proxies.lit_to_bnode[lt2]))
217 except:
218 # there may be a problem with one of the python conversion, but that should have been taken
219 # care of already
220 pass
221
222 # Other datatype definitions can come from explicitly defining some nodes as datatypes (though rarely used,
223 # it is perfectly possible...
224 # there may be explicit relationships set in the graph, too!
225 for (s,p,o) in self.graph.triples((None, type, None)):
226 if o in OWL_RL_Datatypes:
227 _add_to_used_datatypes(o)
228 if s not in implicit:
229 _add_to_explicit(s, o)
230
231 # Finally, there may be sameAs statements that bind nodes to some of the existing ones. This does not introduce
232 # new datatypes, so the used_datatypes array does not get extended
233 for (s,p,o) in self.graph.triples((None, sameAs, None)):
234 if o in implicit:
235 _add_to_explicit(s, implicit[o])
236 # note that s in implicit would mean that the original graph has
237 # a literal in subject position which is not allowed at the moment, so I do not bother
238 if o in explicit :
239 _append_to_explicit(s, o)
240 if s in explicit :
241 _append_to_explicit(o, s)
242
243 # what we have now:
244 # explicit+implicit contains all the resources of type datatype;
245 # implicit contains those that are given by an explicit literal
246 # explicit contains those that are given by general resources, and there can be a whole array for each entry
247
248 # RULE dt-type1: add a Datatype typing for all those
249 # Note: the strict interpretation of OWL RL is to do that for all allowed datatypes, but this is
250 # under discussion right now. The optimized version uses only what is really in use
251 for dt in OWL_RL_Datatypes:
252 self.store_triple((dt,type,Datatype))
253 for dts in explicit.values():
254 for dt in dts:
255 self.store_triple((dt, type, Datatype))
256
257 # Datatype reasoning means that certain datatypes are subtypes of one another.
258 # I could simply generate the extra subclass relationships into the graph and let the generic
259 # process work its way, but it seems to be an overkill. Instead, I prefer to add the explicit typing
260 # into the graph 'manually'
261 for r in explicit:
262 # these are the datatypes that this resource has
263 dtypes = explicit[r]
264 for dt in dtypes:
265 _handle_subsumptions(r, dt)
266
267 for r in implicit:
268 dt = implicit[r]
269 _handle_subsumptions(r, dt)
270
271 # Last step: add the datatype disjointness relationships. This is done only for
272 # - 'top' level datatypes
273 # - used in the graph
274 for t in OWLRL_Datatypes_Disjointness:
275 (l, pred, r) = t
276 if l in used_datatypes and r in used_datatypes:
277 self.store_triple(t)
278
280 """
281 Rules executed: cls-thing, cls-nothing, prp-ap.
282 """
283 # RULE cls-thing
284 self.store_triple((Thing, type, OWLClass))
285
286 # RULE cls-nothing
287 self.store_triple((Nothing, type, OWLClass))
288
289 # RULE prp-ap
290 for an in OWLRL_Annotation_properties:
291 self.store_triple((an, type, AnnotationProperty))
292
294 """
295 Some of the rules in the rule set are axiomatic in nature, meaning that they really have to be added only
296 once, there is no reason to add these in a cycle. These are performed by this method that is invoked only once
297 at the beginning of the process.
298
299 These are: cls-thing, cls-nothing1, prp-ap, dt-types1, dt-types2, dt-eq, dt-diff.
300 """
301 self._one_time_rules_misc()
302 self._one_time_rules_datatypes()
303
305 """
306 Go through the various rule groups, as defined in the OWL-RL profile text and implemented via
307 local methods. (The calling cycle takes every tuple in the graph.)
308 @param t: a triple (in the form of a tuple)
309 @param cycle_num: which cycle are we in, starting with 1. This value is forwarded to all local rules; it is also used
310 locally to collect the bnodes in the graph.
311 """
312 if cycle_num == 1:
313 for r in t:
314 if isinstance(r, BNode) and r not in self.bnodes:
315 self.bnodes.append(r)
316
317 self._properties(t,cycle_num)
318 self._equality(t,cycle_num)
319 self._classes(t,cycle_num)
320 self._class_axioms(t,cycle_num)
321 self._schema_vocabulary(t,cycle_num)
322
324 """
325 Implementation of the property chain axiom, invoked from inside the property axiom handler. This is the
326 implementation of rule prp-spo2, taken aside for an easier readability of the code. """
327 chain = self._list(x)
328 # The complication is that, at each step of the chain, there may be spawns, leading to a multitude
329 # of 'sub' chains:-(
330 if len(chain) > 0:
331 for (u1, _y, _z) in self.graph.triples((None, chain[0], None)):
332 # At least the chain can be started, because the leftmost property has at least
333 # one element in its extension
334 finalList = [(u1, _z)]
335 chainExists = True
336 for pi in chain[1:]:
337 newList = []
338 for (_u,ui) in finalList:
339 for u in self.graph.objects(ui,pi):
340 # what is stored is only last entry with u1, the intermediate results
341 # are not of interest
342 newList.append((u1, u))
343 # I have now, in new list, all the intermediate results
344 # until pi
345 # if new list is empty, that means that is a blind alley
346 if len(newList) == 0:
347 chainExists = False
348 break
349 else :
350 finalList = newList
351 if chainExists:
352 for (_u, un) in finalList :
353 self.store_triple((u1, p, un))
354
356 """
357 Table 4: Semantics of equality. Essentially, the eq-* rules.
358 @param triple: triple to work on
359 @param cycle_num: which cycle are we in, starting with 1. Can be used for some optimization.
360 """
361 # In many of the 'if' branches, corresponding to rules in the document,
362 # the branch begins by a renaming of variables (eg, pp,c = s,o).
363 # There is no programming reasons for doing that, but by renaming the
364 # variables it becomes easier to compare the declarative rules
365 # in the document with the implementation
366 s,p,o = triple
367 # RULE eq-ref
368 self.store_triple((s, sameAs, s))
369 self.store_triple((o, sameAs, o))
370 self.store_triple((p, sameAs, p))
371 if p == sameAs:
372 x, y = s, o
373 # RULE eq-sym
374 self.store_triple((y, sameAs, x))
375 # RULE eq-trans
376 for z in self.graph.objects(y, sameAs):
377 self.store_triple((x, sameAs, z))
378 # RULE eq-rep-s
379 for pp,oo in self.graph.predicate_objects(s):
380 self.store_triple((o, pp, oo))
381 # RULE eq-rep-p
382 for ss,oo in self.graph.subject_objects(s):
383 self.store_triple((ss, o, oo))
384 # RULE eq-rep-o
385 for ss,pp in self.graph.subject_predicates(o):
386 self.store_triple((ss, pp, s))
387 # RULE eq-diff1
388 if (s,differentFrom,o) in self.graph or (o,differentFrom,s) in self.graph:
389 s_e = self._get_resource_or_literal(s)
390 o_e = self._get_resource_or_literal(o)
391 self.add_error("'sameAs' and 'differentFrom' cannot be used on the same subject-object pair: (%s, %s)" % (s_e, o_e))
392
393 # RULES eq-diff2 and eq-diff3
394 if p == type and o == AllDifferent:
395 x = s
396 # the objects method are generators, we cannot simply concatenate them. So we turn the results
397 # into lists first. (Otherwise the body of the for loops should be repeated verbatim, which
398 # is silly and error prone...
399 m1 = [i for i in self.graph.objects(x, members)]
400 m2 = [i for i in self.graph.objects(x, distinctMembers)]
401 for y in m1 + m2:
402 zis = self._list(y)
403 for i in xrange(0, len(zis) - 1):
404 zi = zis[i]
405 for j in xrange(i+1, len(zis) - 1):
406 zj = zis[j]
407 if ((zi, sameAs, zj) in self.graph or (zj, sameAs, zi) in self.graph) and zi != zj:
408 self.add_error("'sameAs' and 'AllDifferent' cannot be used on the same subject-object pair: (%s, %s)" % (zi,zj))
409
411 """
412 Table 5: The Semantics of Axioms about Properties. Essentially, the prp-* rules.
413 @param triple: triple to work on
414 @param cycle_num: which cycle are we in, starting with 1. Can be used for some optimization.
415 """
416 # In many of the 'if' branches, corresponding to rules in the document,
417 # the branch begins by a renaming of variables (eg, pp,c = s,o).
418 # There is no programming reasons for doing that, but by renaming the
419 # variables it becomes easier to compare the declarative rules
420 # in the document with the implementation
421 p, t, o = triple
422
423 # RULE prp-ap
424 if cycle_num == 1 and t in OWLRL_Annotation_properties:
425 self.store_triple((t, type, AnnotationProperty))
426
427 # RULE prp-dom
428 if t == domain:
429 for x, y in self.graph.subject_objects(p):
430 self.store_triple((x, type, o))
431
432 # RULE prp-rng
433 elif t == range:
434 for x, y in self.graph.subject_objects(p):
435 self.store_triple((y, type, o))
436
437 elif t == type:
438 # RULE prp-fp
439 if o == FunctionalProperty:
440 # Property axiom #3
441 for x, y1 in self.graph.subject_objects(p):
442 for y2 in self.graph.objects(x,p):
443 # Optimization: if the two resources are identical, the samAs is already
444 # taken place somewhere else, unnecessary to add it here
445 if y1 != y2:
446 self.store_triple((y1, sameAs, y2))
447
448 # RULE prp-ifp
449 elif o == InverseFunctionalProperty:
450 for x1, y in self.graph.subject_objects(p):
451 for x2 in self.graph.subjects(p,y):
452 # Optimization: if the two resources are identical, the samAs is already
453 # taken place somewhere else, unnecessary to add it here
454 if x1 != x2:
455 self.store_triple((x1, sameAs, x2))
456
457 # RULE prp-irp
458 elif o == IrreflexiveProperty:
459 for x, y in self.graph.subject_objects(p):
460 if x == y:
461 self.add_error("Irreflexive property used on %s with %s" % (x,p))
462
463 # RULE prp-symp
464 elif o == SymmetricProperty:
465 for x, y in self.graph.subject_objects(p):
466 self.store_triple((y, p, x))
467
468 # RULE prp-asyp
469 elif o == AsymmetricProperty:
470 for x, y in self.graph.subject_objects(p):
471 if (y, p, x) in self.graph :
472 self.add_error("Erroneous usage of asymmetric property %s on %s and %s" % (p, x, y))
473
474 # RULE prp-trp
475 elif o == TransitiveProperty:
476 for x, y in self.graph.subject_objects(p):
477 for z in self.graph.objects(y, p):
478 self.store_triple((x, p, z))
479
480 #
481 # Breaking the order here, I take some additional rules here to the branch checking the type...
482 #
483 # RULE prp-adp
484 elif o == AllDisjointProperties:
485 x = p
486 for y in self.graph.objects(x, members):
487 pis = self._list(y)
488 for i in xrange(0,len(pis) - 1):
489 pi = pis[i]
490 for j in xrange(i+1,len(pis) - 1):
491 pj = pis[j]
492 for x,y in self.graph.subject_objects(pi):
493 if (x, pj, y) in self.graph :
494 self.add_error("Disjoint properties in an 'AllDisjointProperties' are not really disjoint: (%s, %s,%s) and (%s,%s,%s)" % (x, pi, y, x, pj, y))
495
496 # RULE prp-spo1
497 elif t == subPropertyOf:
498 p1, p2 = p, o
499 for x, y in self.graph.subject_objects(p1):
500 self.store_triple((x, p2, y))
501
502 # RULE prp-spo2
503 elif t == propertyChainAxiom:
504 self._property_chain(p, o)
505
506 # RULES prp-eqp1 and prp-eqp2
507 elif t == equivalentProperty:
508 p1, p2 = p, o
509 # Optimization: it clearly does not make sense to run these
510 # if the two properties are identical (a separate axiom
511 # does create an equivalent property relations among identical
512 # properties, too...)
513 if p1 != p2:
514 # RULE prp-eqp1
515 for x, y in self.graph.subject_objects(p1):
516 self.store_triple((x, p2, y))
517 # RULE prp-eqp2
518 for x, y in self.graph.subject_objects(p2):
519 self.store_triple((x, p1, y))
520
521 # RULE prp-pdw
522 elif t == propertyDisjointWith:
523 p1, p2 = p, o
524 for x, y in self.graph.subject_objects(p1):
525 if (x, p2, y) in self.graph:
526 self.add_error("Erroneous usage of disjoint properties %s and %s on %s and %s" % (p1,p2,x,y))
527
528
529 # RULES prp-inv1 and prp-inv2
530 elif t == inverseOf:
531 p1, p2 = p, o
532 # RULE prp-inv1
533 for x, y in self.graph.subject_objects(p1):
534 self.store_triple((y, p2, x))
535 # RULE prp-inv2
536 for x, y in self.graph.subject_objects(p2):
537 self.store_triple((y, p1, x))
538
539 # RULE prp-key
540 elif t == hasKey :
541 c, u = p, o
542 pis = self._list(u)
543 if len(pis) > 0 :
544 for x in self.graph.subjects(type, c):
545 # "Calculate" the keys for 'x'. The complication is that there can be various combinations
546 # of the keys, and that is the structure one has to build up here...
547 #
548 # The final list will be a list of lists, with each constituents being the possible combinations of the
549 # key values.
550 # startup the list
551 finalList = [[zi] for zi in self.graph.objects(x, pis[0])]
552 for pi in pis[1:]:
553 newList = []
554 for zi in self.graph.objects(x, pi):
555 newList = newList + [l + [zi] for l in finalList]
556 finalList = newList
557
558 # I am not sure this can happen, but better safe then sorry... ruling out
559 # the value lists whose length are not kosher
560 # (To be checked whether this is necessary in the first place)
561 valueList = [l for l in finalList if len(l) == len(pis)]
562
563 # Now we can look for the y-s, to see if they have the same key values
564 for y in self.graph.subjects(type, c):
565 # rule out the existing equivalences
566 if not(y == x or (y, sameAs, x) in self.graph or (x, sameAs, y) in self.graph) :
567 # 'calculate' the keys for the y values and see if there is a match
568 for vals in valueList:
569 same = True
570 for i in xrange(0,len(pis) - 1):
571 if (y, pis[i], vals[i]) not in self.graph :
572 same = False
573 # No use going with this property line
574 break
575 if same :
576 self.store_triple((x, sameAs, y))
577 # Look for the next 'y', this branch is finished, no reason to continue
578 break
579
580 # RULES prp-npa1 and prp-npa2
581 elif t == sourceIndividual:
582 x, i1 = p, o
583 for p1 in self.graph.objects(x, assertionProperty):
584 for i2 in self.graph.objects(x, targetIndividual):
585 if (i1, p1, i2) in self.graph :
586 self.add_error("Negative (object) property assertion violated for: (%s, %s, %s)" % (i1, p1, i2))
587 for i2 in self.graph.objects(x,targetValue) :
588 if (i1, p1, i2) in self.graph :
589 self.add_error("Negative (datatype) property assertion violated for: (%s, %s, %s)" % (i1, p1, self.get_literal_value(i2)))
590
592 """
593 Table 6: The Semantics of Classes. Essentially, the cls-* rules
594 @param triple: triple to work on
595 @param cycle_num: which cycle are we in, starting with 1. Can be used for some optimization.
596 """
597 # In many of the 'if' branches, corresponding to rules in the document,
598 # the branch begins by a renaming of variables (eg, pp,c = s,o).
599 # There is no programming reasons for doing that, but by renaming the
600 # variables it becomes easier to compare the declarative rules
601 # in the document with the implementation
602 c, p, x = triple
603
604 # RULE cls-nothing2
605 if p == type and x == Nothing :
606 self.add_error("%s is defined of type 'Nothing'" % c)
607
608 # RULES cls-int1 and cls-int2
609 if p == intersectionOf:
610 classes = self._list(x)
611 # RULE cls-int1
612 # Optimization: by looking at the members of class[0] right away one
613 # reduces the search spaces a bit. Individuals not in that class
614 # are without interest anyway
615 # I am not sure how empty lists are sanctioned, so having an extra check
616 # on that does not hurt..
617 if len(classes) > 0 :
618 for y in self.graph.subjects(type, classes[0]):
619 if False not in [(y, type, cl) in self.graph for cl in classes[1:]] :
620 self.store_triple((y, type, c))
621 # RULE cls-int2
622 for y in self.graph.subjects(type, c):
623 for cl in classes:
624 self.store_triple((y, type, cl))
625
626 # RULE cls-uni
627 elif p == unionOf:
628 for cl in self._list(x):
629 for y in self.graph.subjects(type, cl):
630 self.store_triple((y, type, c))
631
632 # RULE cls-comm
633 elif p == complementOf:
634 c1, c2 = c, x
635 for x1 in self.graph.subjects(type, c1):
636 if (x1, type, c2) in self.graph :
637 self.add_error("Violation of complementarity for classes %s and %s on element %s" % (c1, c2, x))
638
639 # RULES cls-svf1 and cls=svf2
640 elif p == someValuesFrom:
641 xx, y = c, x
642 # RULE cls-svf1
643 # RULE cls-svf2
644 for pp in self.graph.objects(xx, onProperty):
645 for u, v in self.graph.subject_objects(pp) :
646 if y == Thing or (v, type, y) in self.graph:
647 self.store_triple((u, type, xx))
648
649 # RULE cls-avf
650 elif p == allValuesFrom:
651 xx, y = c, x
652 for pp in self.graph.objects(xx, onProperty):
653 for u in self.graph.subjects(type, xx):
654 for v in self.graph.objects(u, pp) :
655 if self.restriction_typing_check(v, y):
656 self.store_triple((v, type, y))
657 else :
658 self.add_error("Violation of type restriction for allValuesFrom in %s for datatype %s on value %s" % (pp, y, self._get_resource_or_literal(v)))
659
660 # RULES cls-hv1 and cls-hv2
661 elif p == hasValue:
662 xx, y = c, x
663 for pp in self.graph.objects(xx, onProperty):
664 # RULE cls-hv1
665 for u in self.graph.subjects(type, xx):
666 self.store_triple((u, pp, y))
667 # RULE cls-hv2
668 for u in self.graph.subjects(pp, y):
669 self.store_triple((u, type, xx))
670
671 # RULES cls-maxc1 and cls-maxc1
672 elif p == maxCardinality:
673 # This one is a bit complicated, because the literals have been
674 # exchanged against bnodes...
675 #
676 # The construct should lead to an integer. Something may go wrong along the line
677 # leading to an exception...
678 val = -1
679 try:
680 val = int(self.literal_proxies.bnode_to_lit[x].lit)
681 except:
682 pass
683 xx = c
684 if val == 0:
685 # RULE cls-maxc1
686 for pp in self.graph.objects(xx, onProperty):
687 for u,y in self.graph.subject_objects(pp):
688 # This should not occur:
689 if (u, type, xx) in self.graph:
690 self.add_error("Erroneous usage of maximum cardinality with %s, %s" % (xx, y))
691 elif val == 1:
692 # RULE cls-maxc2
693 for pp in self.graph.objects(xx, onProperty):
694 for u, y1 in self.graph.subject_objects(pp):
695 if (u, type, xx) in self.graph:
696 for y2 in self.graph.objects(u, pp):
697 if y1 != y2 :
698 self.store_triple((y1, sameAs, y2))
699
700 # RULES cls-maxqc1, cls-maxqc2, cls-maxqc3, cls-maxqc4
701 elif p == maxQualifiedCardinality:
702 # This one is a bit complicated, because the literals have been
703 # exchanged against bnodes...
704 #
705 # The construct should lead to an integer. Something may go wrong along the line
706 # leading to an exception...
707 val = -1
708 try :
709 val = int(self.literal_proxies.bnode_to_lit[x].lit)
710 except:
711 pass
712 xx = c
713 if val == 0 :
714 # RULES cls-maxqc1 and cls-maxqc2 folded in one
715 for pp in self.graph.objects(xx, onProperty):
716 for cc in self.graph.objects(xx, onClass):
717 for u,y in self.graph.subject_objects(pp):
718 # This should not occur:
719 if (u, type, xx) in self.graph and (cc == Thing or (y, type, cc) in self.graph):
720 self.add_error("Erroneous usage of maximum qualified cardinality with %s, %s, and %s" % (xx, cc, y))
721 elif val == 1 :
722 # RULE cls-maxqc3 and cls-maxqc4 folded in one
723 for pp in self.graph.objects(xx, onProperty):
724 for cc in self.graph.objects(xx, onClass) :
725 for u, y1 in self.graph.subject_objects(pp):
726 if (u, type, xx) in self.graph :
727 if cc == Thing:
728 for y2 in self.graph.objects(u, pp):
729 if y1 != y2:
730 self.store_triple((y1, sameAs, y2))
731 else :
732 if (y1, type, cc) in self.graph:
733 for y2 in self.graph.objects(u, pp) :
734 if y1 != y2 and (y2, type, cc) in self.graph:
735 self.store_triple((y1, sameAs, y2))
736
737 # RULE cls-oo
738 elif p == oneOf:
739 for y in self._list(x):
740 self.store_triple((y, type, c))
741
743 """
744 Table 7: Class Axioms. Essentially, the cax-* rules.
745 @param triple: triple to work on
746 @param cycle_num: which cycle are we in, starting with 1. Can be used for some optimization.
747 """
748 # In many of the 'if' branches, corresponding to rules in the document,
749 # the branch begins by a renaming of variables (eg, pp,c = s,o).
750 # There is no programming reasons for doing that, but by renaming the
751 # variables it becomes easier to compare the declarative rules
752 # in the document with the implementation
753 c1,p,c2 = triple
754 # RULE cax-sco
755 if p == subClassOf:
756 # Other axioms sets classes to be subclasses of themselves, to one can optimize the trivial case
757 if c1 != c2 :
758 for x in self.graph.subjects(type, c1):
759 self.store_triple((x, type, c2))
760
761 # RULES cax-eqc1 and cax-eqc1
762 # Other axioms set classes to be equivalent to themselves, one can optimize the trivial case
763 elif p == equivalentClass and c1 != c2:
764 # RULE cax-eqc1
765 for x in self.graph.subjects(type, c1):
766 self.store_triple((x, type, c2))
767 # RULE cax-eqc1
768 for x in self.graph.subjects(type, c2):
769 self.store_triple((x, type, c1))
770
771 # RULE cax-dw
772 elif p == disjointWith:
773 for x in self.graph.subjects(type, c1):
774 if (x, type, c2) in self.graph:
775 self.add_error("Disjoint classes %s and %s have a common individual %s" % (c1, c2, self._get_resource_or_literal(x)))
776
777 # RULE cax-adc
778 elif p == type and c2 == AllDisjointClasses:
779 x = c1
780 for y in self.graph.objects(x, members):
781 classes = self._list(y)
782 if len(classes) > 0:
783 for i in xrange(0, len(classes) - 1):
784 cl1 = classes[i]
785 for z in self.graph.subjects(type,cl1):
786 for cl2 in classes[(i + 1):]:
787 if (z, type, cl2) in self.graph:
788 self.add_error("Disjoint classes %s and %s have a common individual %s" % (cl1, cl2, z))
789
791 """
792 Table 9: The Semantics of Schema Vocabulary. Essentially, the scm-* rules
793 @param triple: triple to work on
794 @param cycle_num: which cycle are we in, starting with 1. Can be used for some optimization.
795 """
796 # In many of the 'if' branches, corresponding to rules in the document,
797 # the branch begins by a renaming of variables (eg, pp,c = s,o).
798 # There is no programming reasons for doing that, but by renaming the
799 # variables it becomes easier to compare the declarative rules
800 # in the document with the implementation
801 s,p,o = triple
802
803 # RULE scm-cls
804 if p == type and o == OWLClass:
805 c = s
806 self.store_triple((c, subClassOf, c))
807 self.store_triple((c, equivalentClass, c))
808 self.store_triple((c, subClassOf, Thing))
809 self.store_triple((Nothing, subClassOf, c))
810
811 # RULE scm-sco
812 # Rule scm-eqc2
813 elif p == subClassOf:
814 c1, c2 = s, o
815 # RULE scm-sco
816 # Optimize out the trivial identity case (set elsewhere already)
817 if c1 != c2:
818 for c3 in self.graph.objects(c2, subClassOf):
819 # Another axiom already sets that...
820 if c1 != c3 : self.store_triple((c1, subClassOf, c3))
821 # RULE scm-eqc2
822 if (c2, subClassOf, c1) in self.graph:
823 self.store_triple((c1, equivalentClass, c2))
824
825 # RULE scm-eqc
826 elif p == equivalentClass and s != o:
827 c1,c2 = s,o
828 self.store_triple((c1, subClassOf, c2))
829 self.store_triple((c2, subClassOf, c1))
830
831 # RULE scm-op and RULE scm-dp folded together
832 # There is a bit of a cheating here: 'Property' is not, strictly speaking, in the rule set!
833 elif p == type and (o == ObjectProperty or o == DatatypeProperty or o == Property):
834 pp = s
835 self.store_triple((pp, subPropertyOf, pp))
836 self.store_triple((pp, equivalentProperty, pp))
837
838 # RULE scm-spo
839 # RULE scm-eqp2
840 elif p == subPropertyOf and s != o:
841 p1, p2 = s, o
842 # Optimize out the trivial identity case (set elsewhere already)
843 # RULE scm-spo
844 if p1 != p2 :
845 for p3 in self.graph.objects(p2,subPropertyOf):
846 if p1 != p3:
847 self.store_triple((p1, subPropertyOf, p3))
848
849 #RULE scm-eqp2
850 if (p2, subPropertyOf, p1) in self.graph:
851 self.store_triple((p1, equivalentProperty, p2))
852
853 # RULE scm-eqp
854 # Optimize out the trivial identity case (set elsewhere already)
855 elif p == equivalentProperty and s != o:
856 p1, p2 = s, o
857 self.store_triple((p1, subPropertyOf, p2))
858 self.store_triple((p2, subPropertyOf, p1))
859
860 # RULES scm-dom1 and scm-dom2
861 elif p == domain:
862 # RULE scm-dom1
863 pp, c1 = s, o
864 for (_x, _y, c2) in self.graph.triples((c1, subClassOf, None)) :
865 if c1 != c2 :
866 self.store_triple((pp, domain, c2))
867 # RULE scm-dom1
868 p2, c = s, o
869 for (p1,_x,_y) in self.graph.triples((None, subPropertyOf, p2)) :
870 if p1 != p2 :
871 self.store_triple((p1, domain, c))
872
873 # RULES scm-rng1 and scm-rng2
874 elif p == range:
875 # RULE scm-rng1
876 pp, c1 = s, o
877 for (_x,_y,c2) in self.graph.triples((c1, subClassOf, None)) :
878 if c1 != c2 : self.store_triple((pp, range, c2))
879 # RULE scm-rng1
880 p2, c = s, o
881 for (p1,_x,_y) in self.graph.triples((None, subPropertyOf, p2)) :
882 if p1 != p2 : self.store_triple((p1, range, c))
883
884 # RULE scm-hv
885 elif p == hasValue:
886 c1, i = s, o
887 for p1 in self.graph.objects(c1, onProperty):
888 for c2 in self.graph.subjects(hasValue, i):
889 for p2 in self.graph.objects(c2, onProperty):
890 if (p1, subPropertyOf, p2) in self.graph:
891 self.store_triple((c1, subClassOf, c2))
892
893 # RULES scm-svf1 and scm-svf2
894 elif p == someValuesFrom:
895 # RULE scm-svf1
896 c1, y1 = s, o
897 for pp in self.graph.objects(c1,onProperty):
898 for c2 in self.graph.subjects(onProperty,pp):
899 for y2 in self.graph.objects(c2, someValuesFrom):
900 if (y1,subClassOf, y2) in self.graph:
901 self.store_triple((c1, subClassOf, c2))
902
903 # RULE scm-svf2
904 c1, y = s, o
905 for p1 in self.graph.objects(c1, onProperty):
906 for c2 in self.graph.subjects(someValuesFrom,y):
907 for p2 in self.graph.objects(c2, onProperty):
908 if (p1,subPropertyOf,p2) in self.graph:
909 self.store_triple((c1, subClassOf, c2))
910
911 # RULES scm-avf1 and scm-avf2
912 elif p == allValuesFrom:
913 # RULE scm-avf1
914 c1, y1 = s, o
915 for pp in self.graph.objects(c1, onProperty):
916 for c2 in self.graph.subjects(onProperty, pp) :
917 for y2 in self.graph.objects(c2, allValuesFrom) :
918 if (y1,subClassOf,y2) in self.graph:
919 self.store_triple((c1, subClassOf, c2))
920
921 # RULE scm-avf2
922 c1, y = s, o
923 for p1 in self.graph.objects(c1, onProperty):
924 for c2 in self.graph.subjects(allValuesFrom, y):
925 for p2 in self.graph.objects(c2, onProperty):
926 if (p1,subPropertyOf, p2) in self.graph :
927 self.store_triple((c2, subClassOf, c1))
928
929 # RULE scm-int
930 elif p == intersectionOf:
931 c, x = s, o
932 for ci in self._list(x):
933 self.store_triple((c, subClassOf, ci))
934
935 # RULE scm-uni
936 elif p == unionOf:
937 c, x = s, o
938 for ci in self._list(x):
939 self.store_triple((ci, subClassOf, c))
940
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Fri Feb 7 15:00:19 2014 | http://epydoc.sourceforge.net |