Package RDFClosure :: Module OWLRL
[hide private]
[frames] | no frames]

Source Code for Module RDFClosure.OWLRL

  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 
49 -class OWLRL_Semantics(Core):
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 """
59 - def __init__(self, graph, axioms, daxioms, rdfs=None):
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
73 - def _list(self, l) :
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
82 - def _get_resource_or_literal(self, node):
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
88 - def post_process(self):
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
100 - def add_axioms(self):
101 """ 102 Add axioms 103 """ 104 for t in OWLRL_Axiomatic_Triples: 105 self.graph.add(t)
106
107 - def add_d_axioms(self) :
108 """ 109 Add the datatype axioms 110 """ 111 for t in OWLRL_D_Axiomatic_Triples: 112 self.graph.add(t)
113
114 - def restriction_typing_check(self, v, t) :
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
279 - def _one_time_rules_misc(self):
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
293 - def one_time_rules(self):
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
304 - def rules(self, t, cycle_num):
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
323 - def _property_chain(self, p, x):
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
355 - def _equality(self, triple, cycle_num):
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
410 - def _properties(self, triple, cycle_num):
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
591 - def _classes(self, triple, cycle_num) :
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
742 - def _class_axioms(self, triple, cycle_num):
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
790 - def _schema_vocabulary(self, triple, cycle_num):
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