Skip to content

Commit

Permalink
refactoring KnowledgeBase
Browse files Browse the repository at this point in the history
  • Loading branch information
alkidbaci committed Jan 2, 2025
1 parent 3a1b063 commit 55c1484
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 259 deletions.
4 changes: 2 additions & 2 deletions examples/example_knowledge_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ def example(args):
print(i)
print('*' * 100)
# All individuals.
for i in kb.all_individuals_set():
for i in kb.individuals():
print(i)
print('*' * 100)
# Count of individuals for each class
for i in kb.get_concepts():
print(f'{i} ==> {kb.individuals_count(i)}')
print('*' * 100)
# IRIs of all individuals.
for i in kb.all_individuals_set():
for i in kb.individuals():
print(i.str)
print('*' * 100)
# Direct concept hierarchy from Top to Bottom.
Expand Down
2 changes: 1 addition & 1 deletion examples/litserve_retrieval_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def generate_concepts_from_kb(symbolic_kb: KnowledgeBase):
# (8) NC*: NC UNION NC⁻.
nc_star = nc.union(nnc)
# (9) Retrieve 10 random Nominals.
nominals = symbolic_kb.all_individuals_set()
nominals = symbolic_kb.individuals()
# (10) All combinations of 3 for Nominals, e.g. {martin, heinz, markus}
nominal_combinations = set( OWLObjectOneOf(combination)for combination in itertools.combinations(nominals, 3))
# (13) NC* UNION NC*.
Expand Down
6 changes: 3 additions & 3 deletions examples/retrieval_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ def execute(args):
# (8) NC*: NC UNION NC⁻.
nc_star = nc.union(nnc)
# (9) Retrieve 10 random Nominals.
if len(symbolic_kb.all_individuals_set())>args.num_nominals:
nominals = set(random.sample(symbolic_kb.all_individuals_set(), args.num_nominals))
if len(symbolic_kb.individuals())>args.num_nominals:
nominals = set(random.sample(symbolic_kb.individuals(), args.num_nominals))
else:
nominals = symbolic_kb.all_individuals_set()
nominals = symbolic_kb.individuals()
# (10) All combinations of 3 for Nominals, e.g. {martin, heinz, markus}
nominal_combinations = set( OWLObjectOneOf(combination)for combination in itertools.combinations(nominals, 3))

Expand Down
4 changes: 2 additions & 2 deletions ontolearn/concept_learner.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
LengthOrderedNode, \
QualityOrderedNode, EvaluatedConcept
from ontolearn.utils import oplogging
from ontolearn.utils.static_funcs import init_length_metric, compute_tp_fn_fp_tn
from ontolearn.utils.static_funcs import init_length_metric, compute_tp_fn_fp_tn, evaluate_concept
from ontolearn.value_splitter import AbstractValueSplitter, BinningValueSplitter, EntropyValueSplitter
from ontolearn.base_nces import BaseNCES
from ontolearn.nces_architectures import LSTM, GRU, SetTransformer
Expand Down Expand Up @@ -489,7 +489,7 @@ def _fitness_func(self, individual: Tree):
individual.fitness.values = (self._cache[ind_str][1],)
else:
concept = gp.compile(individual, self.pset)
e = self.kb.evaluate_concept(concept, self.quality_func, self._learning_problem)
e = evaluate_concept(self.kb, concept, self.quality_func, self._learning_problem)
individual.quality.values = (e.q,)
self.fitness_func.apply(individual)
self._cache[ind_str] = (e.q, individual.fitness.values[0])
Expand Down
264 changes: 22 additions & 242 deletions ontolearn/knowledge_base.py

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion ontolearn/learners/celoe.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from itertools import islice
from owlapy.render import DLSyntaxObjectRenderer

from ..utils.static_funcs import evaluate_concept

_concept_operand_sorter = ConceptOperandSorter()

class CELOE(RefinementBasedConceptLearner):
Expand Down Expand Up @@ -243,7 +245,7 @@ def _add_node(self, ref: OENode, tree_parent: Optional[TreeNode[OENode]]):
self._seen_norm_concepts.add(norm_concept)

self.search_tree[ref.concept] = TreeNode(ref, tree_parent, is_root=ref.is_root)
e = self.kb.evaluate_concept(ref.concept, self.quality_func, self._learning_problem)
e = evaluate_concept(self.kb, ref.concept, self.quality_func, self._learning_problem)

ref.quality = e.q
self._number_of_tested_concepts += 1
Expand Down
53 changes: 51 additions & 2 deletions ontolearn/learning_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@

"""Learning problem in Ontolearn."""
import logging
import random
from typing import Set, Optional, TYPE_CHECKING

from owlapy.render import DLSyntaxObjectRenderer

if TYPE_CHECKING:
from ontolearn.knowledge_base import KnowledgeBase
from ontolearn.abstracts import AbstractLearningProblem, EncodedLearningProblem, EncodedPosNegLPStandardKind
Expand Down Expand Up @@ -97,9 +100,55 @@ def __init__(self,
else:
self.all = frozenset(all_instances)

def encode_kb(self, knowledge_base: 'KnowledgeBase') -> EncodedPosNegLPStandard:
return knowledge_base.encode_learning_problem(self)
# def encode_kb(self, knowledge_base: 'KnowledgeBase') -> EncodedPosNegLPStandard:
# return knowledge_base.encode_learning_problem(self)

def encode_learning_problem(self, kb: 'KnowledgeBase') -> EncodedPosNegLPStandard:
"""
Provides the encoded learning problem (lp), i.e. the class containing the set of OWLNamedIndividuals
as follows:
kb_pos --> the positive examples set,
kb_neg --> the negative examples set,
kb_all --> all lp individuals / all individuals set,
kb_diff --> kb_all - (kb_pos + kb_neg).
Args:
kb (PosNegLPStandard): The knowledge base to encode the learning problem.
Return:
EncodedPosNegLPStandard: The encoded learning problem.
"""
if self.all is None:
kb_all = kb.individuals()
else:
kb_all = kb.individuals_set(self.all)

assert 0 < len(self.pos) < len(kb_all) and len(kb_all) > len(self.neg)
if logger.isEnabledFor(logging.INFO):
r = DLSyntaxObjectRenderer()
logger.info('E^+:[ {0} ]'.format(', '.join(map(r.render, self.pos))))
logger.info('E^-:[ {0} ]'.format(', '.join(map(r.render, self.neg))))

kb_pos = kb.individuals_set(self.pos)
if len(self.neg) == 0: # if negatives are not provided, randomly sample.
kb_neg = type(kb_all)(random.sample(list(kb_all), len(kb_pos)))
else:
kb_neg = kb.individuals_set(self.neg)

try:
assert len(kb_pos) == len(self.pos)
except AssertionError:
print(self.pos)
print(kb_pos)
print(kb_all)
print('Assertion error. Exiting.')
raise
if self.neg:
assert len(kb_neg) == len(self.neg)

return EncodedPosNegLPStandard(
kb_pos=kb_pos,
kb_neg=kb_neg,
kb_all=kb_all,
kb_diff=kb_all.difference(kb_pos.union(kb_neg)))

class EncodedPosNegUndLP(EncodedLearningProblem):
"""To be implemented."""
Expand Down
2 changes: 1 addition & 1 deletion ontolearn/refinement_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def refine_atomic_concept(self, ce: OWLClass, max_length: int,
# TODO probably not correct/complete
if max_length >= 2 and (self.len(ce) + 1 <= self.max_child_length):
# (2.2) Create negation of all leaf_concepts
iter_container.append(self.generator.negation_from_iterables(self.kb.get_leaf_concepts(ce)))
iter_container.append(self.generator.negation_from_iterables(self.kb.class_hierarchy.leaves(of=ce)))

if max_length >= 3 and (self.len(ce) + 2 <= self.max_child_length):
# (2.3) Create ∀.r.T and ∃.r.T where r is the most general relation.
Expand Down
28 changes: 26 additions & 2 deletions ontolearn/utils/static_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
from owlapy.utils import OWLClassExpressionLengthMetric, LRUCache
import traceback
from tqdm import tqdm

from ontolearn.abstracts import EncodedLearningProblem, AbstractScorer
from ontolearn.search import EvaluatedConcept
from typing import Set, Iterable
from owlapy.class_expression import (
OWLQuantifiedObjectRestriction,
Expand Down Expand Up @@ -412,4 +413,27 @@ def verbalize(predictions_file_path: str): # pragma: no cover
elif len(complex_concepts) == 1:
print("Image generated successfully!")
else:
print("Images generated successfully!")
print("Images generated successfully!")


def evaluate_concept(kb: 'KnowledgeBase', concept: OWLClassExpression, quality_func: AbstractScorer,
encoded_learning_problem: EncodedLearningProblem) -> EvaluatedConcept:
"""Evaluates a concept by using the encoded learning problem examples, in terms of Accuracy or F1-score.
Note:
This method is useful to tell the quality (e.q) of a generated concept by the concept learners, to get
the set of individuals (e.inds) that are classified by this concept and the amount of them (e.ic).
Args:
kb: The knowledge base where to evaluate the concept.
concept: The concept to be evaluated.
quality_func: Quality measurement in terms of Accuracy or F1-score.
encoded_learning_problem: The encoded learning problem.
Return:
The evaluated concept.
"""

e = EvaluatedConcept()
e.inds = kb.individuals_set(concept)
e.ic = len(e.inds)
_, e.q = quality_func.score_elp(e.inds, encoded_learning_problem)
return e
2 changes: 1 addition & 1 deletion tests/test_concept.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_concept():
ic = kb.individuals_count(cls)
assert ic > 0
inds = kb.individuals_set(cls)
assert inds.issubset(kb.all_individuals_set())
assert inds.issubset(kb.individuals())


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions tests/test_knowledge_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ def test_reading_data(self):
print(i)
print('*' * 100)
# All individuals.
for i in kb.all_individuals_set():
for i in kb.individuals():
print(i)
print('*' * 100)
# Count of individuals for each class
for i in kb.get_concepts():
print(f'{i} ==> {kb.individuals_count(i)}')
print('*' * 100)
# IRIs of all individuals.
for i in kb.all_individuals_set():
for i in kb.individuals():
print(i.str)
print('*' * 100)
# Direct concept hierarchy from Top to Bottom.
Expand Down

0 comments on commit 55c1484

Please sign in to comment.