Source code for owlrl.RDFSClosure

# -*- coding: utf-8 -*-
#
"""
This module is brute force implementation of the RDFS semantics on the top of RDFLib (with some caveats, see in the
introductory text).


**Requires**: `RDFLib`_, 4.0.0 and higher.

.. _RDFLib: https://github.com/RDFLib/rdflib

**License**: This software is available for use under the `W3C Software License`_.

.. _W3C Software License: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231

**Organization**: `World Wide Web Consortium`_

.. _World Wide Web Consortium: http://www.w3.org

**Author**: `Ivan Herman`_

.. _Ivan Herman: http://www.w3.org/People/Ivan/

"""

__author__ = "Ivan Herman"
__contact__ = "Ivan Herman, ivan@w3.org"
__license__ = "W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231"

import rdflib
from rdflib import Literal
from rdflib.namespace import RDF, RDFS
from itertools import product
from owlrl.Closure import Core
from owlrl.AxiomaticTriples import RDFS_Axiomatic_Triples, RDFS_D_Axiomatic_Triples


######################################################################################################

# RDFS Semantics class
# noinspection PyPep8Naming
[docs]class RDFS_Semantics(Core): """ RDFS Semantics class, ie, implementation of the RDFS closure graph. .. note:: Note that the module does *not* implement the so called Datatype entailment rules, simply because the underlying RDFLib does not implement the datatypes (ie, RDFLib will not make the literal "1.00" and "1.00000" identical, although even with all the ambiguities on datatypes, this I{should} be made equal...). Also, the so-called extensional entailment rules (Section 7.3.1 in the RDF Semantics document) have not been implemented either. The comments and references to the various rule follow the names as used in the `RDF Semantics document`_. .. _RDF Semantics document: http://www.w3.org/TR/rdf-mt/ :param graph: The RDF graph to be extended. :type graph: :class:`rdflib.Graph` :param axioms: Whether (non-datatype) axiomatic triples should be added or not. :type axioms: bool :param daxioms: Whether datatype axiomatic triples should be added or not. :type daxioms: bool :param rdfs: Whether RDFS inference is also done (used in subclassed only). :type rdfs: bool """ def __init__(self, graph, axioms, daxioms, rdfs): """ @param graph: the RDF graph to be extended @type graph: rdflib.Graph @param axioms: whether (non-datatype) axiomatic triples should be added or not @type axioms: bool @param daxioms: whether datatype axiomatic triples should be added or not @type daxioms: bool @param rdfs: whether RDFS inference is also done (used in subclassed only) @type rdfs: boolean """ Core.__init__(self, graph, axioms, daxioms, rdfs)
[docs] def add_axioms(self): """ Add axioms """ for t in RDFS_Axiomatic_Triples: self.graph.add(t) for i in range(1, self.IMaxNum + 1): ci = RDF[("_%d" % i)] self.graph.add((ci, RDF.type, RDF.Property)) self.graph.add((ci, RDFS.domain, RDFS.Resource)) self.graph.add((ci, RDFS.range, RDFS.Resource)) self.graph.add((ci, RDF.type, RDFS.ContainerMembershipProperty))
[docs] def add_d_axioms(self): """ This is not really complete, because it just uses the comparison possibilities that RDFLib provides. """ # #1 literals = (lt for lt in self._literals() if lt.datatype is not None) for lt in literals: self.graph.add((lt, RDF.type, lt.datatype)) for t in RDFS_D_Axiomatic_Triples: self.graph.add(t)
# noinspection PyBroadException
[docs] def one_time_rules(self): """ Some of the rules in the rule set are axiomatic in nature, meaning that they really have to be added only once, there is no reason to add these in a cycle. These are performed by this method that is invoked only once at the beginning of the process. In this case this is related to a 'hidden' same as rules on literals with identical values (though different lexical values). """ # There is also a hidden sameAs rule in RDF Semantics: if a literal appears in a triple, and another one has # the same value, then the triple should be duplicated with the other value. literals = self._literals() items = ( (lt1, lt2) for lt1, lt2 in product(literals, literals) if lt1.value == lt2.value ) for lt1, lt2 in items: # In OWL, this line is simply stating a sameAs for the # corresponding literals, and then let the usual rules take # effect. In RDFS this is not possible, so the sameAs rule is, # essentially replicated... for (s, p, o) in self.graph.triples((None, None, lt1)): self.graph.add((s, p, lt2))
[docs] def rules(self, t, cycle_num): """ Go through the RDFS entailment rules rdf1, rdfs4-rdfs12, by extending the graph. :param t: A triple (in the form of a tuple). :type t: tuple :param cycle_num: Which cycle are we in, starting with 1. Can be used for some (though minor) optimization. :type cycle_num: int """ s, p, o = t # rdf1 self.store_triple((p, RDF.type, RDF.Property)) # rdfs4a if cycle_num == 1: self.store_triple((s, RDF.type, RDFS.Resource)) # rdfs4b if cycle_num == 1: self.store_triple((o, RDF.type, RDFS.Resource)) if p == RDFS.domain: # rdfs2 for uuu, Y, yyy in self.graph.triples((None, s, None)): self.store_triple((uuu, RDF.type, o)) if p == RDFS.range: # rdfs3 for uuu, Y, vvv in self.graph.triples((None, s, None)): self.store_triple((vvv, RDF.type, o)) if p == RDFS.subPropertyOf: # rdfs5 for Z, Y, xxx in self.graph.triples((o, RDFS.subPropertyOf, None)): self.store_triple((s, RDFS.subPropertyOf, xxx)) # rdfs7 for zzz, Z, www in self.graph.triples((None, s, None)): self.store_triple((zzz, o, www)) if p == RDF.type and o == RDF.Property: # rdfs6 self.store_triple((s, RDFS.subPropertyOf, s)) if p == RDF.type and o == RDFS.Class: # rdfs8 self.store_triple((s, RDFS.subClassOf, RDFS.Resource)) # rdfs10 self.store_triple((s, RDFS.subClassOf, s)) if p == RDFS.subClassOf: # rdfs9 for vvv, Y, Z in self.graph.triples((None, RDF.type, s)): self.store_triple((vvv, RDF.type, o)) # rdfs11 for Z, Y, xxx in self.graph.triples((o, RDFS.subClassOf, None)): self.store_triple((s, RDFS.subClassOf, xxx)) if p == RDF.type and o == RDFS.ContainerMembershipProperty: # rdfs12 self.store_triple((s, RDFS.subPropertyOf, RDFS.member)) if p == RDF.type and o == RDFS.Datatype: self.store_triple((s, RDFS.subClassOf, RDFS.Literal))
def _literals(self): """ Get all literals defined in the graph. """ return set(o for s, p, o in self.graph if isinstance(o, Literal))