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