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

Source Code for Module RDFClosure.OWLRLExtras

  1  #!/d/Bin/Python/python.exe 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  """ 
  5   
  6  Extension to OWL 2 RL, ie, some additional rules added to the system from OWL 2 Full. It is implemented through 
  7  the L{OWLRL_Extension} class, whose reference has to be passed to the relevant semantic class (ie, either the OWL 2 RL or the combined 
  8  closure class) as an 'extension'. 
  9   
 10  The added rules and features are: 
 11   
 12   - self restriction 
 13   - owl:rational datatype 
 14   - datatype restrictions via facets 
 15    
 16  In more details, the rules that are added: 
 17   
 18   1. self restriction 1: C{?z owl:hasSelf ?x. ?x owl:onProperty ?p. ?y rdf:type ?z. => ?y ?p ?y.} 
 19   2. self restriction 2: C{?z owl:hasSelf ?x. ?x owl:onProperty ?p. ?y ?p ?y. => ?y rdf:type ?z. } 
 20   
 21  @requires: U{RDFLib<https://github.com/RDFLib/rdflib>}, 4.0.0 and higher 
 22  @license: This software is available for use under the U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>} 
 23  @organization: U{World Wide Web Consortium<http://www.w3.org>} 
 24  @author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">} 
 25   
 26  """ 
 27   
 28  __author__  = 'Ivan Herman' 
 29  __contact__ = 'Ivan Herman, ivan@w3.org' 
 30  __license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231' 
 31   
 32  import rdflib 
 33  # noinspection PyPep8Naming 
 34  from rdflib.namespace   import XSD as ns_xsd 
 35   
 36  from .RDFS      import  Property 
 37  # noinspection PyPep8Naming 
 38  from .RDFS      import type as rdfType 
 39  from .RDFS      import Resource, Class, subClassOf, subPropertyOf, domain 
 40  from .RDFS      import Datatype 
 41   
 42  from fractions import Fraction as Rational 
 43   
 44  from .DatatypeHandling import AltXSDToPYTHON 
 45   
 46  from .OWL import * 
 47  # noinspection PyPep8Naming 
 48  from .OWL import OWLNS as ns_owl 
 49  from .CombinedClosure import RDFS_OWLRL_Semantics 
 50  from .OWLRL import OWLRL_Annotation_properties 
 51   
 52  from .XsdDatatypes import OWL_RL_Datatypes, OWL_Datatype_Subsumptions 
 53   
 54  from RestrictedDatatype import extract_faceted_datatypes 
 55   
 56  ########################################################################################################################### 
 57  # Rational datatype 
 58   
 59  # noinspection PyPep8Naming 
60 -def _strToRational(v):
61 """Converting a string to a rational. 62 63 According to the OWL spec: numerator must be an integer, denominator a positive integer (ie, xsd['integer'] type), and the denominator 64 should not have a '+' sign. 65 66 @param v: the literal string defined as boolean 67 @return corresponding Rational value 68 @rtype: Rational 69 @raise ValueError: invalid rational string literal 70 """ 71 try : 72 r = v.split('/') 73 if len(r) == 2: 74 n_str = r[0] 75 d_str = r[1] 76 else: 77 n_str = r[0] 78 d_str = "1" 79 if d_str.strip()[0] == '+': 80 raise ValueError("Invalid Rational literal value %s" % v) 81 else : 82 return Rational(AltXSDToPYTHON[ns_xsd["integer"]](n_str), AltXSDToPYTHON[ns_xsd["positiveInteger"]](d_str)) 83 except: 84 raise ValueError("Invalid Rational literal value %s" % v)
85 86 ########################################################################################################################### 87 88 89 # noinspection PyPep8Naming,PyBroadException
90 -class OWLRL_Extension(RDFS_OWLRL_Semantics) :
91 """ 92 Additional rules to OWL 2 RL. The initialization method also adds the C{owl:rational} datatype to the set of allowed 93 datatypes with the L{_strToRational} function as a conversion between the literal form and a Rational. The C{xsd:decimal} datatype 94 is also set to be a subclass of C{owl:rational}. Furthermore, the restricted datatypes are extracted from the graph 95 using a L{separate method in a different module<RestrictedDatatype.extract_faceted_datatypes>}, and all those datatypes are also 96 added to the set of allowed datatypes. In the case of the restricted datatypes and extra subsumption relationship is set up 97 between the restricted and the base datatypes. 98 99 @cvar extra_axioms: additional axioms that have to be added to the deductive closure (in case the axiomatic triples are required) 100 @ivar restricted_datatypes : list of the datatype restriction 101 @type restricted_datatypes : array of L{restricted datatype<RestrictedDatatype.RestrictedDatatype>} instances 102 """ 103 extra_axioms = [ 104 (hasSelf, rdfType, Property), 105 (hasSelf, domain, Property), 106 ]
107 - def __init__(self, graph, axioms, daxioms, rdfs=False):
108 """ 109 @param graph: the RDF graph to be extended 110 @type graph: rdflib.Graph 111 @param axioms: whether (non-datatype) axiomatic triples should be added or not 112 @type axioms: Boolean 113 @param daxioms: whether datatype axiomatic triples should be added or not 114 @type daxioms: Boolean 115 @param rdfs: whether RDFS extension is done 116 @type rdfs: boolean 117 """ 118 RDFS_OWLRL_Semantics.__init__(self, graph, axioms, daxioms, rdfs) 119 self.rdfs = rdfs 120 self.add_new_datatype(ns_owl["rational"], _strToRational, OWL_RL_Datatypes, 121 subsumption_dict = OWL_Datatype_Subsumptions, 122 subsumption_key = ns_xsd["decimal"], 123 subsumption_list = [ns_owl["rational"]]) 124 125 self.restricted_datatypes = extract_faceted_datatypes(self, graph) 126 for dt in self.restricted_datatypes: 127 self.add_new_datatype(dt.datatype, dt.toPython, OWL_RL_Datatypes, 128 subsumption_dict = OWL_Datatype_Subsumptions, 129 subsumption_key = dt.datatype, 130 subsumption_list = [dt.base_type])
131 132 # noinspection PyShadowingNames
134 """ 135 A one-time-rule: all the literals are checked whether they are (a) of type restricted by a 136 faceted (restricted) datatype and (b) whether 137 the corresponding value abides to the restrictions. If true, then the literal gets an extra 138 tag as being of type of the restricted datatype, too. 139 """ 140 for rt in self.restricted_datatypes: 141 # This is a recorded restriction. The base type is: 142 base_type = rt.base_type 143 # Look through all the literals; more precisely, through the 144 # proxy bnodes: 145 for bn in self.literal_proxies.bnode_to_lit: 146 # check if the type of that proxy matches. Note that this also takes 147 # into account the subsumption datatypes, that have been taken 148 # care of by the 'regular' OWL RL process 149 if (bn, rdfType, base_type) in self.graph: 150 # yep, that is a good candidate! 151 lt = self.literal_proxies.bnode_to_lit[bn] 152 try : 153 # the conversion or the check may go wrong and raise an exception; then simply move on 154 value = lt.lit.toPython() 155 if rt.checkValue(value): 156 # yep, this is also of type 'rt' 157 self.store_triple((bn, rdfType, rt.datatype)) 158 except: 159 continue
160
161 - def restriction_typing_check(self, v, t):
162 """Helping method to check whether a type statement is in line with a possible 163 restriction. This method is invoked by rule "cls-avf" before setting a type 164 on an allValuesFrom restriction. 165 166 The method is a placeholder at this level. It is typically implemented by subclasses for 167 extra checks, eg, for datatype facet checks. 168 @param v: the resource that is to be 'typed' 169 @param t: the targeted type (ie, Class) 170 @return: boolean 171 """ 172 # Look through the restricted datatypes to see if 't' corresponds to one of those... 173 # There are a bunch of possible exceptions here with datatypes, but they can all 174 # be ignored... 175 try : 176 for rt in self.restricted_datatypes: 177 if rt.datatype == t: 178 # bingo 179 if v in self.literal_proxies.bnode_to_lit: 180 return rt.checkValue(self.literal_proxies.bnode_to_lit[v].lit.toPython()) 181 else : 182 return True 183 # if we got here, no restriction applies 184 return True 185 except : 186 return True
187
188 - def one_time_rules(self):
189 """ 190 This method is invoked only once at the beginning, and prior of, the forward chaining process. 191 192 At present, only the L{subsumption} of restricted datatypes<_subsume_restricted_datatypes>} is performed. 193 """ 194 RDFS_OWLRL_Semantics.one_time_rules(self) 195 # it is important to flush the triples at this point, because 196 # the handling of the restriction datatypes rely on the datatype 197 # subsumption triples added by the superclass 198 self.flush_stored_triples() 199 self._subsume_restricted_datatypes()
200
201 - def add_axioms(self):
202 """ 203 Add the L{extra axioms<OWLRL_Extension.extra_axioms>}, related to the self restrictions. 204 """ 205 RDFS_OWLRL_Semantics.add_axioms(self) 206 for t in self.extra_axioms : self.graph.add(t)
207
208 - def rules(self, t, cycle_num):
209 """ 210 Go through the additional rules implemented by this module. 211 @param t: a triple (in the form of a tuple) 212 @param cycle_num: which cycle are we in, starting with 1. This value is forwarded to all local rules; it is also used 213 locally to collect the bnodes in the graph. 214 """ 215 RDFS_OWLRL_Semantics.rules(self, t, cycle_num) 216 z, q, x = t 217 if q == hasSelf: 218 for p in self.graph.objects(z, onProperty): 219 for y in self.graph.subjects(rdfType, z): 220 self.store_triple((y, p, y)) 221 for y1, y2 in self.graph.subject_objects(p): 222 if y1 == y2 : 223 self.store_triple((y1, rdfType, z))
224 225 226 # noinspection PyPep8Naming
227 -class OWLRL_Extension_Trimming(OWLRL_Extension):
228 """ 229 This Class adds only one feature to L{OWLRL_Extension}: to initialize with a trimming flag set to C{True} by default. 230 231 This is pretty experimental and probably contentious: this class I{removes} a number of triples from the Graph at the very end of the processing steps. 232 These triples are either the by-products of the deductive closure calculation or are axiom like triples that are added following the rules of OWL 2 RL. 233 While these triples I{are necessary} for the correct inference of really 'useful' triples, they may not be of interest for the application 234 for the end result. The triples that are removed are of the form (following a SPARQL-like notation): 235 236 - C{?x owl:sameAs ?x}, C{?x rdfs:subClassOf ?x}, C{?x rdfs:subPropertyOf ?x}, C{?x owl:equivalentClass ?x} type triples 237 - C{?x rdfs:subClassOf rdf:Resource}, C{?x rdfs:subClassOf owl:Thing}, C{?x rdf:type rdf:Resource}, C{owl:Nothing rdfs:subClassOf ?x} type triples 238 - For a datatype that does I{not} appear explicitly in a type assignments (ie, in a C{?x rdf:type dt}) the corresponding C{dt rdf:type owl:Datatype} and C{dt rdf:type owl:DataRange} triples, as well as the disjointness statements with other datatypes 239 - annotation property axioms 240 - a number of axiomatic triples on C{owl:Thing}, C{owl:Nothing} and C{rdf:Resource} (eg, C{owl:Nothing rdf:type owl:Class}, C{owl:Thing owl:equivalentClass rdf:Resource}, etc.) 241 242 Trimming is the only feature of this class, done in the L{post_process} step. If users extend L{OWLRL_Extension}, this class can be safely mixed in via multiple 243 inheritance. 244 """
245 - def __init__(self, graph, axioms, daxioms, rdfs=False):
246 """ 247 @param graph: the RDF graph to be extended 248 @type graph: rdflib.Graph 249 @param axioms: whether (non-datatype) axiomatic triples should be added or not 250 @type axioms: Boolean 251 @param daxioms: whether datatype axiomatic triples should be added or not 252 @type daxioms: Boolean 253 @param rdfs: whether RDFS extension is done 254 @type rdfs: boolean 255 """ 256 OWLRL_Extension.__init__(self, graph, axioms, daxioms, rdfs=False)
257
258 - def post_process(self) :
259 """ 260 Do some post-processing step performing the trimming of the graph. See the L{class description<OWLRL_Extension_Trimming>} for further details. 261 262 """ 263 OWLRL_Extension.post_process(self) 264 self.flush_stored_triples() 265 266 to_be_removed = set() 267 for t in self.graph: 268 s, p, o = t 269 if s == o : 270 if p == sameAs or p == equivalentClass or p == subClassOf or p == subPropertyOf: 271 to_be_removed.add(t) 272 if (p == subClassOf and (o == Thing or o == Resource)) or (p == rdfType and o == Resource) or (s == Nothing and p == subClassOf): 273 to_be_removed.add(t) 274 275 for dt in OWL_RL_Datatypes : 276 # see if this datatype appears explicitly in the graph as the type of a symbol 277 if len([s for s in self.graph.subjects(rdfType, dt)]) == 0: 278 to_be_removed.add((dt, rdfType, Datatype)) 279 to_be_removed.add((dt, rdfType, DataRange)) 280 281 for t in self.graph.triples((dt, disjointWith, None)): 282 to_be_removed.add(t) 283 for t in self.graph.triples((None, disjointWith, dt)): 284 to_be_removed.add(t) 285 286 for an in OWLRL_Annotation_properties: 287 self.graph.remove((an, rdfType, AnnotationProperty)) 288 289 to_be_removed.add((Nothing, rdfType, OWLClass)) 290 to_be_removed.add((Nothing, rdfType, Class)) 291 to_be_removed.add((Thing, rdfType, OWLClass)) 292 to_be_removed.add((Thing, rdfType, Class)) 293 to_be_removed.add((Thing, equivalentClass, Resource)) 294 to_be_removed.add((Resource, equivalentClass, Thing)) 295 to_be_removed.add((OWLClass, equivalentClass, Class)) 296 to_be_removed.add((OWLClass, subClassOf, Class)) 297 to_be_removed.add((Class, equivalentClass, OWLClass)) 298 to_be_removed.add((Class, subClassOf, OWLClass)) 299 to_be_removed.add((Datatype, subClassOf, DataRange)) 300 to_be_removed.add((Datatype, equivalentClass, DataRange)) 301 to_be_removed.add((DataRange, subClassOf, Datatype)) 302 to_be_removed.add((DataRange, equivalentClass, Datatype)) 303 304 for t in to_be_removed: 305 self.graph.remove(t)
306