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

Refactor structural reasoner #100

Merged
merged 4 commits into from
Nov 8, 2024
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
102 changes: 29 additions & 73 deletions owlapy/owl_reasoner.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
OWLPropertyExpression, OWLDataPropertyExpression
from owlapy.owl_individual import OWLNamedIndividual
from owlapy.owl_literal import OWLLiteral
from owlapy.utils import LRUCache, run_with_timeout
from owlapy.utils import run_with_timeout
from owlapy.abstracts.abstract_owl_reasoner import AbstractOWLReasoner
logger = logging.getLogger(__name__)

Expand All @@ -36,37 +36,6 @@

class StructuralReasoner(AbstractOWLReasoner):
"""Tries to check instances fast (but maybe incomplete)."""
__slots__ = '_ontology', '_world', \
'_ind_set', '_cls_to_ind', \
'_has_prop', 'class_cache', \
'_objectsomevalues_cache', '_datasomevalues_cache', '_objectcardinality_cache', \
'_property_cache', \
'_obj_prop', '_obj_prop_inv', '_data_prop', \
'_negation_default', '_sub_properties', \
'__warned'

_ontology: Ontology
_world: owlready2.World
_cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] # Class => individuals
_has_prop: Mapping[Type[_P], LRUCache[_P, FrozenSet[OWLNamedIndividual]]] # Type => Property => individuals
_ind_set: FrozenSet[OWLNamedIndividual]
# ObjectSomeValuesFrom => individuals
_objectsomevalues_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]]
# DataSomeValuesFrom => individuals
_datasomevalues_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]]
# ObjectCardinalityRestriction => individuals
_objectcardinality_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]]
# ObjectProperty => { individual => individuals }
_obj_prop: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]]
# ObjectProperty => { individual => individuals }
_obj_prop_inv: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]]
# DataProperty => { individual => literals }
_data_prop: Dict[OWLDataProperty, Mapping[OWLNamedIndividual, Set[OWLLiteral]]]
class_cache: bool
_property_cache: bool
_negation_default: bool
_sub_properties: bool

def __init__(self, ontology: AbstractOWLOntology, *, class_cache: bool = True,
property_cache: bool = True, negation_default: bool = True, sub_properties: bool = False):
"""Fast instance checker.
Expand All @@ -80,36 +49,34 @@ def __init__(self, ontology: AbstractOWLOntology, *, class_cache: bool = True,
"""
super().__init__(ontology)
assert isinstance(ontology, Ontology)
self._world = ontology._world
self._ontology = ontology
self.class_cache = class_cache
self._property_cache = property_cache
self._negation_default = negation_default
self._sub_properties = sub_properties
self.__warned = 0
self._world: owlready2.World = ontology._world
self._ontology: Ontology = ontology
self.class_cache: bool = class_cache
self._property_cache: bool = property_cache
self._negation_default: bool = negation_default
self._sub_properties: bool = sub_properties
self.__warned: int = 0
self._init()

def _init(self, cache_size=128):

individuals = self._ontology.individuals_in_signature()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see that we are removing the line where we get the individuals once and we are adding individual retrieval on line 606, 647, 767, 803 which will be killing the algo in large KGs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, i just replaced ind_set with the individuals_in_signature() method when ever it is used.
Ig an advantage is that this is only computed if it is really need and not always on initialisation

Copy link
Member

Choose a reason for hiding this comment

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

Let's get the correctness done first, then focus on trading of the memory with runtimes.
It is my mistake that I should have not accept the PR based on caching.

self._ind_set = frozenset(individuals)
self._objectsomevalues_cache = LRUCache(maxsize=cache_size)
self._datasomevalues_cache = LRUCache(maxsize=cache_size)
self._objectcardinality_cache = LRUCache(maxsize=cache_size)
def _init(self):
if self.class_cache:
self._cls_to_ind = dict()
# Class => individuals
self._cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] = {}

if self._property_cache:
self._obj_prop = dict()
self._obj_prop_inv = dict()
self._data_prop = dict()
# ObjectProperty => { individual => individuals }
self._obj_prop: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] = dict()
# ObjectProperty => { individual => individuals }
self._obj_prop_inv: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] = dict()
# DataProperty => { individual => literals }
self._data_prop: Dict[OWLDataProperty, Mapping[OWLNamedIndividual, Set[OWLLiteral]]] = dict()
else:
self._has_prop = MappingProxyType({
OWLDataProperty: LRUCache(maxsize=cache_size),
OWLObjectProperty: LRUCache(maxsize=cache_size),
OWLObjectInverseOf: LRUCache(maxsize=cache_size),
})

self._has_prop: Mapping[Type[_P], Dict[_P, FrozenSet[OWLNamedIndividual]]] = {
OWLDataProperty: {},
OWLObjectProperty: {},
OWLObjectInverseOf: {},
}
def reset(self):
"""The reset method shall reset any cached state."""
self._init()
Expand Down Expand Up @@ -636,7 +603,8 @@ def _lazy_cache_obj_prop(self, pe: OWLObjectPropertyExpression) -> None:
opc[s] = set()
opc[s] |= {o}
else:
for s in self._ind_set:
all_ = frozenset(self._ontology.individuals_in_signature())
for s in all_:
individuals = set(self.object_property_values(s, pe, not self._sub_properties))
if individuals:
opc[s] = individuals
Expand Down Expand Up @@ -676,8 +644,8 @@ def _some_values_subject_index(self, pe: OWLPropertyExpression) -> FrozenSet[OWL
func = self.data_property_values
else:
func = self.object_property_values

for s in self._ind_set:
all_ = frozenset(self._ontology.individuals_in_signature())
for s in all_:
try:
next(iter(func(s, pe, not self._sub_properties)))
subs |= {s}
Expand Down Expand Up @@ -782,9 +750,6 @@ def _(self, ce: OWLObjectIntersectionOf) -> FrozenSet[OWLNamedIndividual]:

@_find_instances.register
def _(self, ce: OWLObjectSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]:
if ce in self._objectsomevalues_cache:
return self._objectsomevalues_cache[ce]

p = ce.get_property()
assert isinstance(p, OWLObjectPropertyExpression)
if not self._property_cache and ce.get_filler().is_owl_thing():
Expand All @@ -794,13 +759,12 @@ def _(self, ce: OWLObjectSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]:

ind = self._find_some_values(p, filler_ind)

self._objectsomevalues_cache[ce] = ind
return ind

@_find_instances.register
def _(self, ce: OWLObjectComplementOf) -> FrozenSet[OWLNamedIndividual]:
if self._negation_default:
all_ = self._ind_set
all_ = frozenset(self._ontology.individuals_in_signature())
complement_ind = self._find_instances(ce.get_operand())
return all_ ^ complement_ind
else:
Expand Down Expand Up @@ -836,7 +800,7 @@ def _(self, ce: OWLObjectMinCardinality) -> FrozenSet[OWLNamedIndividual]:

@_find_instances.register
def _(self, ce: OWLObjectMaxCardinality) -> FrozenSet[OWLNamedIndividual]:
all_ = self._ind_set
all_ = frozenset(self._ontology.individuals_in_signature())
min_ind = self._find_instances(OWLObjectMinCardinality(cardinality=ce.get_cardinality() + 1,
property=ce.get_property(),
filler=ce.get_filler()))
Expand All @@ -847,9 +811,6 @@ def _(self, ce: OWLObjectExactCardinality) -> FrozenSet[OWLNamedIndividual]:
return self._get_instances_object_card_restriction(ce)

def _get_instances_object_card_restriction(self, ce: OWLObjectCardinalityRestriction):
if ce in self._objectcardinality_cache:
return self._objectcardinality_cache[ce]

p = ce.get_property()
assert isinstance(p, OWLObjectPropertyExpression)

Expand All @@ -871,14 +832,10 @@ def _get_instances_object_card_restriction(self, ce: OWLObjectCardinalityRestric

ind = self._find_some_values(p, filler_ind, min_count=min_count, max_count=max_count)

self._objectcardinality_cache[ce] = ind
return ind

@_find_instances.register
def _(self, ce: OWLDataSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]:
if ce in self._datasomevalues_cache:
return self._datasomevalues_cache[ce]

pe = ce.get_property()
filler = ce.get_filler()
assert isinstance(pe, OWLDataProperty)
Expand Down Expand Up @@ -967,7 +924,6 @@ def include(lv: OWLLiteral):
raise ValueError

r = frozenset(ind)
self._datasomevalues_cache[ce] = r
return r

@_find_instances.register
Expand Down
Loading