Break a graph and repair it¶
In this tutorial you break a small, valid graph on purpose, watch the
validator catch it, and then fix it with the edits that broke it. You need
pip install "pyshifty[rdflib]".
The graph and the shapes¶
Two sensors, each in a room. The shapes say every sensor needs one string
label, a unit of "degC" or "degF", and a room that is an
ex:Room:
SHAPES = """
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://ex/> .
ex:SensorShape a sh:NodeShape ;
sh:targetClass ex:Sensor ;
sh:property ex:label, ex:unit, ex:room .
ex:label sh:path ex:label ; sh:datatype xsd:string ; sh:minCount 1 ; sh:maxCount 1 .
ex:unit sh:path ex:unit ; sh:in ( "degC" "degF" ) .
ex:room sh:path ex:inRoom ; sh:class ex:Room ; sh:minCount 1 .
"""
DATA = """
@prefix ex: <http://ex/> .
ex:s1 a ex:Sensor ; ex:label "Temp 1" ; ex:unit "degC" ; ex:inRoom ex:r1 .
ex:s2 a ex:Sensor ; ex:label "Temp 2" ; ex:unit "degF" ; ex:inRoom ex:r2 .
ex:r1 a ex:Room .
ex:r2 a ex:Room .
"""
Each property shape has an IRI (ex:label, ex:unit, ex:room), so
the validation reports below can name them.
1. Check that the graph conforms¶
vio only breaks graphs that conform. If a node already fails a shape,
vio leaves it alone. So check first:
import shifty
conforms, _, text = shifty.validate(DATA, SHAPES)
print(conforms)
True
2. Draw a case¶
A Universe holds every way vio found to break the graph. Each way
is an injection: a few edits that make one node fail one shape.
draw(2, seed=0) picks up to two injections that can be applied
together:
from shifty.experimental import vio
universe = vio.Universe(DATA, SHAPES)
print(len(universe), "ways to break the graph")
case = universe.draw(2, seed=0)
for injection in case.injections:
print(injection.focus, injection.operations[0].detail)
38 ways to break the graph
<http://ex/s1> remove type Room
<http://ex/s2> add r1 via unit
ex:r1 loses its type, so ex:s1 now points at something that is not
a room. ex:s2 gets a unit that is not allowed.
The same seed gives the same case every time. Try another seed to see a different case.
3. Read the edits¶
case.delta holds the edits, as triples to delete and to add:
for s, p, o in sorted(case.delta.delete):
print("-", s, p, o)
for s, p, o in sorted(case.delta.add):
print("+", s, p, o)
- <http://ex/r1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://ex/Room>
+ <http://ex/s2> <http://ex/unit> <http://ex/r1>
4. Validate the broken graph¶
case.graph() is the original graph with those edits applied. Validate
it:
broken = case.graph()
conforms, _, text = shifty.validate(broken, SHAPES)
print(text)
Validation Report
Conforms: False
Results (2):
Constraint Violation in ClassConstraintComponent
Severity: sh:Violation
Source Shape: <http://ex/room>
Focus Node: <http://ex/s1>
Result Path: <http://ex/inRoom>
Value: <http://ex/r1>
Message: Value <http://ex/r1> is not an instance of class <http://ex/Room>
Constraint Violation in InConstraintComponent
Severity: sh:Violation
Source Shape: <http://ex/unit>
Focus Node: <http://ex/s2>
Result Path: <http://ex/unit>
Value: <http://ex/r1>
Message: Value <http://ex/r1> is not in the list of allowed values
The case already knows which nodes should fail. Compare
case.failures:
for failure in case.failures:
print(failure.focus, failure.shape)
<http://ex/s1> http://ex/SensorShape
<http://ex/s2> http://ex/SensorShape
The two agree. Every failure a case lists has been confirmed this way.
5. Repair it¶
case.repair undoes case.delta. case.apply applies a set of
edits to the broken graph:
repaired = case.apply(case.repair)
conforms, _, text = shifty.validate(repaired, SHAPES)
print(conforms)
True
The graph conforms again.
To score a repair tool, apply the tool’s edits instead of case.repair,
and compare the result with the original:
candidate = case.apply(tool_delta)
intended = case.apply(case.repair)
Next steps¶
Build a repair benchmark: build a suite from your own model.
How violation generation works: how injections are found and combined.
Violation generation reference: every class and option.