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

Improved representation of blank nodes #283

Merged
merged 4 commits into from
Dec 3, 2021
Merged
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
95 changes: 71 additions & 24 deletions ontopy/ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
from owlready2.entity import ThingClass

from ontopy.factpluspluswrapper.sync_factpp import sync_reasoner_factpp

from ontopy.utils import asstring, read_catalog, infer_version, convert_imported
from ontopy.utils import (
asstring,
read_catalog,
infer_version,
convert_imported,
FMAP,
IncompatibleVersion,
isinteractive,
Expand Down Expand Up @@ -116,6 +118,27 @@ def get_ontology(self, base_iri="emmo-inferred"):

return onto

def get_unabbreviated_triples(self, label=None):
"""Returns all triples unabbreviated.

If `label` is given, it will be used to represent blank nodes.
"""

def _unabbreviate(i):
if isinstance(i, int):
# negative storid corresponds to blank nodes
if i >= 0:
return self._unabbreviate(i)
return BlankNode(self, i) if label is None else label
return i

for subject, predicate, obj in self.get_triples():
yield (
_unabbreviate(subject),
_unabbreviate(predicate),
_unabbreviate(obj),
)


class Ontology( # pylint: disable=too-many-public-methods
owlready2.Ontology, OntoGraph
Expand Down Expand Up @@ -202,38 +225,29 @@ def __objclass__(self):
pass

def __hash__(self):
"""Returns hash based on base_iri
"""Returns a hash based on base_iri.
This is done to keep Ontology hashable when defining __eq__.
"""
return hash(self.base_iri)

def __eq__(self, other):
"""Checks if this ontology is equal to other.
"""Checks if this ontology is equal to `other`.

Equality of all triples obtained from self.get_unabbreviated_triples(),
i.e. blank nodes are not distinguished, but relations
to blank nodes are included.
This function compares the result of
``set(self.get_unabbreviated_triples(label='_:b'))``,
i.e. blank nodes are not distinguished, but relations to blank
nodes are included.
"""
return set(self.get_unabbreviated_triples()) == set(
other.get_unabbreviated_triples()
return set(self.get_unabbreviated_triples(label="_:b")) == set(
other.get_unabbreviated_triples(label="_:b")
)

def get_unabbreviated_triples(self):
"""Returns all triples unabbreviated"""
def get_unabbreviated_triples(self, label=None):
"""Returns all triples unabbreviated.

def _unabbreviate(i):
if isinstance(i, int):
if i >= 0:
return self._unabbreviate(i)
return "_:" # blank nodes are given random neg. storid
return i

for subject, predicate, obj in self.get_triples():
yield (
_unabbreviate(subject),
_unabbreviate(predicate),
_unabbreviate(obj),
)
If `label` is given, it will be used to represent blank nodes.
"""
return World.get_unabbreviated_triples(self, label)

def get_by_label(
self, label, label_annotations=None, namespace=None
Expand Down Expand Up @@ -1259,3 +1273,36 @@ def new_entity(
with self:
entity = types.new_class(name, parents)
return entity


class BlankNode:
"""Represents a blank node.

A blank node is a node that is not a literal and has no IRI.
Resources represented by blank nodes are also called anonumous resources.
Only the subject or object in an RDF triple can be a blank node.
"""

def __init__(self, onto: Union[World, Ontology], storid: int):
"""Initiate a blank node.

Args:
onto: Ontology or World instance.
storid: The storage id of the blank node.
"""
if storid >= 0:
raise ValueError(
f"A BlankNode is supposed to have a negative storid: {storid}"
)
self.onto = onto
self.storid = storid

def __repr__(self):
return repr(f"_:b{-self.storid}")

def __hash__(self):
return hash((self.onto, self.storid))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return hash((self.onto, self.storid))
return hash(tuple(getattr(self, _) for _ in sorted(self.__dict__))

To make sure all attributes (and future attributes) are always given to the hash-function in an alphabetically sorted tuple of attribute values.


def __eq__(self, other):
"""For now blank nodes always compare true against each other."""
return isinstance(other, BlankNode)