1
2
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
34 from rdflib.namespace import XSD as ns_xsd
35
36 from .RDFS import Property
37
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
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
58
59
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
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
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
142 base_type = rt.base_type
143
144
145 for bn in self.literal_proxies.bnode_to_lit:
146
147
148
149 if (bn, rdfType, base_type) in self.graph:
150
151 lt = self.literal_proxies.bnode_to_lit[bn]
152 try :
153
154 value = lt.lit.toPython()
155 if rt.checkValue(value):
156
157 self.store_triple((bn, rdfType, rt.datatype))
158 except:
159 continue
160
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
173
174
175 try :
176 for rt in self.restricted_datatypes:
177 if rt.datatype == t:
178
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
184 return True
185 except :
186 return True
187
200
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
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
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