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

Adding optimized SHACL shapes to reduce inference time #316

Merged
merged 9 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ format:
black tools/

test: Brick.ttl shacl/BrickShape.ttl
pytest -s -vvvv -m 'not slow' tests
pytest -s -vvvv -m 'not slow' --durations=0 tests
cd tests/integration && bash run_integration_tests.sh

quantity-test: Brick.ttl
Expand Down
2 changes: 1 addition & 1 deletion bricksrc/equipment.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
},
},
"Fire_Safety_Equipment": {
"tags": [TAG.Equipment, TAG.Fire, TAG.Safety, TAG.Equipment],
"tags": [TAG.Equipment, TAG.Fire, TAG.Safety],
"subclasses": {
"Fire_Control_Panel": {
"tags": [TAG.Equipment, TAG.Fire, TAG.Safety, TAG.Panel],
Expand Down
27 changes: 17 additions & 10 deletions generate_brick.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,22 +189,29 @@ def add_tags(klass, definition):
num_tags = len(definition)
if len(definition) not in has_exactly_n_tags_shapes:
# tag count condition
cond = BNode(f"has_exactly_{num_tags}_tags_condition")
cond = BSH[f"has_exactly_{num_tags}_tags_condition"]
prop = BNode(f"has_exactly_{num_tags}_tags")
shaclGraph.add((cond, A, OWL.Class))
shaclGraph.add((cond, A, SH.NodeShape))
shaclGraph.add((cond, SH.property, prop))
shaclGraph.add((prop, SH.path, BRICK.hasTag))
shaclGraph.add((prop, SH.minCount, Literal(len(definition))))
shaclGraph.add((prop, SH.maxCount, Literal(len(definition))))
has_exactly_n_tags_shapes[len(definition)] = cond
shaclGraph.add((rule, SH.condition, has_exactly_n_tags_shapes[len(definition)]))

# ensure that the rule applies to at least one of the base tags that should be on
# most Brick classes
# base_tags = [TAG.Equipment, TAG.Point, TAG.Location, TAG.System, TAG.Solid, TAG.Fluid]
# target_class_tag = [t for t in base_tags if t in definition]
# assert len(target_class_tag) > 0, klass
# shaclGraph.add((sc, SH.targetClass, has_tag_restriction_class[target_class_tag[0]]))
shaclGraph.add((sc, SH.targetSubjectsOf, BRICK.hasTag))

# generate inference rule
rule = BSH[f"has_exactly_{num_tags}_tags_rule"]
body = BNode(f"has_{num_tags}_tags_body")
shaclGraph.add((rule, A, SH.NodeShape))
shaclGraph.add((rule, SH.targetSubjectsOf, BRICK.hasTag))
shaclGraph.add((rule, SH.rule, body))
shaclGraph.add((body, A, SH.TripleRule))
shaclGraph.add((body, SH.subject, SH.this))
shaclGraph.add((body, SH.predicate, RDF.type))
shaclGraph.add((body, SH.object, cond))
shaclGraph.add((body, SH.condition, cond))

shaclGraph.add((sc, SH.targetClass, has_exactly_n_tags_shapes[len(definition)]))

# if we've already mapped this class, don't map it again
if klass in intersection_classes:
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ rdflib-jsonld==0.5.0
six==1.12.0
pytest>=5.4.3
tqdm>=4.46.1
pyshacl>=0.15.0
pyshacl>=0.16.1.post1
docker>=4.3.0
brickschema>=0.5.0a1
brickschema>=0.5.1a1
black==21.5b2
pre-commit>=2.13.0
flake8==3.9.2
Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ def pytest_generate_tests(metafunc):
example_files = glob.glob("examples/*/*.ttl")
# example_files = set(example_files_1 + example_files_2)
metafunc.parametrize("filename", example_files)

def pytest_configure(config):
config.addinivalue_line(
"markers", "slow: mark tests as slow (deselect w/ '-m \"not slow\"')"
)
15 changes: 10 additions & 5 deletions tests/test_hierarchy_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,16 @@ def test_hierarchyinference():
) # This is based on how the entity name is defined above.

# Find the original classes through the hierarchy from the original graph.
qstr = """
select ?parent where {{
<{0}> rdfs:subClassOf* ?parent.
?parent rdfs:subClassOf* brick:Class.
}}""".format(
qstr = """select ?parent where {{
{{
<{0}> owl:equivalentClass*/rdfs:subClassOf*/owl:equivalentClass*/rdfs:subClassOf* ?parent .
}} UNION {{
?other owl:equivalentClass* <{0}> .
?other rdfs:subClassOf*/owl:equivalentClass*/rdfs:subClassOf* ?parent .
}}
?parent rdfs:subClassOf* brick:Class
}}
""".format(
true_class
)
res = g.query(qstr)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_tag_inference():

# Apply reasoner
g.load_file("extensions/brick_extension_shacl_tag_inference.ttl")
g.expand(profile="owlrl+shacl+owlrl")
g.expand(profile="shacl+owlrl+shacl+owlrl")

g.bind("rdf", RDF)
g.bind("owl", OWL)
Expand Down
11 changes: 8 additions & 3 deletions tests/test_measures_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_measures_infers():

# Infer classes of the entities.
# Apply reasoner
g.expand(profile="owlrl")
g.expand(profile="brick")

qstr = """select ?instance ?class where {
?instance a ?class.
Expand Down Expand Up @@ -94,8 +94,13 @@ def test_measures_infers():

# Find the original classes through the hierarchy from the original graph.
qstr = """select ?parent where {{
<{0}> rdfs:subClassOf* ?parent.
?parent rdfs:subClassOf* brick:Class.
{{
<{0}> owl:equivalentClass*/rdfs:subClassOf*/owl:equivalentClass*/rdfs:subClassOf* ?parent .
}} UNION {{
?other owl:equivalentClass* <{0}> .
?other rdfs:subClassOf*/owl:equivalentClass*/rdfs:subClassOf* ?parent .
}}
?parent rdfs:subClassOf* brick:Class
}}
""".format(
true_class
Expand Down