Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graph with subclass relationship and sh:class constraint not considered conform #142

Closed
morth opened this issue Apr 8, 2022 · 2 comments

Comments

@morth
Copy link

morth commented Apr 8, 2022

Given the following shapes graph:

# shapes.ttl

@prefix owl:    <http://www.w3.org/2002/07/owl#> .
@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh:     <http://www.w3.org/ns/shacl#> .
@prefix ex:     <http://example.com/ns#> .

ex:Person a owl:Class .
ex:Animal a owl:Class .
ex:Dog    a owl:Class ; rdfs:subClassOf ex:Animal .

ex:PersonShape
    a sh:NodeShape ;
    sh:targetClass ex:Person ;
    sh:property [
        sh:path     ex:hasPet ;
        sh:class    ex:Animal ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
.

And the following data graph:

# data.ttl

@prefix ex: <http://example.com/ns#> .

ex:Brutus a ex:Dog .

ex:Jane a ex:Person ; ex:hasPet ex:Brutus .

The following code prints a report that says Conforms: False because of a violation of the sh:class constraint:

from rdflib import Graph
import pyshacl

shapes_graph = Graph()
shapes_graph.parse("shapes.ttl")

data_graph = Graph()
data_graph.parse("data.ttl")

_, _, results_text = pyshacl.validate(data_graph, shacl_graph=shapes_graph)
print(results_text)

Is this expected behaviour?

As I understand the SHACL spec, this example should be valid, because:

  1. ex:Animal is a SHACL superclass of ex:Dog because ex:Dog rdfs:subClassOf ex:Animal
  2. ex:Animal is in the set of SHACL types of ex:Brutus, because the latter has rdf:type ex:Dog and (1)
  3. ex:Brutus is a SHACL instance of ex:Animal, because one of its SHACL types is ex:Animal
  4. Because of (3), the sh:class constraint should be fulfilled

Is this a bug in pySHACL or am I missing something here?

I'm using pySHACL 0.19.0 and Python 3.9.

@ashleysommer
Copy link
Collaborator

ashleysommer commented Apr 9, 2022

Hi @morth
The reason your example is not working is simple.
Your RDFS relationship (rdfs:subClassOf) is not in the data graph.

PySHACL only evaluates the contents of the data graph at runtime. The sh:class constraint does follow rdfs:subClassOf relationships, but only if they exist in the data graph.

Your rdfs and owl definitions in the SHACL Shapes file do not affect the data in the datagraph.

ex:Person a owl:Class .
ex:Animal a owl:Class .
ex:Dog    a owl:Class ; rdfs:subClassOf ex:Animal .

These do not need to be in the SHACL Shapes file, but they do need to be known to the datagraph at runtime.

If it is not possible to have the owl and rdfs ontological definitions in the datagraph (eg, if the data in the datagraph is pulled from a separate closed system), then you can use PySHACLs 3-file method. Where you have the Shacl Shape file, and Extra ontology file, and the data graph.
In this mode, PySHACL will "mix-in" the contents of the extra-ontology file into the datagraph before validation. The feature is intended to solve exactly this problem.

For example, this works:

import rdflib
from pyshacl import validate

shacl_file = """\
@prefix owl:    <http://www.w3.org/2002/07/owl#> .
@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh:     <http://www.w3.org/ns/shacl#> .
@prefix ex:     <http://example.com/ns#> .

ex:PersonShape
    a sh:NodeShape ;
    sh:targetClass ex:Person ;
    sh:property [
        sh:path     ex:hasPet ;
        sh:class    ex:Animal ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
.
"""

ont_file = """\
@prefix owl:    <http://www.w3.org/2002/07/owl#> .
@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ex:     <http://example.com/ns#> .

ex:Person a owl:Class .
ex:Animal a owl:Class .
ex:Dog    a owl:Class ; rdfs:subClassOf ex:Animal .
"""

data_file = """\
@prefix ex: <http://example.com/ns#> .
ex:Brutus a ex:Dog .
ex:Jane a ex:Person ; ex:hasPet ex:Brutus .
"""

data = rdflib.Graph()
data.parse(data=data_file, format="turtle")
shapes = rdflib.Graph()
shapes.parse(data=shacl_file, format="turtle")
ont = rdflib.Graph()
ont.parse(data=ont_file, format="turtle")
res = validate(data, shacl_graph=shapes, ont_graph=ont)
conforms, graph, string = res

@morth
Copy link
Author

morth commented Apr 9, 2022

Hey Ashley,

that makes sense, thank you very much for the clarification. I actually read about ont_graph when I dealt with pySHACL in the first place, but totally forgot about it in the meantime. Putting the OWL axioms into the data graph should also be fine in my use case.

Thanks again!
Moritz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants