Repository: dice-group/Ontolearn (language: python)

Issue:
Owlapy 1.3.3
- owlapy version set to 1.3.3
- Added regression tests for `retrieval_eval.py` and `retrieval_eval_under_incomplete.py`
- Documentation published only on new release (merge with master) and pushes on `documentation` branch for typo fixing etc.
- Ontolearn new release &rarr; version increased to 0.8.1
Incomplete population of classes_in_signature 
```py
    def classes_in_signature(
            self, confidence_threshold: float = None) -> Generator[OWLClass, None, None]:
        if self.inferred_named_owl_classes is None:
            self.inferred_named_owl_classes = set()
            for prediction in self.get_predictions(
                    h=None,
                    r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
                    t="http://www.w3.org/2002/07/owl#Class",
                    confidence_threshold=confidence_threshold):
                try:
                    owl_class = OWLClass(prediction[0])
                    self.inferred_named_owl_classes.add(owl_class)
                    yield owl_class
                except Exception as e:
                    # Log the invalid IRI
                    print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
                    continue
        else:
            yield from self.inferred_named_owl_classes
```
from  [here](https://github.com/dice-group/Ontolearn/blob/cd03332a4e2417e01453ed5271342486de3df71b/ontolearn/owl_neural_reasoner.py#L453C3-L475C53)

I found that yielding from within the for loop can lead to a not fully populated inferred_named_owl_classes set. 
This leads to max recursion depth error for instance retrieval (not sure why) and incorrect results in general.
The same happens for inidividuals_in_signature and object_properties_in_signature!


I have a fix for this on the branch that implements LRU prediction caching - should I create a PR?





Discussion / hints:

Fix patch:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index abe8130a..28f8dfdb 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -4,7 +4,7 @@ on:
   push:
     branches:
      - master
-     - develop
+     - documentation # only for quick updates or testing purposes
   pull_request:
 
 jobs:
diff --git a/.gitignore b/.gitignore
index 4f3f7d60..68ae0432 100644
--- a/.gitignore
+++ b/.gitignore
@@ -157,6 +157,7 @@ pre_trained_agents.zip
 embeddings.zip
 KGs.zip
 /Fuseki/
+/checkpoints/
 /KGs/
 **/NCESData*
 **/CLIPData*
diff --git a/README.md b/README.md
index ffd436a2..d6fd8382 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 [![Coverage](https://img.shields.io/badge/coverage-86%25-green)](https://ontolearn-docs-dice-group.netlify.app/usage/09_further_resources#code-coverage)
-[![Pypi](https://img.shields.io/badge/pypi-0.8.0-blue)](https://pypi.org/project/ontolearn/0.8.0/)
-[![Docs](https://img.shields.io/badge/documentation-0.8.0-yellow)](https://ontolearn-docs-dice-group.netlify.app/usage/01_introduction)
+[![Pypi](https://img.shields.io/badge/pypi-0.8.1-blue)](https://pypi.org/project/ontolearn/0.8.1/)
+[![Docs](https://img.shields.io/badge/documentation-0.8.1-yellow)](https://ontolearn-docs-dice-group.netlify.app/usage/01_introduction)
 [![Python](https://img.shields.io/badge/python-3.10.13+-4584b6)](https://www.python.org/downloads/release/python-31013/)
 &nbsp;
 
diff --git a/docs/usage/01_introduction.md b/docs/usage/01_introduction.md
index 3e9c8f33..92ca860c 100644
--- a/docs/usage/01_introduction.md
+++ b/docs/usage/01_introduction.md
@@ -1,6 +1,6 @@
 # About Ontolearn
 
-**Version:** ontolearn 0.8.0
+**Version:** ontolearn 0.8.1
 
 **GitHub repository:** [https://github.com/dice-group/Ontolearn](https://github.com/dice-group/Ontolearn)
 
diff --git a/docs/usage/06_concept_learners.md b/docs/usage/06_concept_learners.md
index 8631584b..7c4bb382 100644
--- a/docs/usage/06_concept_learners.md
+++ b/docs/usage/06_concept_learners.md
@@ -363,28 +363,4 @@ you pass this url to `triplestore_address` argument, you have to add the
 
 You can now create a triplestore knowledge base or a reasoner that uses this URL for their 
 operations:
-
-```python
-from ontolearn.triple_store import TripleStoreKnowledgeBase
-
-father_kb = TripleStoreKnowledgeBase("http://localhost:3030/father/sparql")
-
-# ** Continue to execute the learning algorithm as you normally do. ** .
-```
-
--------------------------------------------------------------------
-
-
-In this guide, we have shown the prerequisites of running a concept learner,
-how to configure it's input properties and how to run it to successfully
-learn class expressions for learning problems in an ontology. We showed as well how to set up
-a triplestore server that can be used to execute the concept learner. There is also a jupyter 
-notebook for each of these concept learners:
-
-- [NCES notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/simple-usage-NCES.ipynb)
-- [CLIP notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/clip_notebook.ipynb)
-- [DRILL notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/drill_notebook.ipynb)
-- [EvoLearner notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/evolearner_notebook.ipynb)
-- [CELOE notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/celoe_notebook.ipynb)
-- [OCEL notebook](https://github.com/dice-group/Ontolearn/blob/develop/examples/ocel_notebook.ipynb)
-- [TDL example](https://github.com/dice-group/Ontolearn/blob/develop/examples/concept_learning_with_tdl_and_triplestore_kb.py)
+TODO
\ No newline at end of file
diff --git a/examples/carcinogenesis_lp.json b/examples/carcinogenesis_lp.json
deleted file mode 100644
index 9af28753..00000000
--- a/examples/carcinogenesis_lp.json
+++ /dev/null
@@ -1,303 +0,0 @@
-{
-  "data_path": "../KGs/Carcinogenesis/carcinogenesis.owl",
-  "positive_examples": ["http://dl-learner.org/carcinogenesis#d1",
-"http://dl-learner.org/carcinogenesis#d10",
-"http://dl-learner.org/carcinogenesis#d101",
-"http://dl-learner.org/carcinogenesis#d102",
-"http://dl-learner.org/carcinogenesis#d103",
-"http://dl-learner.org/carcinogenesis#d106",
-"http://dl-learner.org/carcinogenesis#d107",
-"http://dl-learner.org/carcinogenesis#d108",
-"http://dl-learner.org/carcinogenesis#d11",
-"http://dl-learner.org/carcinogenesis#d12",
-"http://dl-learner.org/carcinogenesis#d13",
-"http://dl-learner.org/carcinogenesis#d134",
-"http://dl-learner.org/carcinogenesis#d135",
-"http://dl-learner.org/carcinogenesis#d136",
-"http://dl-learner.org/carcinogenesis#d138",
-"http://dl-learner.org/carcinogenesis#d140",
-"http://dl-learner.org/carcinogenesis#d141",
-"http://dl-learner.org/carcinogenesis#d144",
-"http://dl-learner.org/carcinogenesis#d145",
-"http://dl-learner.org/carcinogenesis#d146",
-"http://dl-learner.org/carcinogenesis#d147",
-"http://dl-learner.org/carcinogenesis#d15",
-"http://dl-learner.org/carcinogenesis#d17",
-"http://dl-learner.org/carcinogenesis#d19",
-"http://dl-learner.org/carcinogenesis#d192",
-"http://dl-learner.org/carcinogenesis#d193",
-"http://dl-learner.org/carcinogenesis#d195",
-"http://dl-learner.org/carcinogenesis#d196",
-"http://dl-learner.org/carcinogenesis#d197",
-"http://dl-learner.org/carcinogenesis#d198",
-"http://dl-learner.org/carcinogenesis#d199",
-"http://dl-learner.org/carcinogenesis#d2",
-"http://dl-learner.org/carcinogenesis#d20",
-"http://dl-learner.org/carcinogenesis#d200",
-"http://dl-learner.org/carcinogenesis#d201",
-"http://dl-learner.org/carcinogenesis#d202",
-"http://dl-learner.org/carcinogenesis#d203",
-"http://dl-learner.org/carcinogenesis#d204",
-"http://dl-learner.org/carcinogenesis#d205",
-"http://dl-learner.org/carcinogenesis#d21",
-"http://dl-learner.org/carcinogenesis#d22",
-"http://dl-learner.org/carcinogenesis#d226",
-"http://dl-learner.org/carcinogenesis#d227",
-"http://dl-learner.org/carcinogenesis#d228",
-"http://dl-learner.org/carcinogenesis#d229",
-"http://dl-learner.org/carcinogenesis#d231",
-"http://dl-learner.org/carcinogenesis#d232",
-"http://dl-learner.org/carcinogenesis#d234",
-"http://dl-learner.org/carcinogenesis#d236",
-"http://dl-learner.org/carcinogenesis#d239",
-"http://dl-learner.org/carcinogenesis#d23_2",
-"http://dl-learner.org/carcinogenesis#d242",
-"http://dl-learner.org/carcinogenesis#d245",
-"http://dl-learner.org/carcinogenesis#d247",
-"http://dl-learner.org/carcinogenesis#d249",
-"http://dl-learner.org/carcinogenesis#d25",
-"http://dl-learner.org/carcinogenesis#d252",
-"http://dl-learner.org/carcinogenesis#d253",
-"http://dl-learner.org/carcinogenesis#d254",
-"http://dl-learner.org/carcinogenesis#d255",
-"http://dl-learner.org/carcinogenesis#d26",
-"http://dl-learner.org/carcinogenesis#d272",
-"http://dl-learner.org/carcinogenesis#d275",
-"http://dl-learner.org/carcinogenesis#d277",
-"http://dl-learner.org/carcinogenesis#d279",
-"http://dl-learner.org/carcinogenesis#d28",
-"http://dl-learner.org/carcinogenesis#d281",
-"http://dl-learner.org/carcinogenesis#d283",
-"http://dl-learner.org/carcinogenesis#d284",
-"http://dl-learner.org/carcinogenesis#d288",
-"http://dl-learner.org/carcinogenesis#d29",
-"http://dl-learner.org/carcinogenesis#d290",
-"http://dl-learner.org/carcinogenesis#d291",
-"http://dl-learner.org/carcinogenesis#d292",
-"http://dl-learner.org/carcinogenesis#d30",
-"http://dl-learner.org/carcinogenesis#d31",
-"http://dl-learner.org/carcinogenesis#d32",
-"http://dl-learner.org/carcinogenesis#d33",
-"http://dl-learner.org/carcinogenesis#d34",
-"http://dl-learner.org/carcinogenesis#d35",
-"http://dl-learner.org/carcinogenesis#d36",
-"http://dl-learner.org/carcinogenesis#d37",
-"http://dl-learner.org/carcinogenesis#d38",
-"http://dl-learner.org/carcinogenesis#d42",
-"http://dl-learner.org/carcinogenesis#d43",
-"http://dl-learner.org/carcinogenesis#d44",
-"http://dl-learner.org/carcinogenesis#d45",
-"http://dl-learner.org/carcinogenesis#d46",
-"http://dl-learner.org/carcinogenesis#d47",
-"http://dl-learner.org/carcinogenesis#d48",
-"http://dl-learner.org/carcinogenesis#d49",
-"http://dl-learner.org/carcinogenesis#d5",
-"http://dl-learner.org/carcinogenesis#d51",
-"http://dl-learner.org/carcinogenesis#d52",
-"http://dl-learner.org/carcinogenesis#d53",
-"http://dl-learner.org/carcinogenesis#d55",
-"http://dl-learner.org/carcinogenesis#d58",
-"http://dl-learner.org/carcinogenesis#d6",
-"http://dl-learner.org/carcinogenesis#d7",
-"http://dl-learner.org/carcinogenesis#d84",
-"http://dl-learner.org/carcinogenesis#d85_2",
-"http://dl-learner.org/carcinogenesis#d86",
-"http://dl-learner.org/carcinogenesis#d87",
-"http://dl-learner.org/carcinogenesis#d88",
-"http://dl-learner.org/carcinogenesis#d89",
-"http://dl-learner.org/carcinogenesis#d9",
-"http://dl-learner.org/carcinogenesis#d91",
-"http://dl-learner.org/carcinogenesis#d92",
-"http://dl-learner.org/carcinogenesis#d93",
-"http://dl-learner.org/carcinogenesis#d95",
-"http://dl-learner.org/carcinogenesis#d96",
-"http://dl-learner.org/carcinogenesis#d98",
-"http://dl-learner.org/carcinogenesis#d99",
-"http://dl-learner.org/carcinogenesis#d100",
-"http://dl-learner.org/carcinogenesis#d104",
-"http://dl-learner.org/carcinogenesis#d105",
-"http://dl-learner.org/carcinogenesis#d109",
-"http://dl-learner.org/carcinogenesis#d137",
-"http://dl-learner.org/carcinogenesis#d139",
-"http://dl-learner.org/carcinogenesis#d14",
-"http://dl-learner.org/carcinogenesis#d142",
-"http://dl-learner.org/carcinogenesis#d143",
-"http://dl-learner.org/carcinogenesis#d148",
-"http://dl-learner.org/carcinogenesis#d16",
-"http://dl-learner.org/carcinogenesis#d18",
-"http://dl-learner.org/carcinogenesis#d191",
-"http://dl-learner.org/carcinogenesis#d206",
-"http://dl-learner.org/carcinogenesis#d230",
-"http://dl-learner.org/carcinogenesis#d233",
-"http://dl-learner.org/carcinogenesis#d235",
-"http://dl-learner.org/carcinogenesis#d237",
-"http://dl-learner.org/carcinogenesis#d238",
-"http://dl-learner.org/carcinogenesis#d23_1",
-"http://dl-learner.org/carcinogenesis#d24",
-"http://dl-learner.org/carcinogenesis#d240",
-"http://dl-learner.org/carcinogenesis#d241",
-"http://dl-learner.org/carcinogenesis#d243",
-"http://dl-learner.org/carcinogenesis#d244",
-"http://dl-learner.org/carcinogenesis#d246",
-"http://dl-learner.org/carcinogenesis#d248",
-"http://dl-learner.org/carcinogenesis#d250",
-"http://dl-learner.org/carcinogenesis#d251",
-"http://dl-learner.org/carcinogenesis#d27",
-"http://dl-learner.org/carcinogenesis#d273",
-"http://dl-learner.org/carcinogenesis#d274",
-"http://dl-learner.org/carcinogenesis#d278",
-"http://dl-learner.org/carcinogenesis#d286",
-"http://dl-learner.org/carcinogenesis#d289",
-"http://dl-learner.org/carcinogenesis#d3",
-"http://dl-learner.org/carcinogenesis#d39",
-"http://dl-learner.org/carcinogenesis#d4",
-"http://dl-learner.org/carcinogenesis#d40",
-"http://dl-learner.org/carcinogenesis#d41",
-"http://dl-learner.org/carcinogenesis#d50",
-"http://dl-learner.org/carcinogenesis#d54",
-"http://dl-learner.org/carcinogenesis#d56",
-"http://dl-learner.org/carcinogenesis#d57",
-"http://dl-learner.org/carcinogenesis#d8",
-"http://dl-learner.org/carcinogenesis#d85_1",
-"http://dl-learner.org/carcinogenesis#d90",
-"http://dl-learner.org/carcinogenesis#d94",
-"http://dl-learner.org/carcinogenesis#d97"
-],
-  "negative_examples": ["http://dl-learner.org/carcinogenesis#d110",
-"http://dl-learner.org/carcinogenesis#d111",
-"http://dl-learner.org/carcinogenesis#d114",
-"http://dl-learner.org/carcinogenesis#d116",
-"http://dl-learner.org/carcinogenesis#d117",
-"http://dl-learner.org/carcinogenesis#d119",
-"http://dl-learner.org/carcinogenesis#d121",
-"http://dl-learner.org/carcinogenesis#d123",
-"http://dl-learner.org/carcinogenesis#d124",
-"http://dl-learner.org/carcinogenesis#d125",
-"http://dl-learner.org/carcinogenesis#d127",
-"http://dl-learner.org/carcinogenesis#d128",
-"http://dl-learner.org/carcinogenesis#d130",
-"http://dl-learner.org/carcinogenesis#d133",
-"http://dl-learner.org/carcinogenesis#d150",
-"http://dl-learner.org/carcinogenesis#d151",
-"http://dl-learner.org/carcinogenesis#d154",
-"http://dl-learner.org/carcinogenesis#d155",
-"http://dl-learner.org/carcinogenesis#d156",
-"http://dl-learner.org/carcinogenesis#d159",
-"http://dl-learner.org/carcinogenesis#d160",
-"http://dl-learner.org/carcinogenesis#d161",
-"http://dl-learner.org/carcinogenesis#d162",
-"http://dl-learner.org/carcinogenesis#d163",
-"http://dl-learner.org/carcinogenesis#d164",
-"http://dl-learner.org/carcinogenesis#d165",
-"http://dl-learner.org/carcinogenesis#d166",
-"http://dl-learner.org/carcinogenesis#d169",
-"http://dl-learner.org/carcinogenesis#d170",
-"http://dl-learner.org/carcinogenesis#d171",
-"http://dl-learner.org/carcinogenesis#d172",
-"http://dl-learner.org/carcinogenesis#d173",
-"http://dl-learner.org/carcinogenesis#d174",
-"http://dl-learner.org/carcinogenesis#d178",
-"http://dl-learner.org/carcinogenesis#d179",
-"http://dl-learner.org/carcinogenesis#d180",
-"http://dl-learner.org/carcinogenesis#d181",
-"http://dl-learner.org/carcinogenesis#d183",
-"http://dl-learner.org/carcinogenesis#d184",
-"http://dl-learner.org/carcinogenesis#d185",
-"http://dl-learner.org/carcinogenesis#d186",
-"http://dl-learner.org/carcinogenesis#d188",
-"http://dl-learner.org/carcinogenesis#d190",
-"http://dl-learner.org/carcinogenesis#d194",
-"http://dl-learner.org/carcinogenesis#d207",
-"http://dl-learner.org/carcinogenesis#d208_1",
-"http://dl-learner.org/carcinogenesis#d209",
-"http://dl-learner.org/carcinogenesis#d210",
-"http://dl-learner.org/carcinogenesis#d211",
-"http://dl-learner.org/carcinogenesis#d212",
-"http://dl-learner.org/carcinogenesis#d213",
-"http://dl-learner.org/carcinogenesis#d214",
-"http://dl-learner.org/carcinogenesis#d215",
-"http://dl-learner.org/carcinogenesis#d217",
-"http://dl-learner.org/carcinogenesis#d218",
-"http://dl-learner.org/carcinogenesis#d219",
-"http://dl-learner.org/carcinogenesis#d220",
-"http://dl-learner.org/carcinogenesis#d224",
-"http://dl-learner.org/carcinogenesis#d256",
-"http://dl-learner.org/carcinogenesis#d257",
-"http://dl-learner.org/carcinogenesis#d258",
-"http://dl-learner.org/carcinogenesis#d261",
-"http://dl-learner.org/carcinogenesis#d262",
-"http://dl-learner.org/carcinogenesis#d263",
-"http://dl-learner.org/carcinogenesis#d264",
-"http://dl-learner.org/carcinogenesis#d265",
-"http://dl-learner.org/carcinogenesis#d266",
-"http://dl-learner.org/carcinogenesis#d267",
-"http://dl-learner.org/carcinogenesis#d269",
-"http://dl-learner.org/carcinogenesis#d271",
-"http://dl-learner.org/carcinogenesis#d276",
-"http://dl-learner.org/carcinogenesis#d280",
-"http://dl-learner.org/carcinogenesis#d285",
-"http://dl-learner.org/carcinogenesis#d287",
-"http://dl-learner.org/carcinogenesis#d293",
-"http://dl-learner.org/carcinogenesis#d294",
-"http://dl-learner.org/carcinogenesis#d59",
-"http://dl-learner.org/carcinogenesis#d60",
-"http://dl-learner.org/carcinogenesis#d61",
-"http://dl-learner.org/carcinogenesis#d63",
-"http://dl-learner.org/carcinogenesis#d64",
-"http://dl-learner.org/carcinogenesis#d65",
-"http://dl-learner.org/carcinogenesis#d69",
-"http://dl-learner.org/carcinogenesis#d70",
-"http://dl-learner.org/carcinogenesis#d71",
-"http://dl-learner.org/carcinogenesis#d72",
-"http://dl-learner.org/carcinogenesis#d73",
-"http://dl-learner.org/carcinogenesis#d74",
-"http://dl-learner.org/carcinogenesis#d75",
-"http://dl-learner.org/carcinogenesis#d76",
-"http://dl-learner.org/carcinogenesis#d77",
-"http://dl-learner.org/carcinogenesis#d78",
-"http://dl-learner.org/carcinogenesis#d79",
-"http://dl-learner.org/carcinogenesis#d80",
-"http://dl-learner.org/carcinogenesis#d81",
-"http://dl-learner.org/carcinogenesis#d82",
-"http://dl-learner.org/carcinogenesis#d112",
-"http://dl-learner.org/carcinogenesis#d113",
-"http://dl-learner.org/carcinogenesis#d115",
-"http://dl-learner.org/carcinogenesis#d118",
-"http://dl-learner.org/carcinogenesis#d120",
-"http://dl-learner.org/carcinogenesis#d122",
-"http://dl-learner.org/carcinogenesis#d126",
-"http://dl-learner.org/carcinogenesis#d129",
-"http://dl-learner.org/carcinogenesis#d131",
-"http://dl-learner.org/carcinogenesis#d132",
-"http://dl-learner.org/carcinogenesis#d149",
-"http://dl-learner.org/carcinogenesis#d152",
-"http://dl-learner.org/carcinogenesis#d153",
-"http://dl-learner.org/carcinogenesis#d157",
-"http://dl-learner.org/carcinogenesis#d158",
-"http://dl-learner.org/carcinogenesis#d167",
-"http://dl-learner.org/carcinogenesis#d168",
-"http://dl-learner.org/carcinogenesis#d175",
-"http://dl-learner.org/carcinogenesis#d176",
-"http://dl-learner.org/carcinogenesis#d177",
-"http://dl-learner.org/carcinogenesis#d182",
-"http://dl-learner.org/carcinogenesis#d187",
-"http://dl-learner.org/carcinogenesis#d189",
-"http://dl-learner.org/carcinogenesis#d208_2",
-"http://dl-learner.org/carcinogenesis#d216",
-"http://dl-learner.org/carcinogenesis#d221",
-"http://dl-learner.org/carcinogenesis#d222",
-"http://dl-learner.org/carcinogenesis#d223",
-"http://dl-learner.org/carcinogenesis#d225",
-"http://dl-learner.org/carcinogenesis#d259",
-"http://dl-learner.org/carcinogenesis#d260",
-"http://dl-learner.org/carcinogenesis#d268",
-"http://dl-learner.org/carcinogenesis#d270",
-"http://dl-learner.org/carcinogenesis#d282",
-"http://dl-learner.org/carcinogenesis#d295",
-"http://dl-learner.org/carcinogenesis#d62",
-"http://dl-learner.org/carcinogenesis#d66",
-"http://dl-learner.org/carcinogenesis#d67",
-"http://dl-learner.org/carcinogenesis#d68",
-"http://dl-learner.org/carcinogenesis#d83"
-]
-}
\ No newline at end of file
diff --git a/examples/celoe_notebook.ipynb b/examples/celoe_notebook.ipynb
deleted file mode 100644
index f5b06508..00000000
--- a/examples/celoe_notebook.ipynb
+++ /dev/null
@@ -1,185 +0,0 @@
-{
- "cells": [
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "blond-letter",
-   "metadata": {},
-   "source": [
-    "# CELOE Notebook\n",
-    "This is a jupyter notebook file to execute [CELOE](ontolearn.concept_learner.CELOE) and generate predictive results. We recommend you to see the [concept learners](../docs/usage/06_concept_learners.md) guide before continuing with the execution.\n",
-    "Also if you have not done it already, from the main directory \"Ontolearn\", run the commands for Datasets mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download the datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "japanese-ivory",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.concept_learner import CELOE\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.owl_individual import OWLNamedIndividual, IRI\n",
-    "from ontolearn.utils import setup_logging\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "pending-coast",
-   "metadata": {},
-   "source": [
-    "Open `uncle_lp.json` where we have stored the learning problem for the concept of 'Uncle' and the path to the 'family' ontology."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "beginning-syntax",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "with open('uncle_lp.json') as json_file:\n",
-    "    settings = json.load(json_file)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "humanitarian-heating",
-   "metadata": {},
-   "source": [
-    "Create an instance of the class `KnowledeBase` by using the path that is stored in `settings`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "caroline-indiana",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "kb = KnowledgeBase(path=settings['data_path'])"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "lucky-activation",
-   "metadata": {},
-   "source": [
-    "Retreive the IRIs of the positive and negative examples of Uncle from `settings` and create an instance of `PosNegLPStandard`. (more info about this [here](../docs/usage/06_concept_learners.md#configure-the-learning-problem))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "processed-patrick",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "examples = settings['Uncle']\n",
-    "p = set(examples['positive_examples'])\n",
-    "n = set(examples['negative_examples'])\n",
-    "typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))\n",
-    "typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))\n",
-    "lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "mechanical-latin",
-   "metadata": {},
-   "source": [
-    "Create a model of [CELOE](ontolearn.concept_learner.CELOE) and fit the learning problem to the model."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "binding-moderator",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<ontolearn.concept_learner.CELOE at 0x210f0224ac0>"
-      ]
-     },
-     "execution_count": 8,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "model = CELOE(knowledge_base=kb, max_runtime=600)\n",
-    "model.fit(lp)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "lesser-cover",
-   "metadata": {},
-   "source": [
-    "Retrieve top 3 hypotheses and print them."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "departmental-international",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "<class 'ontolearn.search.OENode'> at 0xdecf19\t(¬Female) ⊓ (∀ hasParent.Grandparent)\tQuality:0.90476\tHeuristic:0.20607\tDepth:8\tH_exp:8\t|RC|:5\t|Indv.|:None\n",
-      "<class 'ontolearn.search.OENode'> at 0xdecf82\t(¬Female) ⊓ (∀ hasChild.Grandchild)\tQuality:0.90476\tHeuristic:0.20607\tDepth:8\tH_exp:8\t|RC|:5\t|Indv.|:None\n",
-      "<class 'ontolearn.search.OENode'> at 0xe73bcf\t(¬Female) ⊓ (∀ hasParent⁻.Grandchild)\tQuality:0.90476\tHeuristic:0.20607\tDepth:8\tH_exp:8\t|RC|:5\t|Indv.|:None\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "[None, None, None]"
-      ]
-     },
-     "execution_count": 6,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "hypotheses = list(model.best_hypotheses(n=3))\n",
-    "[print(_) for _ in hypotheses]"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.10"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/clip_notebook.ipynb b/examples/clip_notebook.ipynb
deleted file mode 100644
index 4154eeb1..00000000
--- a/examples/clip_notebook.ipynb
+++ /dev/null
@@ -1,285 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "thick-expansion",
-   "metadata": {},
-   "source": [
-    "# CLIP Notebook\n",
-    "This is a jupyter notebook file to execute [CLIP](ontolearn.concept_learner.CLIP) and generate predictive results. We recommend you to see the [concept learners](../docs/usage/06_concept_learners.md) guide before continuing with the execution.\n",
-    "Also if you have not done it already, from the main directory \"Ontolearn\", run the commands for Datasets mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download the datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "id": "typical-modification",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/home/nkouagou/.conda/envs/onto/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
-      "  from .autonotebook import tqdm as notebook_tqdm\n"
-     ]
-    }
-   ],
-   "source": [
-    "import json\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.concept_learner import CLIP\n",
-    "from ontolearn.refinement_operators import ExpressRefinement\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.owl_individual import OWLNamedIndividual, IRI\n",
-    "from ontolearn.utils import setup_logging\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "vocational-focus",
-   "metadata": {},
-   "source": [
-    "Open `uncle_lp.json` where we have stored the learning problem for the concept of 'Uncle' and the path to the 'family' ontology."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "special-bloom",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [],
-   "source": [
-    "with open('uncle_lp.json') as json_file:\n",
-    "    settings = json.load(json_file)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "functional-temple",
-   "metadata": {},
-   "source": [
-    "Create an instance of the class `KnowledeBase` by using the path that is stored in `settings`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "neutral-present",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [],
-   "source": [
-    "kb = KnowledgeBase(path=settings['data_path'])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "valued-dutch",
-   "metadata": {},
-   "source": [
-    "Retreive the IRIs of the positive and negative examples of Uncle from `settings` and create an instance of `PosNegLPStandard`. (more info about this [here](../docs/usage/06_concept_learners.md#configure-the-learning-problem))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "applicable-double",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [],
-   "source": [
-    "examples = settings['Uncle']\n",
-    "p = set(examples['positive_examples'])\n",
-    "n = set(examples['negative_examples'])\n",
-    "typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))\n",
-    "typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))\n",
-    "lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "filled-tract",
-   "metadata": {},
-   "source": [
-    "Create a model of [CLIP](ontolearn.concept_learner.CLIP) and fit the learning problem to the model."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "floppy-enzyme",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [],
-   "source": [
-    "op = ExpressRefinement(knowledge_base=kb, use_inverse=False,\n",
-    "                          use_numeric_datatypes=False)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "given-democrat",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded length predictor!\n",
-      "\n",
-      " Loaded length predictor!\n",
-      "\n",
-      " Loaded length predictor!\n",
-      "\n",
-      " Loaded length predictor!\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/home/nkouagou/.conda/envs/onto/lib/python3.10/site-packages/torch/cuda/__init__.py:141: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 6050). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at ../c10/cuda/CUDAFunctions.cpp:108.)\n",
-      "  return torch._C._cuda_getDeviceCount() > 0\n"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "***** Predicted length: 5 *****\n",
-      "\n",
-      "***** Predicted length: 5 *****\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "<ontolearn.concept_learner.CLIP at 0x7f5488546110>"
-      ]
-     },
-     "execution_count": 6,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "model = CLIP(knowledge_base=kb, path_of_embeddings=\"../CLIPData/family/embeddings/ConEx_entity_embeddings.csv\",\n",
-    "             refinement_operator=op, load_pretrained=True, max_runtime=200)\n",
-    "model.fit(lp)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "demanding-feeling",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[<class 'ontolearn.search.OENode'> at 0x8516977\tBrother ⊔ (∃ married.Sister)\tQuality:0.93827\tHeuristic:0.44752\tDepth:2\tH_exp:6\t|RC|:4\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x8516970\tBrother ⊔ (∃ married.Daughter)\tQuality:0.92683\tHeuristic:0.43264\tDepth:2\tH_exp:6\t|RC|:4\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x8518500\tSon ⊔ (∃ married.Sister)\tQuality:0.91566\tHeuristic:0.42124\tDepth:2\tH_exp:6\t|RC|:4\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x85184f9\tSon ⊔ (∃ married.Daughter)\tQuality:0.90476\tHeuristic:0.40707\tDepth:2\tH_exp:6\t|RC|:4\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x851870d\tMale ⊓ (∀ hasParent.Grandparent)\tQuality:0.90476\tHeuristic:0.40407\tDepth:2\tH_exp:6\t|RC|:7\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x85187ca\tMale ⊓ (∀ hasChild.Grandchild)\tQuality:0.90476\tHeuristic:0.40407\tDepth:2\tH_exp:6\t|RC|:7\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x85184e4\tBrother ⊔ (∀ married.Sister)\tQuality:0.89412\tHeuristic:0.37788\tDepth:3\tH_exp:6\t|RC|:3\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x8997da8\tBrother ⊔ (∃ married.Female)\tQuality:0.89412\tHeuristic:0.45336\tDepth:1\tH_exp:6\t|RC|:9\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x851a43c\tMale ⊓ (∀ hasChild.(¬Grandfather))\tQuality:0.88889\tHeuristic:0.39044\tDepth:2\tH_exp:6\t|RC|:0\t|Indv.|:None,\n",
-       " <class 'ontolearn.search.OENode'> at 0x882ab84\tMale\tQuality:0.88372\tHeuristic:0.38383\tDepth:1\tH_exp:4\t|RC|:265\t|Indv.|:None]"
-      ]
-     },
-     "execution_count": 7,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "list(model.best_descriptions)#[0].quality"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "together-enforcement",
-   "metadata": {},
-   "source": [
-    "## Retrieve top 3 hypotheses and print them."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "insured-enterprise",
-   "metadata": {
-    "tags": []
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "OWLObjectUnionOf((OWLClass(IRI('http://www.benchmark.org/family#','Brother')), OWLObjectSomeValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','married')),filler=OWLClass(IRI('http://www.benchmark.org/family#','Sister')))))\n",
-      "OWLObjectUnionOf((OWLClass(IRI('http://www.benchmark.org/family#','Brother')), OWLObjectSomeValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','married')),filler=OWLClass(IRI('http://www.benchmark.org/family#','Daughter')))))\n",
-      "OWLObjectUnionOf((OWLClass(IRI('http://www.benchmark.org/family#','Son')), OWLObjectSomeValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','married')),filler=OWLClass(IRI('http://www.benchmark.org/family#','Sister')))))\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "[None, None, None]"
-      ]
-     },
-     "execution_count": 8,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "hypotheses = list(model.best_hypotheses(n=3))\n",
-    "[print(_) for _ in hypotheses]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "surprising-transfer",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "onto",
-   "language": "python",
-   "name": "onto"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.10.14"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/concept_learning_evaluation.py b/examples/concept_learning_evaluation.py
index a680a2db..4f8dff67 100644
--- a/examples/concept_learning_evaluation.py
+++ b/examples/concept_learning_evaluation.py
@@ -1,46 +1,38 @@
 """
-Fitting DL Concept Learning Algorithms:
+Fitting OWL Class Expression Learners:
 
-Given E^+  and E^-, a learner finds a concept H and F1 score is computed w.r.t. E^+, E^-, and R(H) retrieval of H.
+Given positive examples (E^+)  and negative examples (E^-),
+Evaluate the performances of OWL Class Expression Learners  w.r.t. the quality of learned/found OWL Class Expression
 
-python examples/concept_learning_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family.owl --max_runtime 30 --report family.csv
+Example to run the script
+python examples/concept_learning_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family-benchmark_rich_background.owl --max_runtime 3 --report family.csv
 
 """
-
 import json
-import os
 import time
 import pandas as pd
 from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import CELOE, OCEL, EvoLearner
+from ontolearn.learners import CELOE, OCEL
+from ontolearn.concept_learner import EvoLearner
 from ontolearn.learners import Drill, TDL
 from ontolearn.learning_problem import PosNegLPStandard
 from ontolearn.metrics import F1
 from owlapy.owl_individual import OWLNamedIndividual, IRI
 import argparse
-from rdflib import Graph
-
 from ontolearn.utils.static_funcs import compute_f1_score
-
 pd.set_option("display.precision", 5)
 
-
 def dl_concept_learning(args):
     with open(args.lps) as json_file:
         settings = json.load(json_file)
 
     kb = KnowledgeBase(path=args.kb)
-
     ocel = OCEL(knowledge_base=kb, quality_func=F1(), max_runtime=args.max_runtime)
     celoe = CELOE(knowledge_base=kb, quality_func=F1(), max_runtime=args.max_runtime)
     drill = Drill(knowledge_base=KnowledgeBase(path=args.kb),
-                  path_pretrained_kge=args.path_pretrained_kge,
                   quality_func=F1(),
                   max_runtime=args.max_runtime)
     tdl = TDL(knowledge_base=KnowledgeBase(path=args.kb),
-              dataframe_triples=pd.DataFrame(
-                  data=sorted([(str(s), str(p), str(o)) for s, p, o in Graph().parse(args.kb)], key=lambda x: len(x)),
-                  columns=['subject', 'relation', 'object'], dtype=str),
               kwargs_classifier={"random_state": 0},
               max_runtime=args.max_runtime)
     # dictionary to store the data
@@ -62,18 +54,17 @@ def dl_concept_learning(args):
         pred_ocel = ocel.fit(lp).best_hypotheses(n=1)
         print("OCEL ends..", end="\t")
         rt_ocel = time.time() - start_time
-        f1_ocel = compute_f1_score(individuals={i for i in kb.individuals(pred_ocel.concept)}, pos=lp.pos, neg=lp.neg)
+        f1_ocel = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_ocel)}), pos=lp.pos, neg=lp.neg)
         data.setdefault("F1-OCEL", []).append(f1_ocel)
         data.setdefault("RT-OCEL", []).append(rt_ocel)
         print(f"OCEL Quality: {f1_ocel:.3f}", end="\t")
         print(f"OCEL Runtime: {rt_ocel:.3f}")
-
         print("CELOE starts..", end="\t")
         start_time = time.time()
         pred_celoe = celoe.fit(lp).best_hypotheses(n=1)
         print("CELOE Ends..", end="\t")
         rt_celoe = time.time() - start_time
-        f1_celoe = compute_f1_score(individuals={i for i in kb.individuals(pred_celoe.concept)}, pos=lp.pos, neg=lp.neg)
+        f1_celoe = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_celoe)}), pos=lp.pos, neg=lp.neg)
         data.setdefault("F1-CELOE", []).append(f1_celoe)
         data.setdefault("RT-CELOE", []).append(rt_celoe)
         print(f"CELOE Quality: {f1_celoe:.3f}", end="\t")
@@ -86,7 +77,7 @@ def dl_concept_learning(args):
         pred_evo = evo.fit(lp).best_hypotheses(n=1)
         print("Evo ends..", end="\t")
         rt_evo = time.time() - start_time
-        f1_evo = compute_f1_score(individuals={i for i in kb.individuals(pred_evo.concept)}, pos=lp.pos, neg=lp.neg)
+        f1_evo = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_evo)}), pos=lp.pos, neg=lp.neg)
         data.setdefault("F1-Evo", []).append(f1_evo)
         data.setdefault("RT-Evo", []).append(rt_evo)
         print(f"Evo Quality: {f1_evo:.3f}", end="\t")
@@ -97,7 +88,7 @@ def dl_concept_learning(args):
         pred_drill = drill.fit(lp).best_hypotheses(n=1)
         print("DRILL ends..", end="\t")
         rt_drill = time.time() - start_time
-        f1_drill = compute_f1_score(individuals=set(kb.individuals(pred_drill.concept)), pos=lp.pos, neg=lp.neg)
+        f1_drill = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_drill)}), pos=lp.pos, neg=lp.neg)
         data.setdefault("F1-DRILL", []).append(f1_drill)
         data.setdefault("RT-DRILL", []).append(rt_drill)
         print(f"DRILL Quality: {f1_drill:.3f}", end="\t")
@@ -111,7 +102,7 @@ def dl_concept_learning(args):
         rt_tdl = time.time() - start_time
 
         # () Quality on the training data
-        f1_tdl = compute_f1_score(individuals={i for i in kb.individuals(pred_tdl)},
+        f1_tdl = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_tdl)}),
                                         pos=lp.pos,
                                         neg=lp.neg)
 
@@ -124,12 +115,13 @@ def dl_concept_learning(args):
     print(df)
     print(df.select_dtypes(include="number").mean())
 
+    print(df.select_dtypes(include="number").mean().values.tolist())
 
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='Description Logic Concept Learning')
-    parser.add_argument("--max_runtime", type=int, default=1)
-    parser.add_argument("--lps", type=str, required=True)
-    parser.add_argument("--kb", type=str, required=True)
+    parser.add_argument("--max_runtime", type=int, default=3000)
+    parser.add_argument("--lps", type=str, default="/home/cdemir/Desktop/Softwares/Ontolearn/LPs/Family/lps.json")#, required=True)
+    parser.add_argument("--kb", type=str, default="/home/cdemir/Desktop/Softwares/Ontolearn/KGs/Family/family-benchmark_rich_background.owl")#,required=True)
     parser.add_argument("--path_pretrained_kge", type=str, default=None)
     parser.add_argument("--report", type=str, default="report.csv")
     dl_concept_learning(parser.parse_args())
diff --git a/examples/concept_learning_neural_evaluation.py b/examples/concept_learning_neural_evaluation.py
index c8f040b1..7bc7e6ca 100644
--- a/examples/concept_learning_neural_evaluation.py
+++ b/examples/concept_learning_neural_evaluation.py
@@ -15,92 +15,36 @@
 import platform
 import pandas as pd
 from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import CELOE, OCEL, EvoLearner, NCES, CLIP
+from ontolearn.learners import CELOE, OCEL, Drill, TDL
+from ontolearn.concept_learner import EvoLearner, NCES, CLIP
 from ontolearn.refinement_operators import ExpressRefinement
-from ontolearn.learners import Drill, TDL
 from ontolearn.learning_problem import PosNegLPStandard
 from ontolearn.metrics import F1
 from owlapy.owl_individual import OWLNamedIndividual, IRI
 import argparse
 from sklearn.model_selection import StratifiedKFold
 import numpy as np
-
 from ontolearn.utils.static_funcs import compute_f1_score
 from ontolearn.triple_store import TripleStore
-
 from ontolearn.owl_neural_reasoner import TripleStoreNeuralReasoner
-
 from owlapy import owl_expression_to_dl
 
 pd.set_option("display.precision", 5)
 
-
-def get_embedding_path(ftp_link: str, embeddings_path_arg, kb_path_arg: str):
-    if embeddings_path_arg is None or (
-        embeddings_path_arg is not None and not os.path.exists(embeddings_path_arg)
-    ):
-        file_name = ftp_link.split("/")[-1]
-        if not os.path.exists(os.path.join(os.getcwd(), file_name)):
-            subprocess.run(["curl", "-O", ftp_link])
-
-            if platform.system() == "Windows":
-                subprocess.run(["tar", "-xf", file_name])
-            else:
-                subprocess.run(["unzip", file_name])
-            os.remove(os.path.join(os.getcwd(), file_name))
-
-        embeddings_path = os.path.join(os.getcwd(), file_name[:-4] + "/")
-
-        if "family" in kb_path_arg:
-            embeddings_path += "family/embeddings/ConEx_entity_embeddings.csv"
-        elif "carcinogenesis" in kb_path_arg:
-            embeddings_path += "carcinogenesis/embeddings/ConEx_entity_embeddings.csv"
-        elif "mutagenesis" in kb_path_arg:
-            embeddings_path += "mutagenesis/embeddings/ConEx_entity_embeddings.csv"
-        elif "nctrer" in kb_path_arg:
-            embeddings_path += "nctrer/embeddings/ConEx_entity_embeddings.csv"
-        elif "animals" in kb_path_arg:
-            embeddings_path += "animals/embeddings/ConEx_entity_embeddings.csv"
-        elif "lymphography" in kb_path_arg:
-            embeddings_path += "lymphography/embeddings/ConEx_entity_embeddings.csv"
-        elif "semantic_bible" in kb_path_arg:
-            embeddings_path += "semantic_bible/embeddings/ConEx_entity_embeddings.csv"
-        elif "suramin" in kb_path_arg:
-            embeddings_path += "suramin/embeddings/ConEx_entity_embeddings.csv"
-        elif "vicodi" in kb_path_arg:
-            embeddings_path += "vicodi/embeddings/ConEx_entity_embeddings.csv"
-
-        return embeddings_path
-    else:
-        return embeddings_path_arg
-
-
 def dl_concept_learning(args):
     with open(args.lps) as json_file:
         settings = json.load(json_file)
 
-    # To compute the "original quality". RDF KGs provied in ontolearn are complete and consistent.
-    # So we can use kb to compute the original quality
+
     kb = KnowledgeBase(path=args.kb)
-    drill_with_symbolic_retriever = Drill(
-        knowledge_base=kb,
-        path_embeddings=args.path_drill_embeddings,
-        quality_func=F1(),
-        max_runtime=args.max_runtime,
-        verbose=0,
-    )
+    drill_with_symbolic_retriever = Drill(knowledge_base=kb, path_embeddings=args.path_drill_embeddings,
+                                          quality_func=F1(), max_runtime=args.max_runtime,verbose=0)
 
-    neural_kb = TripleStore(
-        reasoner=TripleStoreNeuralReasoner(path_neural_embedding=args.kge)
-    )
+    neural_kb = TripleStore(reasoner=TripleStoreNeuralReasoner(path_neural_embedding=args.kge))
 
-    drill_with_neural_retriever = Drill(
-        knowledge_base=neural_kb,
-        path_embeddings=args.path_drill_embeddings,
-        quality_func=F1(),
-        max_runtime=args.max_runtime,
-        verbose=0,
-    )
+    drill_with_neural_retriever = Drill(knowledge_base=neural_kb,
+                                        path_embeddings=args.path_drill_embeddings,
+                                        quality_func=F1(), max_runtime=args.max_runtime, verbose=0)
 
     # dictionary to store the data
     data = dict()
@@ -112,14 +56,13 @@ def dl_concept_learning(args):
         problems = settings.items()
         positives_key = "positive examples"
         negatives_key = "negative examples"
+
     for str_target_concept, examples in problems:
         print("Target concept: ", str_target_concept)
         p = examples[positives_key]
         n = examples[negatives_key]
 
-        kf = StratifiedKFold(
-            n_splits=args.folds, shuffle=True, random_state=args.random_seed
-        )
+        kf = StratifiedKFold(n_splits=args.folds, shuffle=True, random_state=args.random_seed)
         X = np.array(p + n)
         y = np.array([1.0 for _ in p] + [0.0 for _ in n])
 
@@ -128,175 +71,102 @@ def dl_concept_learning(args):
             data.setdefault("LP", []).append(str_target_concept)
             data.setdefault("Fold", []).append(ith)
             # () Extract positive and negative examples from train fold
-            train_pos = {
-                pos_individual for pos_individual in X[train_index][y[train_index] == 1]
-            }
-            train_neg = {
-                neg_individual for neg_individual in X[train_index][y[train_index] == 0]
-            }
+            train_pos = {pos_individual for pos_individual in X[train_index][y[train_index] == 1]}
+            train_neg = {neg_individual for neg_individual in X[train_index][y[train_index] == 0]}
 
             # Sanity checking for individuals used for training.
             assert train_pos.issubset(examples[positives_key])
             assert train_neg.issubset(examples[negatives_key])
 
             # () Extract positive and negative examples from test fold
-            test_pos = {
-                pos_individual for pos_individual in X[test_index][y[test_index] == 1]
-            }
-            test_neg = {
-                neg_individual for neg_individual in X[test_index][y[test_index] == 0]
-            }
+            test_pos = {pos_individual for pos_individual in X[test_index][y[test_index] == 1]}
+            test_neg = {neg_individual for neg_individual in X[test_index][y[test_index] == 0]}
 
             # Sanity checking for individuals used for testing.
             assert test_pos.issubset(examples[positives_key])
             assert test_neg.issubset(examples[negatives_key])
             train_lp = PosNegLPStandard(
                 pos={OWLNamedIndividual(i) for i in train_pos},
-                neg={OWLNamedIndividual(i) for i in train_neg},
-            )
+                neg={OWLNamedIndividual(i) for i in train_neg})
 
             test_lp = PosNegLPStandard(
                 pos={OWLNamedIndividual(i) for i in test_pos},
-                neg={OWLNamedIndividual(i) for i in test_neg},
-            )
+                neg={OWLNamedIndividual(i) for i in test_neg})
             print("DRILL Symbolic starts..", end=" ")
             start_time = time.time()
             # Prediction of DRILL through symbolic retriever.
-            pred_symbolic_drill = drill_with_symbolic_retriever.fit(
-                train_lp
-            ).best_hypotheses()
+            pred_symbolic_drill = drill_with_symbolic_retriever.fit(train_lp).best_hypotheses()
             symbolic_rt_drill = time.time() - start_time
             print("DRILL Symbolic ends..", end="\t")
             # Quality of prediction through symbolic retriever on the train split.
             symbolic_train_f1_drill = compute_f1_score(
                 individuals=frozenset({i for i in kb.individuals(pred_symbolic_drill)}),
                 pos=train_lp.pos,
-                neg=train_lp.neg,
-            )
+                neg=train_lp.neg)
             # Quality of prediction through symbolic retriever on the test split.
             symbolic_test_f1_drill = compute_f1_score(
                 individuals=frozenset({i for i in kb.individuals(pred_symbolic_drill)}),
                 pos=test_lp.pos,
-                neg=test_lp.neg,
-            )
-            print(
-                f"DRILL Symbolic Train Quality: {symbolic_train_f1_drill:.3f}", end="\t"
-            )
-            print(
-                f"DRILL Symbolic Test Quality: {symbolic_test_f1_drill:.3f}", end="\t"
-            )
+                neg=test_lp.neg)
+            print(f"DRILL Symbolic Train Quality: {symbolic_train_f1_drill:.3f}", end="\t")
+            print(f"DRILL Symbolic Test Quality: {symbolic_test_f1_drill:.3f}", end="\t")
             print(f"DRILL Symbolic Runtime: {symbolic_rt_drill:.3f}", end="\t")
             print(f"Prediction: {owl_expression_to_dl(pred_symbolic_drill)}")
-
-            data.setdefault("Train-F1-Symbolic-DRILL", []).append(
-                symbolic_train_f1_drill
-            )
+            data.setdefault("Train-F1-Symbolic-DRILL", []).append(symbolic_train_f1_drill)
             data.setdefault("Test-F1-Symbolic-DRILL", []).append(symbolic_test_f1_drill)
             data.setdefault("RT-Symbolic-DRILL", []).append(symbolic_rt_drill)
-            data.setdefault("Prediction-Symbolic-DRILL", []).append(
-                owl_expression_to_dl(pred_symbolic_drill)
-            )
+            data.setdefault("Prediction-Symbolic-DRILL", []).append(owl_expression_to_dl(pred_symbolic_drill))
 
-            print("DRILL Neural starts..", end="\t")
+            print("DRILL Neural starts..", end=" ")
             start_time = time.time()
-            # Prediction of DRILL through neural retriever.
-            pred_neural_drill = drill_with_neural_retriever.fit(
-                train_lp
-            ).best_hypotheses()
+            # Prediction of DRILL through symbolic retriever.
+            pred_neural_drill = drill_with_neural_retriever.fit(train_lp).best_hypotheses()
             neural_rt_drill = time.time() - start_time
             print("DRILL Neural ends..", end="\t")
-            # Quality of prediction through neural retriever on the train split.
-            neural_train_f1_drill = compute_f1_score(
-                individuals=frozenset(
-                    {i for i in neural_kb.individuals(pred_neural_drill)}
-                ),
-                pos=train_lp.pos,
-                neg=train_lp.neg,
-            )
-            # Quality of prediction through neural retriever on the test split.
-            neural_test_f1_drill = compute_f1_score(
-                individuals=frozenset(
-                    {i for i in neural_kb.individuals(pred_neural_drill)}
-                ),
-                pos=test_lp.pos,
-                neg=test_lp.neg,
-            )
             # Quality of prediction through symbolic retriever on the train split.
-            neural_symbolic_train_f1_drill = compute_f1_score(
-                individuals=frozenset({i for i in kb.individuals(pred_neural_drill)}),
+            neural_train_f1_drill = compute_f1_score(
+                individuals=frozenset({i for i in neural_kb.individuals(pred_neural_drill)}),
                 pos=train_lp.pos,
-                neg=train_lp.neg,
-            )
+                neg=train_lp.neg)
             # Quality of prediction through symbolic retriever on the test split.
-            neural_symbolic_test_f1_drill = compute_f1_score(
-                individuals=frozenset({i for i in kb.individuals(pred_neural_drill)}),
+            neural_test_f1_drill = compute_f1_score(
+                individuals=frozenset({i for i in neural_kb.individuals(pred_neural_drill)}),
                 pos=test_lp.pos,
-                neg=test_lp.neg,
-            )
-
-            # Quality of prediction w.r.t. neural retriever on the train split.
+                neg=test_lp.neg)
             print(f"DRILL Neural Train Quality: {neural_train_f1_drill:.3f}", end="\t")
-            # Quality of prediction w.r.t. neural retriever on the test split.
             print(f"DRILL Neural Test Quality: {neural_test_f1_drill:.3f}", end="\t")
-
-            # Quality of prediction w.r.t. symbolic retriever on the train split.
-            print(
-                f"DRILL Neural-Symbolic-Train Quality: {neural_symbolic_train_f1_drill:.3f}",
-                end="\t",
-            )
-            # Quality of prediction w.r.t. symbolic retriever on the test split.
-            print(
-                f"DRILL Neural-Symbolic-Test Quality: {neural_symbolic_test_f1_drill:.3f}",
-                end="\t",
-            )
-
             print(f"DRILL Neural Runtime: {neural_rt_drill:.3f}", end="\t")
             print(f"Prediction: {owl_expression_to_dl(pred_neural_drill)}")
 
-            data.setdefault("Train-F1-Neural-Symbolic-DRILL", []).append(
-                neural_symbolic_train_f1_drill
-            )
-            data.setdefault("Test-F1-Neural-Symbolic-DRILL", []).append(
-                neural_symbolic_test_f1_drill
-            )
-
             data.setdefault("Train-F1-Neural-DRILL", []).append(neural_train_f1_drill)
             data.setdefault("Test-F1-Neural-DRILL", []).append(neural_test_f1_drill)
-
             data.setdefault("RT-Neural-DRILL", []).append(neural_rt_drill)
-            data.setdefault("Prediction-Symbolic-DRILL", []).append(
-                owl_expression_to_dl(pred_neural_drill)
-            )
+            data.setdefault("Prediction-Neural-DRILL", []).append(owl_expression_to_dl(pred_neural_drill))
 
+    print(data)
     df = pd.DataFrame.from_dict(data)
     df.to_csv(args.report, index=False)
     print(df)
     print(df.select_dtypes(include="number").mean())
 
+    assert df.select_dtypes(include="number").mean()["Train-F1-Symbolic-DRILL"] >= 0.90
+
+
 
 if __name__ == "__main__":
-    parser = argparse.ArgumentParser(
-        description="OWL Class Expression Learning with Neural Reasoner"
-    )
-    parser.add_argument(
-        "--lps", type=str, required=True, help="Path to the learning problems"
-    )
-    parser.add_argument(
-        "--folds", type=int, default=10, help="Number of folds of cross validation."
-    )
-    parser.add_argument("--kb", type=str, required=True, help="Knowledge base")
-    parser.add_argument(
-        "--kge",
-        type=str,
-        required=True,
-        default=None,
-        help="Knowledge Graph Embedding Path",
-    )
+    # python examples/concept_learning_neural_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family-benchmark_rich_background.owl --kge KeciFamilyRun --max_runtime 3 --report family.csv
+    parser = argparse.ArgumentParser(description="OWL Class Expression Learning with Neural Reasoner")
+    parser.add_argument("--lps", type=str, default="/home/cdemir/Desktop/Softwares/Ontolearn/LPs/Family/lps.json",
+                        help="Path to the learning problems")
+    parser.add_argument("--folds", type=int, default=2, help="Number of folds of cross validation.")
+    parser.add_argument("--kb", type=str, default="/home/cdemir/Desktop/Softwares/Ontolearn/KGs/Family/family-benchmark_rich_background.owl", help="Knowledge base")
+    parser.add_argument("--kge",type=str,default="/home/cdemir/Desktop/Softwares/Ontolearn/KeciFamilyRun",
+                        help="Knowledge Graph Embedding Path")
 
     parser.add_argument("--path_drill_embeddings", type=str, default=None)
     parser.add_argument("--path_of_nces_embeddings", type=str, default=None)
     parser.add_argument("--path_of_clip_embeddings", type=str, default=None)
     parser.add_argument("--report", type=str, default="report.csv")
-    parser.add_argument("--max_runtime", type=int, default=10, help="Max runtime")
+    parser.add_argument("--max_runtime", type=int, default=1, help="Max runtime")
     parser.add_argument("--random_seed", type=int, default=1)
     dl_concept_learning(parser.parse_args())
diff --git a/examples/concept_learning_with_celoe_heuristic.py b/examples/concept_learning_with_celoe_heuristic.py
index 01eb68f5..348f412a 100644
--- a/examples/concept_learning_with_celoe_heuristic.py
+++ b/examples/concept_learning_with_celoe_heuristic.py
@@ -1,18 +1,14 @@
 import json
 import os
 import random
-
 from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import CELOE
+from ontolearn.learners import CELOE
 from ontolearn.heuristics import CELOEHeuristic
 from ontolearn.learning_problem import PosNegLPStandard
 from ontolearn.metrics import Accuracy
 from owlapy.owl_individual import OWLNamedIndividual, IRI
 from owlapy.class_expression import OWLClass
 from ontolearn.refinement_operators import ModifiedCELOERefinement
-from ontolearn.utils import setup_logging
-
-setup_logging()
 
 try:
     os.chdir("examples")
@@ -69,16 +65,14 @@
                   refinement_operator=op,
                   quality_func=qual,
                   heuristic_func=heur,
-                  max_num_of_concepts_tested=10_000_000_000,
-                  iter_bound=10_000_000_000)
+                  max_num_of_concepts_tested=100,
+                  iter_bound=100)
     model.fit(lp)
-
-    model.save_best_hypothesis(n=3, path='Predictions_{0}'.format(str_target_concept))
+    model.save_best_hypothesis(n=1, path=f'Predictions_{str_target_concept}')
     # Get Top n hypotheses
     hypotheses = list(model.best_hypotheses(n=3))
     # Use hypotheses as binary function to label individuals.
     predictions = model.predict(individuals=list(typed_pos | typed_neg),
                                 hypotheses=hypotheses)
     # print(predictions)
-    [print(_) for _ in hypotheses]
-    # exit(1)
+    [print(_) for _ in hypotheses]
\ No newline at end of file
diff --git a/examples/concept_learning_with_ocel.py b/examples/concept_learning_with_ocel.py
index 269af259..51ad3fdb 100644
--- a/examples/concept_learning_with_ocel.py
+++ b/examples/concept_learning_with_ocel.py
@@ -1,8 +1,7 @@
 import json
 import os
-
 from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import OCEL
+from ontolearn.learners import OCEL
 from ontolearn.learning_problem import PosNegLPStandard
 from ontolearn.utils import setup_logging
 from owlapy.owl_individual import OWLNamedIndividual, IRI
@@ -54,7 +53,7 @@
     lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
 
     model = OCEL(knowledge_base=target_kb,
-                 max_runtime=600,
+                 max_runtime=10,
                  max_num_of_concepts_tested=10_000_000_000,
                  iter_bound=10_000_000_000)
     model.fit(lp)
diff --git a/examples/drill_notebook.ipynb b/examples/drill_notebook.ipynb
deleted file mode 100644
index 9dda414d..00000000
--- a/examples/drill_notebook.ipynb
+++ /dev/null
@@ -1,182 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "verified-temple",
-   "metadata": {
-    "tags": []
-   },
-   "source": [
-    "# DRILL Notebook\n",
-    "This is a jupyter notebook file to execute [DRILL](ontolearn.learners.drill) and generate predictive results. If you have not done it already, from the main directory \"Ontolearn\", run the commands for Datasets mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download the datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "sustainable-poland",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json\n",
-    "import numpy as np\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.learners import Drill\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.owl_individual import OWLNamedIndividual, IRI\n",
-    "from ontolearn.metrics import F1\n",
-    "from sklearn.model_selection import StratifiedKFold\n",
-    "from ontolearn.utils.static_funcs import compute_f1_score\n",
-    "from owlapy.render import DLSyntaxObjectRenderer"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "happy-colorado",
-   "metadata": {},
-   "source": [
-    "Open `uncle_lp.json` where we have stored the learning problem for the concept of 'Uncle' and the path to the 'family' ontology."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "buried-miami",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "with open('uncle_lp.json') as json_file:\n",
-    "    settings = json.load(json_file)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "refined-yellow",
-   "metadata": {},
-   "source": [
-    "Create an instance of the class `KnowledeBase` by using the path that is stored in `settings`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "outdoor-player",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "kb = KnowledgeBase(path=settings['data_path'])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fabulous-sucking",
-   "metadata": {},
-   "source": [
-    "Retreive the IRIs of the positive and negative examples of Uncle from `settings` and create an instance of `StratifiedKFold` so that we can create a train and a test set."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "right-organizer",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "examples = settings['Uncle']\n",
-    "p = set(examples['positive_examples'])\n",
-    "n = set(examples['negative_examples'])\n",
-    "\n",
-    "kf = StratifiedKFold(n_splits=10, shuffle=True, random_state=1)\n",
-    "X = np.array(p + n)\n",
-    "Y = np.array([1.0 for _ in p] + [0.0 for _ in n])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "earlier-peripheral",
-   "metadata": {},
-   "source": [
-    "Create a model of [DRILL](ontolearn.learners.drill)."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "permanent-alabama",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "model = Drill(knowledge_base=kb, path_pretrained_kge=\"../embeddings/ConEx_Family/ConEx_entity_embeddings.csv\",\n",
-    "                  quality_func=F1(), max_runtime=10)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c23ee156",
-   "metadata": {},
-   "source": [
-    "1. For each training/testing set create a learning problem of type `PosNegLPStandard`.\n",
-    "2. Fit the training learning problem to the drill model and retrieve the top predicion.\n",
-    "3. Compute the F1 score of the prediction on the train and test sets.\n",
-    "4. Print the prediction together with the quality."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "263df5aa-a8c6-466a-9cb0-d82125b6a852",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "for (ith, (train_index, test_index)) in enumerate(kf.split(X, Y)):\n",
-    "    #  (1)\n",
-    "    train_pos = {pos_individual for pos_individual in X[train_index][Y[train_index] == 1]}\n",
-    "    train_neg = {neg_individual for neg_individual in X[train_index][Y[train_index] == 0]}\n",
-    "    test_pos = {pos_individual for pos_individual in X[test_index][Y[test_index] == 1]}\n",
-    "    test_neg = {neg_individual for neg_individual in X[test_index][Y[test_index] == 0]}\n",
-    "    train_lp = PosNegLPStandard(pos=set(map(OWLNamedIndividual, map(IRI.create, train_pos))),\n",
-    "                                neg=set(map(OWLNamedIndividual, map(IRI.create, train_neg))))\n",
-    "\n",
-    "    test_lp = PosNegLPStandard(pos=set(map(OWLNamedIndividual, map(IRI.create, test_pos))),\n",
-    "                               neg=set(map(OWLNamedIndividual, map(IRI.create, test_neg))))\n",
-    "    \n",
-    "    #  (2)\n",
-    "    pred_drill = model.fit(train_lp).best_hypotheses(n=1)\n",
-    "\n",
-    "    #  (3)\n",
-    "    train_f1_drill = compute_f1_score(individuals={i for i in kb.individuals(pred_drill.concept)},\n",
-    "                                              pos=train_lp.pos,\n",
-    "                                              neg=train_lp.neg)\n",
-    "    test_f1_drill = compute_f1_score(individuals={i for i in kb.individuals(pred_drill.concept)},\n",
-    "                                     pos=test_lp.pos,\n",
-    "                                     neg=test_lp.neg)\n",
-    "    \n",
-    "    #  (4)\n",
-    "    print(f\"Prediction: {DLSyntaxObjectRenderer().render(pred_drill.concept)} |\"\n",
-    "          f\"Train Quality: {train_f1_drill:.3f} |\"\n",
-    "          f\"Test Quality: {test_f1_drill:.3f} \\n\")"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.9.18"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/evolearner_notebook.ipynb b/examples/evolearner_notebook.ipynb
deleted file mode 100644
index 27d19a20..00000000
--- a/examples/evolearner_notebook.ipynb
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "cells": [
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "unlike-landscape",
-   "metadata": {},
-   "source": [
-    "# EvoLearner Notebook\n",
-    "This is a jupyter notebook file to execute [EvoLearner](ontolearn.concept_learner.EvoLearner) and generate predictive results. We recommend you to see the [concept learners](../docs/usage/06_concept_learners.md) guide before continuing with the execution. Also if you have not done it already, from the main directory \"Ontolearn\", run the commands for Datasets mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download the datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "numerous-algorithm",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.concept_learner import EvoLearner\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.owl_individual import OWLNamedIndividual, IRI\n"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "direct-color",
-   "metadata": {},
-   "source": [
-    "Open `uncle_lp.json` where we have stored the learning problem for the concept of 'Uncle' and the path to the 'family' ontology."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "modular-trunk",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "with open('uncle_lp.json') as json_file:\n",
-    "    settings = json.load(json_file)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "creative-necklace",
-   "metadata": {},
-   "source": [
-    "Create an instance of the class `KnowledeBase` by using the path that is stored in `settings`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "wanted-reward",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "kb = KnowledgeBase(path=settings['data_path'])"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "eleven-creation",
-   "metadata": {},
-   "source": [
-    "Retreive the IRIs of the positive and negative examples of Uncle from `settings` and create an instance of `PosNegLPStandard`. (more info about this [here](../docs/usage/06_concept_learners.md#configure-the-learning-problem))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "entitled-cooling",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "examples = settings['Uncle']\n",
-    "p = set(examples['positive_examples'])\n",
-    "n = set(examples['negative_examples'])\n",
-    "typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))\n",
-    "typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))\n",
-    "lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "clear-tracy",
-   "metadata": {},
-   "source": [
-    "Create a model of EvoLearner and fit the learning problem to the model."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "prescribed-explosion",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<ontolearn.concept_learner.EvoLearner at 0x1d92907b040>"
-      ]
-     },
-     "execution_count": 14,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "model = EvoLearner(knowledge_base=kb, max_runtime=600)\n",
-    "model.fit(lp)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "exempt-monaco",
-   "metadata": {},
-   "source": [
-    "Retrieve top 3 hypotheses and print them."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "proud-dependence",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "<class 'ontolearn.search.EvoLearnerNode'> at 0x0bd309\tMale ⊓ ((∃ married.((∃ married.Sister) ⊔ (∃ hasSibling.Parent))) ⊔ (∃ hasSibling.Parent))\tQuality:1.0\tLength:15\tTree Length:11\tTree Depth:5\t|Indv.|:38\n",
-      "<class 'ontolearn.search.EvoLearnerNode'> at 0x0b62f9\tMale ⊓ ((∃ married.PersonWithASibling) ⊔ (∃ hasSibling.Parent))\tQuality:0.97368\tLength:9\tTree Length:7\tTree Depth:3\t|Indv.|:53\n",
-      "<class 'ontolearn.search.EvoLearnerNode'> at 0x0ba7a4\tMale ⊓ ((∃ married.PersonWithASibling) ⊔ (∃ hasSibling.Parent))\tQuality:0.97368\tLength:9\tTree Length:7\tTree Depth:3\t|Indv.|:53\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "[None, None, None]"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "hypotheses = list(model.best_hypotheses(n=3))\n",
-    "[print(_) for _ in hypotheses]"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.10"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/example_knowledge_base.py b/examples/example_knowledge_base.py
index eec7e298..2278443f 100644
--- a/examples/example_knowledge_base.py
+++ b/examples/example_knowledge_base.py
@@ -1,65 +1,33 @@
-from ontolearn.concept_generator import ConceptGenerator
 from ontolearn.knowledge_base import KnowledgeBase
-import os
-
-try:
-    os.chdir("examples")
-except FileNotFoundError:
-    pass
-
-kb_path = '../KGs/Family/family-benchmark_rich_background.owl'
-kb = KnowledgeBase(path=kb_path)
-generator = ConceptGenerator()
-
-# All concepts.
-for i in kb.get_concepts():
-    print(i)
-print('*' * 100)
-
-# All object properties.
-for i in kb.get_object_properties():
-    print(i)
-print('*' * 100)
-
-# All individuals.
-for i in kb.all_individuals_set():
-    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():
-    print(i.str)
-print('*' * 100)
-
-# Direct concept hierarchy from Top to Bottom.
-for concept in kb.class_hierarchy.items():
-    print(f'{concept.str} => {[c.str for c in kb.get_direct_sub_concepts(concept)]}')
-print('*' * 100)
-
-
-for concept in kb.most_general_existential_restrictions(domain=generator.thing):
-    print(concept)
-
-print('*' * 100)
-for concept in kb.most_general_universal_restrictions(domain=generator.thing):
-    print(concept)
-
-print('*' * 100)
-for concept in kb.most_general_existential_restrictions(domain=generator.nothing):
-    print(concept)
-
-print('*' * 100)
-for concept in kb.most_general_universal_restrictions(domain=generator.nothing):
-    print(concept)
-
-print('*' * 100)
-for c in kb.get_concepts():
-    neg_c = generator.negation(c)
-    neg_neg_c = generator.negation(neg_c)
-    assert neg_neg_c == c
+import argparse
+
+def example(args):
+    kb = KnowledgeBase(path=args.path_kb)
+    # All concepts.
+    for i in kb.get_concepts():
+        print(i)
+    print('*' * 100)
+    # All object properties.
+    for i in kb.get_object_properties():
+        print(i)
+    print('*' * 100)
+    # All individuals.
+    for i in kb.all_individuals_set():
+        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():
+        print(i.str)
+    print('*' * 100)
+    # Direct concept hierarchy from Top to Bottom.
+    for concept in kb.class_hierarchy.items():
+        print(f'{concept.str} => {[c.str for c in kb.get_direct_sub_concepts(concept)]}')
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(add_help=False)
+    parser.add_argument("--path_kb", type=str, default="KGs/Family/family-benchmark_rich_background.owl")
+    example(parser.parse_args())
\ No newline at end of file
diff --git a/examples/example_reasoner.py b/examples/example_reasoner.py
index 566f3242..c51ad106 100644
--- a/examples/example_reasoner.py
+++ b/examples/example_reasoner.py
@@ -4,7 +4,7 @@
 from owlapy.owl_individual import OWLNamedIndividual
 from owlapy.owl_property import OWLDataProperty, OWLObjectProperty
 
-from owlapy.owl_reasoner import OntologyReasoner, BaseReasoner, SyncReasoner
+from owlapy.owl_reasoner import StructuralReasoner, SyncReasoner
 from ontolearn.knowledge_base import KnowledgeBase
 
 data_file = '../KGs/Test/test_ontology.owl'
@@ -127,14 +127,9 @@
 
 # onto.save(IRI.create('test' + '.owl'))
 
-base_reasoner = OntologyReasoner(onto)
-# reasoner = FastInstanceCheckerReasoner(
-#                     onto,
-#                     base_reasoner,
-#                     negation_default=True,
-#                     sub_properties=True)
+reasoner = StructuralReasoner(onto, negation_default=True, sub_properties=True)
 
-reasoner = SyncReasoner(onto, BaseReasoner.HERMIT)
+# sync_reasoner = SyncReasoner(data_file, "HermiT") # if you use this you have to remove `only_named` argument:
 
 t1 = list(reasoner.instances(N))
 t2 = list(reasoner.instances(r7E))
diff --git a/examples/litserve_retrieval_eval.py b/examples/litserve_retrieval_eval.py
new file mode 100644
index 00000000..171cd61f
--- /dev/null
+++ b/examples/litserve_retrieval_eval.py
@@ -0,0 +1,209 @@
+import asyncio
+import aiohttp
+import time
+import pandas as pd
+from tqdm import tqdm
+import random
+import itertools
+from argparse import ArgumentParser
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.owl_neural_reasoner import TripleStoreNeuralReasoner
+from ontolearn.utils import concept_reducer, concept_reducer_properties
+from itertools import chain
+from owlapy.class_expression import (
+	OWLObjectUnionOf,
+	OWLObjectIntersectionOf,
+	OWLObjectSomeValuesFrom,
+	OWLObjectAllValuesFrom,
+	OWLObjectMinCardinality,
+	OWLObjectMaxCardinality,
+	OWLObjectOneOf,
+)
+from owlapy.owl_individual import OWLNamedIndividual
+from owlapy import owl_expression_to_dl
+from owlapy.iri import IRI
+
+# Asynchronous function to query the neural reasoner API
+async def async_neural_retrieval(session, url, expression, namespace):
+	"""
+	Asynchronously queries the neural reasoner API to retrieve individuals satisfying the given OWL expression.	
+	"""
+	payload = {'expression': owl_expression_to_dl(expression), 'namespace': namespace}
+	async with session.post(url + '/predict', json=payload) as response:
+		result = await response.json()
+		return set(result['retrieval_result'])
+
+def generate_concepts_from_kb(symbolic_kb: KnowledgeBase):
+	"""
+	Generates concepts from the knowledge base, including named concepts, negations, unions, intersections,
+	existential restrictions, and other combinations as described.
+	"""
+	object_properties = {i for i in symbolic_kb.get_object_properties()}
+	
+	
+	# (4) R⁻: Inverse of object properties.
+	object_properties_inverse = {i.get_inverse_property() for i in object_properties}
+
+	# (5) R*: R UNION R⁻.
+	object_properties_and_inverse = object_properties.union(object_properties_inverse)
+	# (6) NC: Named owl concepts.
+	nc = {i for i in symbolic_kb.get_concepts()}
+	# (7) NC⁻: Complement of NC.
+	nnc = {i.get_object_complement_of() for i in nc}
+	# (8) NC*: NC UNION NC⁻.
+	nc_star = nc.union(nnc)
+	# (9) Retrieve 10 random Nominals.
+	nominals = symbolic_kb.all_individuals_set()
+	# (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*.
+	unions_nc_star = concept_reducer(nc_star, opt=OWLObjectUnionOf)
+	# (14) NC* INTERACTION NC*.
+	intersections_nc_star = concept_reducer(nc_star, opt=OWLObjectIntersectionOf)
+	# (15) \exist r. C s.t. C \in NC* and r \in R* .
+	exist_nc_star = concept_reducer_properties(
+		concepts=nc_star,
+		properties=object_properties_and_inverse,
+		cls=OWLObjectSomeValuesFrom,
+	)
+	# (16) \forall r. C s.t. C \in NC* and r \in R* .
+	for_all_nc_star = concept_reducer_properties(
+		concepts=nc_star,
+		properties=object_properties_and_inverse,
+		cls=OWLObjectAllValuesFrom,
+	)
+	# (17) >= n r. C  and =< n r. C, s.t. C \in NC* and r \in R* .
+	min_cardinality_nc_star_1, min_cardinality_nc_star_2, min_cardinality_nc_star_3 = (
+		concept_reducer_properties(
+			concepts=nc_star,
+			properties=object_properties_and_inverse,
+			cls=OWLObjectMinCardinality,
+			cardinality=i,
+		)
+		for i in [1, 2, 3]
+	)
+	max_cardinality_nc_star_1, max_cardinality_nc_star_2, max_cardinality_nc_star_3 = (
+		concept_reducer_properties(
+			concepts=nc_star,
+			properties=object_properties_and_inverse,
+			cls=OWLObjectMaxCardinality,
+			cardinality=i,
+		)
+		for i in [1, 2, 3]
+	)
+	# (18) \exist r. Nominal s.t. Nominal \in Nominals and r \in R* .
+	exist_nominals = concept_reducer_properties(
+		concepts=nominal_combinations,
+		properties=object_properties_and_inverse,
+		cls=OWLObjectSomeValuesFrom,
+	)
+
+	concepts = list(
+		chain(
+			nc,           # named concepts          (C)
+			nnc,                   # negated named concepts  (\neg C)
+			unions_nc_star,        # A set of Union of named concepts and negat
+			intersections_nc_star, #
+			exist_nc_star,
+			for_all_nc_star,
+			min_cardinality_nc_star_1, min_cardinality_nc_star_1, min_cardinality_nc_star_3,
+			max_cardinality_nc_star_1, max_cardinality_nc_star_2, max_cardinality_nc_star_3,
+			exist_nominals))
+			
+	random.shuffle(concepts)  
+	return concepts
+
+def execute(args):
+	"""
+	Executes the retrieval runtime evaluation for the neural reasoner API.
+	"""
+	# Initialize the normal reasoner using OwlReady2
+	kb = KnowledgeBase(path=args.path_kg)
+	local_reasoner = TripleStoreNeuralReasoner(path_of_kb=args.path_kg)
+
+	# Generate concepts from the KB
+	concepts = generate_concepts_from_kb(kb) 
+
+	# Prepare data storage
+	data = []
+	total_local_runtime = 0
+	total_litserve_runtime = 0
+
+	# Start asynchronous session for neural reasoner API
+	async def main():
+		nonlocal total_local_runtime, total_litserve_runtime
+		async with aiohttp.ClientSession() as session:
+			tasks = []
+			for expression in tqdm(concepts, desc="Preparing tasks"):
+				namespace = args.namespace  # Assume a fixed namespace for all expressions
+				tasks.append(
+					asyncio.ensure_future(
+						async_neural_retrieval(
+							session, args.neural_reasoner_url, expression, namespace
+						)
+					)
+				)
+
+			# Measure retrieval times
+			results = []
+			for idx, expression in enumerate(tqdm(concepts, desc="Evaluating expressions")):
+				dl_expression = owl_expression_to_dl(expression)
+				
+				# Normal reasoner retrieval
+				start_time = time.time()
+				individuals_local_gen = local_reasoner.individuals(expression)
+				individuals_local = set(individuals_local_gen)
+				time_local = time.time() - start_time
+				total_local_runtime += time_local
+
+				# Neural reasoner retrieval (already scheduled)
+				start_time = time.time()
+				individuals_litserve = await tasks[idx]
+				time_litserve = time.time() - start_time  
+				total_litserve_runtime += time_litserve
+				individuals_litserve = {OWLNamedIndividual(IRI.create(namespace, ind)) for ind in individuals_litserve}
+
+				if individuals_local != individuals_litserve:
+					print(f"Error in retrieval for expression: {dl_expression}")
+					print(f"Local: {individuals_local}")
+					print(f"Litserve: {individuals_litserve}")
+					print()
+				# Store data
+				data.append({
+					"Expression": dl_expression,
+					"Type": type(expression).__name__,
+					"Runtime Local": time_local,
+					"Runtime litserve": time_litserve,
+					"Runtime Difference": time_local - time_litserve,
+					"Individuals Normal": individuals_local,
+					"Individuals Neural": individuals_litserve,
+				})
+
+	# Run the asynchronous main function
+	asyncio.run(main())
+
+	if total_litserve_runtime > 0:
+		speedup_factor = total_local_runtime / total_litserve_runtime
+		print(f"Litserve is, on average, {speedup_factor:.2f} times faster than the local retrieval.")
+	# Save results to DataFrame
+	df = pd.DataFrame(data)
+	df.to_csv(args.path_report, index=False)
+
+	# Display summary
+	df_summary = df.groupby("Type").agg({
+		"Runtime Local": "mean",
+		"Runtime litserve": "mean",
+		"Runtime Difference": "mean"
+	})
+	print(df_summary)
+
+def get_default_arguments():
+	parser = ArgumentParser()
+	parser.add_argument("--path_kg", type=str, default="KGs/Family/father.owl", help="Path to the ontology file")
+	parser.add_argument("--neural_reasoner_url", type=str, default="http://localhost:8000", help="URL of the neural reasoner API")
+	parser.add_argument("--namespace", type=str, default="http://example.com/father#", help="Default namespace")
+	parser.add_argument("--path_report", type=str, default="retrieval_runtime_results.csv", help="Path to save the report")
+	return parser.parse_args()
+
+if __name__ == "__main__":
+	execute(get_default_arguments())
diff --git a/examples/nces_notebook1.ipynb b/examples/nces_notebook1.ipynb
deleted file mode 100644
index 7732c54d..00000000
--- a/examples/nces_notebook1.ipynb
+++ /dev/null
@@ -1,699 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "assured-bible",
-   "metadata": {},
-   "source": [
-    "From the main directory \"Ontolearn\", run the commands for NCES data mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download pretrained models and datasets."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "pleasant-transaction",
-   "metadata": {},
-   "source": [
-    "## Inference with NCES"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "id": "romantic-valley",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/opt/conda/envs/onto_env/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
-      "  from .autonotebook import tqdm as notebook_tqdm\n"
-     ]
-    }
-   ],
-   "source": [
-    "from ontolearn.concept_learner import NCES\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.metrics import F1\n",
-    "from owlapy.parser import DLSyntaxParser"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "streaming-payday",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces = NCES(knowledge_base_path=\"../NCESData/family/family.owl\", quality_func=F1(), num_predictions=100, learner_names=[\"SetTransformer\"],\n",
-    "     path_of_embeddings=\"../NCESData/family/embeddings/ConEx_entity_embeddings.csv\", load_pretrained=True, max_length=48, proj_dim=128, rnn_n_layers=2, drop_prob=0.1, num_heads=4, num_seeds=1, num_inds=32)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "fifth-sampling",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "KB = KnowledgeBase(path=nces.knowledge_base_path)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "compatible-mexico",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "dl_parser = DLSyntaxParser(nces.kb_namespace)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "durable-venice",
-   "metadata": {},
-   "source": [
-    "### Let's learn the concept ``Father''"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "eleven-director",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "father = dl_parser.parse('Father')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "graphic-context",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "not_father = dl_parser.parse('¬Father') # For negative examples"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "hollow-consultancy",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "pos = set([ind.str.split(\"/\")[-1] for ind in KB.individuals(father)])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "subjective-occurrence",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "neg = set([ind.str.split(\"/\")[-1] for ind in KB.individuals(not_father)])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "interstate-center",
-   "metadata": {},
-   "source": [
-    "#### Prediction with SetTransformer"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "becoming-python",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "lp = PosNegLPStandard(pos=pos, neg=neg)\n",
-    "nodes = nces.fit(lp).best_hypotheses(3)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "fabulous-preliminary",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[OWLObjectIntersectionOf((OWLClass(IRI('http://www.benchmark.org/family#','Father')), OWLObjectUnionOf((OWLClass(IRI('http://www.benchmark.org/family#','Father')), OWLObjectSomeValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','hasParent')),filler=OWLClass(IRI('http://www.benchmark.org/family#','PersonWithASibling'))))), OWLObjectAllValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','hasParent')),filler=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing'))))),\n",
-       " OWLObjectUnionOf((OWLClass(IRI('http://www.benchmark.org/family#','Father')), OWLClass(IRI('http://www.benchmark.org/family#','Father')), OWLClass(IRI('http://www.benchmark.org/family#','Grandfather')), OWLClass(IRI('http://www.benchmark.org/family#','Grandfather')))),\n",
-       " OWLClass(IRI('http://www.benchmark.org/family#','Father'))]"
-      ]
-     },
-     "execution_count": 10,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nodes"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "harmful-consumer",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "False"
-      ]
-     },
-     "execution_count": 11,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nces.sorted_examples"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "filled-canvas",
-   "metadata": {},
-   "source": [
-    "#### Prediction with GRU"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "necessary-illinois",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.learner_names = ['GRU']\n",
-    "nces.refresh()\n",
-    "nodes = list(nces.fit(lp).best_predictions)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "gothic-listing",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[<class 'ontolearn.search.NCESNode'> at 0x69d8928\tFather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x70389f8\tFather\tQuality:1.0\tLength:1\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7038a54\tFather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69d8884\tGrandfather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69db160\tFather ⊔ Grandfather\tQuality:1.0\tLength:3\t|Indv.|:60]"
-      ]
-     },
-     "execution_count": 13,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nodes[:5]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bizarre-practice",
-   "metadata": {},
-   "source": [
-    "#### Prediction with LSTM"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "certain-corner",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.learner_names = ['LSTM']\n",
-    "nces.refresh()\n",
-    "nodes = list(nces.fit(lp).best_predictions)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "renewable-virgin",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[<class 'ontolearn.search.NCESNode'> at 0x69da730\tFather\tQuality:1.0\tLength:1\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7035258\tMale ⊓ (∃ hasChild.(Female ⊔ (∃ hasParent.(¬Daughter))))\tQuality:1.0\tLength:10\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69bcd10\tFather ⊓ (Father ⊓ (Male ⊔ (∃ hasSibling.(¬Grandfather))))\tQuality:1.0\tLength:10\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69d93b0\tFather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7038a78\tFather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60]"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nodes[:5]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "public-cement",
-   "metadata": {},
-   "source": [
-    "#### Prediction with ensemble SetTransformer+GRU+LSTM"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "upset-toyota",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.learner_names = ['SetTransformer','GRU','LSTM']\n",
-    "nces.refresh() # Load model weights\n",
-    "nodes = list(nces.fit(lp).best_predictions)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "polyphonic-kansas",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[<class 'ontolearn.search.NCESNode'> at 0x69d8970\tFather ⊔ Grandfather\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69d889c\tFather ⊔ Grandfather\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x702cc18\tFather\tQuality:1.0\tLength:1\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69d7358\tFather ⊔ Father\tQuality:1.0\tLength:3\t|Indv.|:60,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x69d7274\tFather\tQuality:1.0\tLength:1\t|Indv.|:60]"
-      ]
-     },
-     "execution_count": 17,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nodes[:5]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "silent-authorization",
-   "metadata": {},
-   "source": [
-    "### Scalability of NCES (solving multiple learning problems in a go!)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "id": "textile-wildlife",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json, time"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "id": "subjective-columbia",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "with open(\"../NCESData/family/training_data/Data.json\") as file:\n",
-    "    data = list(json.load(file).items())\n",
-    "## The function below takes an iterable of tuples\n",
-    "LPs = list(map(lambda x: (x[0], x[1][\"positive examples\"], x[1][\"negative examples\"]), data))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "id": "cathedral-gibson",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "## We solve 256 learning problems!\n",
-    "lps = LPs[:256]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "id": "improved-pickup",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Duration:  0.46558618545532227 seconds!\n"
-     ]
-    }
-   ],
-   "source": [
-    "t0 = time.time()\n",
-    "concepts = nces.fit_from_iterable(lps, verbose=False) ## Now predict with nces.fit_from_iterable.\n",
-    "t1 = time.time()\n",
-    "print(\"Duration: \", t1-t0, \"seconds!\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 22,
-   "id": "filled-sunglasses",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "OWLClass(IRI('http://www.benchmark.org/family#','Sister'))\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(concepts[0])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 23,
-   "id": "killing-conspiracy",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "256\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(len(concepts))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "august-classics",
-   "metadata": {},
-   "source": [
-    "### Change pretrained model name, e.g., use ensemble model prediction"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 24,
-   "id": "portuguese-thinking",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "nces.learner_names = ['SetTransformer', 'GRU']"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "id": "instant-hearts",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['SetTransformer', 'GRU']"
-      ]
-     },
-     "execution_count": 25,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nces.learner_names"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "several-bailey",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.refresh()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "elementary-kitchen",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Duration:  0.8283030986785889 seconds!\n"
-     ]
-    }
-   ],
-   "source": [
-    "t0 = time.time()\n",
-    "concepts = nces.fit_from_iterable(lps, verbose=False)\n",
-    "t1 = time.time()\n",
-    "print(\"Duration: \", t1-t0, \"seconds!\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "least-training",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "OWLObjectAllValuesFrom(property=OWLObjectProperty(IRI('http://www.benchmark.org/family#','married')),filler=OWLObjectComplementOf(OWLClass(IRI('http://www.benchmark.org/family#','PersonWithASibling'))))"
-      ]
-     },
-     "execution_count": 28,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "concepts[-1]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "australian-layout",
-   "metadata": {},
-   "source": [
-    "## Training NCES"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "constitutional-style",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json, time\n",
-    "with open(\"../NCESData/family/training_data/Data.json\") as file:\n",
-    "    data = json.load(file) # Training data. Below we use the first 200 datapoints to train the synthesizer. You train on the full data, e.g. on GPU by remobing \"[:200]\""
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "exterior-illinois",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "from ontolearn.concept_learner import NCES"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 31,
-   "id": "sticky-artist",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "!!!Returning untrained models, could not load pretrained. Check the `load_pretrained parameter` or train the models using NCES.train(data).\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces = NCES(knowledge_base_path=\"../NCESData/family/family.owl\", learner_names=[\"SetTransformer\"],\n",
-    "     path_of_embeddings=\"../NCESData/family/embeddings/ConEx_entity_embeddings.csv\", max_length=48, proj_dim=128, rnn_n_layers=2, drop_prob=0.1, num_heads=4, num_seeds=1, num_inds=32,\n",
-    "            load_pretrained=False)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "id": "general-adult",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "********************Trainable model size********************\n",
-      "Synthesizer:  515296\n",
-      "********************Trainable model size********************\n",
-      "\n",
-      "GPU available !\n",
-      "\n",
-      "##################################################\n",
-      "\n",
-      "SetTransformer starts training... \n",
-      "\n",
-      "################################################## \n",
-      "\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "Loss: 0.4141, Soft Acc: 72.98%, Hard Acc: 72.07%: 100%|██████████| 20/20 [00:05<00:00,  3.43it/s]"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Top performance: loss: 0.4141, soft accuracy: 77.84% ... hard accuracy: 76.40%\n",
-      "\n",
-      "SetTransformer saved\n"
-     ]
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.train(list(data.items())[-200:], epochs=20, learning_rate=0.001, save_model=True)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "advised-cause",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "challenging-relations",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "onto_env",
-   "language": "python",
-   "name": "onto_env"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.10.14"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/nces_notebook2.ipynb b/examples/nces_notebook2.ipynb
deleted file mode 100644
index 86f756ed..00000000
--- a/examples/nces_notebook2.ipynb
+++ /dev/null
@@ -1,693 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "atmospheric-marker",
-   "metadata": {},
-   "source": [
-    "From the main directory \"Ontolearn\", run the commands for NCES data mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download pretrained models and datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "id": "specific-german",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/opt/conda/envs/onto_env/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
-      "  from .autonotebook import tqdm as notebook_tqdm\n"
-     ]
-    }
-   ],
-   "source": [
-    "from ontolearn.concept_learner import NCES\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.parser import DLSyntaxParser\n",
-    "from owlapy.render import DLSyntaxObjectRenderer\n",
-    "import sys\n",
-    "sys.path.append(\"examples/\")\n",
-    "from ontolearn.metrics import F1\n",
-    "from quality_functions import quality\n",
-    "import time\n",
-    "import random"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "functional-messaging",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces = NCES(knowledge_base_path=\"../NCESData/family/family.owl\", quality_func=F1(), num_predictions=100, learner_names=[\"SetTransformer\"],\n",
-    "     path_of_embeddings=\"../NCESData/family/embeddings/ConEx_entity_embeddings.csv\", load_pretrained=True, max_length=48, proj_dim=128, rnn_n_layers=2, drop_prob=0.1, num_heads=4, num_seeds=1, num_inds=32)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "meaningful-hindu",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "KB = KnowledgeBase(path=nces.knowledge_base_path)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "divine-denial",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "dl_syntax_renderer = DLSyntaxObjectRenderer()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "written-threshold",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "atomic_classes = [dl_syntax_renderer.render(a) for a in KB.ontology.classes_in_signature()]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "pleasant-hebrew",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['Brother',\n",
-       " 'Male',\n",
-       " 'PersonWithASibling',\n",
-       " 'Child',\n",
-       " 'Person',\n",
-       " 'Daughter',\n",
-       " 'Female',\n",
-       " 'Father',\n",
-       " 'Parent',\n",
-       " 'Grandchild',\n",
-       " 'Granddaughter',\n",
-       " 'Grandfather',\n",
-       " 'Grandparent',\n",
-       " 'Grandmother',\n",
-       " 'Grandson',\n",
-       " 'Mother',\n",
-       " 'Sister',\n",
-       " 'Son']"
-      ]
-     },
-     "execution_count": 6,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "atomic_classes"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "lovely-confusion",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "dl_parser = DLSyntaxParser(nces.kb_namespace)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "false-solution",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "brother = dl_parser.parse('Brother')\n",
-    "daughter = dl_parser.parse('Daughter')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "rolled-thursday",
-   "metadata": {},
-   "source": [
-    "#### Input examples can be sets or lists"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "ahead-threshold",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "pos = set(KB.individuals(brother)).union(set(KB.individuals(daughter)))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "serial-music",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "neg = set(KB.individuals())-set(pos)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "raised-royalty",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "120"
-      ]
-     },
-     "execution_count": 11,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "len(neg)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "solid-syndicate",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "lp = PosNegLPStandard(pos=pos, neg=neg)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "failing-disclaimer",
-   "metadata": {},
-   "source": [
-    "#### Prediction with SetTransformer (default model)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "bound-decline",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Duration:  0.48354291915893555  seconds\n"
-     ]
-    }
-   ],
-   "source": [
-    "t0 = time.time()\n",
-    "node = list(nces.fit(lp).best_predictions)[0]\n",
-    "t1 = time.time()\n",
-    "print(\"\\nDuration: \", t1-t0, \" seconds\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "current-haven",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "82"
-      ]
-     },
-     "execution_count": 14,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "len(set(KB.individuals(node.concept)))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "lasting-stage",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<class 'ontolearn.search.NCESNode'> at 0x75f30ec\tBrother ⊔ (Person ⊓ (Daughter ⊔ (∃ hasSibling.Granddaughter)))\tQuality:1.0\tLength:9\t|Indv.|:82"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "node"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "sweet-standing",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Accuracy: 100.0%\n",
-      "Precision: 100.0%\n",
-      "Recall: 100.0%\n",
-      "F1: 100.0%\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(100.0, 100.0, 100.0, 100.0)"
-      ]
-     },
-     "execution_count": 16,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "quality(KB, node.concept, pos, neg)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "three-polymer",
-   "metadata": {},
-   "source": [
-    "### Ensemble prediction"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "informal-poetry",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " Loaded NCES weights!\n",
-      "\n",
-      "\n",
-      "Duration:  0.4260680675506592  seconds\n"
-     ]
-    }
-   ],
-   "source": [
-    "nces.learner_names = ['SetTransformer','GRU','LSTM']\n",
-    "nces.refresh() # Loads model weights\n",
-    "t0 = time.time()\n",
-    "node = list(nces.fit(lp).best_predictions)[0]\n",
-    "t1 = time.time()\n",
-    "print(\"\\nDuration: \", t1-t0, \" seconds\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "id": "least-tribe",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Accuracy: 100.0%\n",
-      "Precision: 100.0%\n",
-      "Recall: 100.0%\n",
-      "F1: 100.0%\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(100.0, 100.0, 100.0, 100.0)"
-      ]
-     },
-     "execution_count": 18,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "quality(KB, node.concept, pos, neg)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "id": "surrounded-roller",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<class 'ontolearn.search.NCESNode'> at 0xa840520\tBrother ⊔ Daughter ⊔ Daughter\tQuality:1.0\tLength:5\t|Indv.|:82"
-      ]
-     },
-     "execution_count": 19,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "node"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "id": "medieval-advance",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[<class 'ontolearn.search.NCESNode'> at 0xa840520\tBrother ⊔ Daughter ⊔ Daughter\tQuality:1.0\tLength:5\t|Indv.|:82,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7c63c78\tPersonWithASibling ⊔ Daughter\tQuality:1.0\tLength:3\t|Indv.|:82,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7c63c58\tBrother ⊔ Daughter\tQuality:1.0\tLength:3\t|Indv.|:82,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7c5ea34\tBrother ⊔ Daughter\tQuality:1.0\tLength:3\t|Indv.|:82,\n",
-       " <class 'ontolearn.search.NCESNode'> at 0x7c5ea74\tPersonWithASibling ⊔ Daughter\tQuality:1.0\tLength:3\t|Indv.|:82]"
-      ]
-     },
-     "execution_count": 20,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nces.best_predictions[:5]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "conditional-dependence",
-   "metadata": {},
-   "source": [
-    "### Complex learning problems, potentially without an exact solution"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fluid-crawford",
-   "metadata": {},
-   "source": [
-    "#### First learning problem"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "id": "revised-major",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "all_individuals = set(KB.individuals())\n",
-    "pos = set(random.sample(list(all_individuals), 150))\n",
-    "remaining = all_individuals-pos\n",
-    "neg = set(random.sample(list(remaining), min(100, len(remaining))))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 22,
-   "id": "developmental-tender",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "lp = PosNegLPStandard(pos=pos, neg=neg)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 23,
-   "id": "failing-patrick",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['SetTransformer', 'GRU', 'LSTM']"
-      ]
-     },
-     "execution_count": 23,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "nces.learner_names"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 24,
-   "id": "balanced-linux",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Duration:  0.3714330196380615  seconds\n"
-     ]
-    }
-   ],
-   "source": [
-    "t0 = time.time()\n",
-    "node = list(nces.fit(lp).best_predictions)[0]\n",
-    "t1 = time.time()\n",
-    "print(\"\\nDuration: \", t1-t0, \" seconds\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "id": "constitutional-lindsay",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<class 'ontolearn.search.NCESNode'> at 0x75f5058\tPerson ⊔ Child\tQuality:0.85227\tLength:3\t|Indv.|:202"
-      ]
-     },
-     "execution_count": 25,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "node"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "legal-broadcasting",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Accuracy: 74.25699999999999%\n",
-      "Precision: 74.25699999999999%\n",
-      "Recall: 100.0%\n",
-      "F1: 85.227%\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(74.25699999999999, 74.25699999999999, 100.0, 85.227)"
-      ]
-     },
-     "execution_count": 26,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "quality(KB, node.concept, pos, neg)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ambient-percentage",
-   "metadata": {},
-   "source": [
-    "#### Second learning problem"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "speaking-plain",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "pos = set(random.sample(list(all_individuals), 80))\n",
-    "remaining = all_individuals-pos\n",
-    "neg = set(random.sample(list(remaining), min(150, len(remaining))))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "upper-oasis",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "lp = PosNegLPStandard(pos=pos, neg=neg)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "weekly-desperate",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      "Duration:  0.37883949279785156  seconds\n"
-     ]
-    }
-   ],
-   "source": [
-    "t0 = time.time()\n",
-    "node = list(nces.fit(lp).best_predictions)[0]\n",
-    "t1 = time.time()\n",
-    "print(\"\\nDuration: \", t1-t0, \" seconds\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "fallen-programming",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Accuracy: 39.604%\n",
-      "Precision: 39.604%\n",
-      "Recall: 100.0%\n",
-      "F1: 56.738%\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(39.604, 39.604, 100.0, 56.738)"
-      ]
-     },
-     "execution_count": 30,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "quality(KB, node.concept, pos, neg)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "administrative-karen",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "markdown",
-   "id": "personal-basketball",
-   "metadata": {},
-   "source": [
-    "## Important note\n",
-    "\n",
-    "- Each of the synthesized expressions are not present in the knowledge base.\n",
-    "- NCES synthesizes solutions by leveraging its experience on the training data.\n",
-    "- The inputs (positive/negative examples) need not be balanced\n",
-    "- NCES can solve multiple learning problems at the same time (through broadcasting on matrix operations in its neural network component), see nces_notebook1.ipynb\n",
-    "- Since LSTM and GRU are not permutation-equivariant, we can get different but closely related solutions by shuflling the input examples for these architectures. For this, one needs to instantiate the NCES class with the attribute \"sorted_examples=False\" which is the case by default."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "atomic-crime",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "onto_env",
-   "language": "python",
-   "name": "onto_env"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.10.14"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/ocel_notebook.ipynb b/examples/ocel_notebook.ipynb
deleted file mode 100644
index 798e40e5..00000000
--- a/examples/ocel_notebook.ipynb
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "cells": [
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "verified-temple",
-   "metadata": {},
-   "source": [
-    "# OCEL Notebook\n",
-    "This is a jupyter notebook file to execute [OCEL](ontolearn.concept_learner.OCEL) and generate predictive results. We recommend you to see the [concept learners](../docs/usage/06_concept_learners.md) guide before continuing with the execution. Also if you have not done it already, from the main directory \"Ontolearn\", run the commands for Datasets mentioned [here](https://ontolearn-docs-dice-group.netlify.app/usage/02_installation#download-external-files) to download the datasets."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "sustainable-poland",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import json\n",
-    "from ontolearn.knowledge_base import KnowledgeBase\n",
-    "from ontolearn.concept_learner import OCEL\n",
-    "from ontolearn.learning_problem import PosNegLPStandard\n",
-    "from owlapy.owl_individual import OWLNamedIndividual, IRI"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "happy-colorado",
-   "metadata": {},
-   "source": [
-    "Open `uncle_lp.json` where we have stored the learning problem for the concept of 'Uncle' and the path to the 'family' ontology."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "buried-miami",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "with open('uncle_lp.json') as json_file:\n",
-    "    settings = json.load(json_file)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "refined-yellow",
-   "metadata": {},
-   "source": [
-    "Create an instance of the class `KnowledeBase` by using the path that is stored in `settings`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "outdoor-player",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "kb = KnowledgeBase(path=settings['data_path'])"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "fabulous-sucking",
-   "metadata": {},
-   "source": [
-    "Retreive the IRIs of the positive and negative examples of Uncle from `settings` and create an instance of `PosNegLPStandard`. (more info about this [here](../docs/usage/06_concept_learners.md#configure-the-learning-problem))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "right-organizer",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "examples = settings['Uncle']\n",
-    "p = set(examples['positive_examples'])\n",
-    "n = set(examples['negative_examples'])\n",
-    "typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))\n",
-    "typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))\n",
-    "lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "earlier-peripheral",
-   "metadata": {},
-   "source": [
-    "Create a model of [OCEL](ontolearn.concept_learner.OCEL) and fit the learning problem to the model."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "permanent-alabama",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "<ontolearn.concept_learner.OCEL at 0x2936feb8820>"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "model = OCEL(knowledge_base=kb, max_runtime=600)\n",
-    "model.fit(lp)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "id": "future-elevation",
-   "metadata": {},
-   "source": [
-    "Retrieve top 3 hypotheses and print them."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "incoming-debate",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "<class 'ontolearn.search.LBLNode'> at 0x8eb85c\t(¬Female) ⊓ (∀ hasSibling.(∃ hasSibling.(∃ hasSibling.Parent)))\tQuality:0.90476\tHeuristic:0.6479\tDepth:12\tH_exp:13\t|RC|:33\t|Indv.|:95\n",
-      "<class 'ontolearn.search.LBLNode'> at 0xb3997c\t(¬Female) ⊓ (∀ hasSibling.(∃ hasSibling.(∃ hasSibling.(∃ hasChild.⊤))))\tQuality:0.90476\tHeuristic:0.63474\tDepth:13\tH_exp:13\t|RC|:5\t|Indv.|:95\n",
-      "<class 'ontolearn.search.LBLNode'> at 0xb4f8b4\t(¬Female) ⊓ (∀ hasSibling.(∃ hasSibling.(∃ hasSibling.(∃ hasChild.Person))))\tQuality:0.90476\tHeuristic:0.63474\tDepth:14\tH_exp:13\t|RC|:9\t|Indv.|:95\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "[None, None, None]"
-      ]
-     },
-     "execution_count": 13,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "hypotheses = list(model.best_hypotheses(n=3))\n",
-    "[print(_) for _ in hypotheses]"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.9.18"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/examples/quality_functions.py b/examples/quality_functions.py
deleted file mode 100644
index 336ec14e..00000000
--- a/examples/quality_functions.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from ontolearn.metrics import F1, Accuracy, Precision, Recall
-
-
-def quality(KB, solution, pos, neg):
-    f1 = F1().score2
-    accuracy = Accuracy().score2
-    precision = Precision().score2
-    recall = Recall().score2
-    instances = set(KB.individuals(solution))
-    if isinstance(list(pos)[0], str):
-        instances = {ind.str.split("/")[-1] for ind in instances}
-    tp = len(pos.intersection(instances))
-    fn = len(pos.difference(instances))
-    fp = len(neg.intersection(instances))
-    tn = len(neg.difference(instances))
-    acc = 100*accuracy(tp, fn, fp, tn)[-1]
-    prec = 100*precision(tp, fn, fp, tn)[-1]
-    rec = 100*recall(tp, fn, fp, tn)[-1]
-    f_1 = 100*f1(tp, fn, fp, tn)[-1]
-    print("Accuracy: {}%".format(acc))
-    print("Precision: {}%".format(prec))
-    print("Recall: {}%".format(rec))
-    print("F1: {}%".format(f_1))
-    return acc, prec, rec, f_1
diff --git a/examples/retrieval_eval.py b/examples/retrieval_eval.py
index 140f2c62..e6226c6e 100644
--- a/examples/retrieval_eval.py
+++ b/examples/retrieval_eval.py
@@ -64,7 +64,12 @@
 import random
 import itertools
 import ast
-
+# Set pandas options to ensure full output
+pd.set_option('display.max_rows', None)
+pd.set_option('display.max_columns', None)
+pd.set_option('display.width', None)
+pd.set_option('display.colheader_justify', 'left')
+pd.set_option('display.expand_frame_repr', False)
 
 def execute(args):
     # (1) Initialize knowledge base.
@@ -86,9 +91,9 @@ def execute(args):
     object_properties = sorted({i for i in symbolic_kb.get_object_properties()})
     
     # (3.1) Subsample if required.
-    if args.ratio_sample_object_prob:
+    if args.ratio_sample_object_prop and len(object_properties) > 0:
         object_properties = {i for i in random.sample(population=list(object_properties),
-                                                      k=max(1, int(len(object_properties) * args.ratio_sample_object_prob)))}
+                                                      k=max(1, int(len(object_properties) * args.ratio_sample_object_prop)))}
 
     object_properties = set(object_properties)    
     
@@ -103,7 +108,7 @@ def execute(args):
 
 
 
-    if args.ratio_sample_nc:
+    if args.ratio_sample_nc and len(nc) > 0:
         # (6.1) Subsample if required.
         nc = {i for i in random.sample(population=list(nc), k=max(1, int(len(nc) * args.ratio_sample_nc)))}
 
@@ -211,7 +216,10 @@ def concept_retrieval(retriever_func, c) -> Tuple[Set[str], float]:
     # () Shuffled the data so that the progress bar is not influenced by the order of concepts.
 
     random.shuffle(concepts)
-
+    # check if csv arleady exists and delete it cause we want to override it
+    if os.path.exists(args.path_report):
+        os.remove(args.path_report)
+    file_exists = False
     # () Iterate over single OWL Class Expressions in ALCQIHO
     for expression in (tqdm_bar := tqdm(concepts, position=0, leave=True)):
         retrieval_y: Set[str]
@@ -225,8 +233,8 @@ def concept_retrieval(retriever_func, c) -> Tuple[Set[str], float]:
         # () Compute the F1-score.
         f1_sim = f1_set_similarity(retrieval_y, retrieval_neural_y)
         # () Store the data.
-        data.append(
-            {
+        df_row = pd.DataFrame(
+            [{
                 "Expression": owl_expression_to_dl(expression),
                 "Type": type(expression).__name__,
                 "Jaccard Similarity": jaccard_sim,
@@ -235,33 +243,35 @@ def concept_retrieval(retriever_func, c) -> Tuple[Set[str], float]:
                 "Runtime Neural": runtime_neural_y,
                 "Symbolic_Retrieval": retrieval_y,
                 "Symbolic_Retrieval_Neural": retrieval_neural_y,
-            }
-        )
+            }])
+        # Append the row to the CSV file
+        df_row.to_csv(args.path_report, mode='a', header=not file_exists, index=False)
+        file_exists = True
         # () Update the progress bar.
         tqdm_bar.set_description_str(
             f"Expression: {owl_expression_to_dl(expression)} | Jaccard Similarity:{jaccard_sim:.4f} | F1 :{f1_sim:.4f} | Runtime Benefits:{runtime_y - runtime_neural_y:.3f}"
         )
     # () Read the data into pandas dataframe
-    df = pd.DataFrame(data)
-    assert df["Jaccard Similarity"].mean() >= args.min_jaccard_similarity
-    # () Save the experimental results into csv file.
-    df.to_csv(args.path_report)
-    del df
-    # () Load the saved CSV file.
     df = pd.read_csv(args.path_report, index_col=0, converters={'Symbolic_Retrieval': lambda x: ast.literal_eval(x),
-                                                                'Symbolic_Retrieval_Neural': lambda x: ast.literal_eval(
-                                                                    x)})
-    # () A retrieval result can be parsed into  set of instances to python object.
+                                                                'Symbolic_Retrieval_Neural': lambda x: ast.literal_eval(x)})
+    # () Assert that the mean Jaccard Similarity meets the threshold
+    assert df["Jaccard Similarity"].mean() >= args.min_jaccard_similarity
+
+    # () Ensure 'Symbolic_Retrieval_Neural' contains sets
     x = df["Symbolic_Retrieval_Neural"].iloc[0]
     assert isinstance(x, set)
-    # () Extract the numerical features.
+
+    # () Extract numerical features
     numerical_df = df.select_dtypes(include=["number"])
-    # () Extract the type of owl concepts
+
+    # () Group by the type of OWL concepts
     df_g = df.groupby(by="Type")
     print(df_g["Type"].count())
+
+    # () Compute mean of numerical columns per group
     mean_df = df_g[numerical_df.columns].mean()
     print(mean_df)
-
+    return jaccard_sim, f1_sim
 
 def get_default_arguments():
     parser = ArgumentParser()
@@ -271,7 +281,7 @@ def get_default_arguments():
     parser.add_argument("--gamma", type=float, default=0.9)
     parser.add_argument("--seed", type=int, default=1)
     parser.add_argument("--ratio_sample_nc", type=float, default=0.2, help="To sample OWL Classes.")
-    parser.add_argument("--ratio_sample_object_prob", type=float, default=0.1, help="To sample OWL Object Properties.")
+    parser.add_argument("--ratio_sample_object_prop", type=float, default=0.1, help="To sample OWL Object Properties.")
     parser.add_argument("--min_jaccard_similarity", type=float, default=0.0, help="Minimum Jaccard similarity to be achieve by the reasoner")
     parser.add_argument("--num_nominals", type=int, default=10, help="Number of OWL named individuals to be sampled.")
 
diff --git a/examples/retrieval_eval_under_incomplete.py b/examples/retrieval_eval_under_incomplete.py
index 2a1d544e..0746497d 100644
--- a/examples/retrieval_eval_under_incomplete.py
+++ b/examples/retrieval_eval_under_incomplete.py
@@ -24,10 +24,11 @@
 from owlapy.parser import DLSyntaxParser
 import ast
 from owlapy import owl_expression_to_dl
+from owlapy.owl_reasoner import SyncReasoner
 from owlapy.owl_ontology_manager import OntologyManager
-from owlapy.owlapi_adaptor import OWLAPIAdaptor
 import pandas as pd
-
+import re
+from owlapy.static_funcs import stopJVM
 
 # Create incomplete/noisy KGs
 def generate_subgraphs(kb_path: str, directory: str, n: int, ratio: float, operation: str) -> Set[str]:
@@ -86,6 +87,7 @@ def generate_subgraphs(kb_path: str, directory: str, n: int, ratio: float, opera
 
     return file_paths
 
+
 def execute(args):
     symbolic_kb = KnowledgeBase(path=args.path_kg)
     namespace = list(symbolic_kb.ontology.classes_in_signature())[0].iri.get_namespace()
@@ -111,29 +113,35 @@ def execute(args):
         data = []
 
         if args.sample == "Yes":
-            subprocess.run(['python', 'examples/retrieval_eval.py', "--path_kg", path, "--ratio_sample_nc","0.1", "--ratio_sample_object_prob", "0.2", "--path_report", path_report])
+            subprocess.run(['python', 'examples/retrieval_eval.py', "--path_kg", path, "--ratio_sample_nc","0.1", "--ratio_sample_object_prop", "0.2", "--path_report", path_report])
         else:
             subprocess.run(['python', 'examples/retrieval_eval.py', "--path_kg", path, "--path_report", path_report])
         
-        df = pd.read_csv(f"{directory}/ALCQHI_Retrieval_Results.csv", index_col=0)
-        
-        expressions = {i for i in df["Expression"].to_list()}
 
+        df = pd.read_csv(f"{directory}/ALCQHI_Retrieval_Results.csv")
+
+        # Extract expressions
+        expressions = df["Expression"].tolist()
+        
         ontology_path = path
         reasoners = ['HermiT', 'Pellet', 'JFact', 'Openllet']
         reasoner_jaccards = {reasoner: [] for reasoner in reasoners}
         reasoner_times = {reasoner: [] for reasoner in reasoners}  # To store running times
 
 
-        owlapi_adaptor = OWLAPIAdaptor(path=ontology_path, name_reasoner='HermiT')
+        hermit_reasoner = SyncReasoner(ontology=ontology_path, reasoner='HermiT')
 
-        if owlapi_adaptor.has_consistent_ontology():
+        if hermit_reasoner.has_consistent_ontology():
 
             for expression in expressions:
-                
-                print("-"*100)
+                print("-" * 100)
                 print("Expression:", expression)
-                target_concept = parser.parse_expression(expression)
+                try:
+                    target_concept = parser.parse_expression(expression)
+                except Exception as e:
+                    print(f"Failed to parse expression: {expression}")
+                    print(e)
+                    continue
                 goal_retrieval = {i.str for i in symbolic_kb.individuals(target_concept)}
                 result_neural_symbolic = df[df["Expression"] == expression]["Symbolic_Retrieval_Neural"].apply(ast.literal_eval).iloc[0]
                 jaccard_sim_neural = jaccard_similarity(result_neural_symbolic, goal_retrieval)
@@ -150,13 +158,13 @@ def execute(args):
 
                 for reasoner in reasoners:
 
-                    owlapi_adaptor = OWLAPIAdaptor(path=ontology_path, name_reasoner=reasoner)
+                    cur_reasoner = SyncReasoner(ontology=ontology_path, reasoner=reasoner)
 
                     print(f"...Reasoner {reasoner} starts")
 
                     start_time = time.time()  # Start timing
 
-                    result_symbolic = {i.str for i in (owlapi_adaptor.instances(target_concept, direct=False))}
+                    result_symbolic = {i.str for i in (cur_reasoner.instances(target_concept, direct=False))}
                     end_time = time.time()  # End timing
                     
                     elapsed_time = end_time - start_time  # Calculate elapsed time
@@ -217,8 +225,8 @@ def execute(args):
 
     print(final_df.head())
     print(f"Results have been saved to {final_csv_path}")
-        
-    owlapi_adaptor.stopJVM()  # Stop the standard reasoner 
+    stopJVM()
+    return avg_jaccard_reasoners
    
 
 
@@ -228,7 +236,7 @@ def get_default_arguments():
     parser.add_argument("--path_kg", type=str, default="KGs/Family/family-benchmark_rich_background.owl")
     parser.add_argument("--seed", type=int, default=1)
     parser.add_argument("--ratio_sample_nc", type=float, default=None, help="To sample OWL Classes.")
-    parser.add_argument("--ratio_sample_object_prob", type=float, default=None, help="To sample OWL Object Properties.")
+    parser.add_argument("--ratio_sample_object_prop", type=float, default=None, help="To sample OWL Object Properties.")
     parser.add_argument("--path_report", type=str, default="ALCQHI_Retrieval_Incomplete_Results.csv")
     parser.add_argument("--number_of_subgraphs", type=int, default=1)
     parser.add_argument("--ratio", type=float, default=0.1, \
diff --git a/examples/sampling_example.py b/examples/sampling_example.py
deleted file mode 100644
index 8e99874f..00000000
--- a/examples/sampling_example.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import json
-from ontolearn.concept_learner import EvoLearner
-from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.learning_problem import PosNegLPStandard
-from ontolearn.metrics import F1, Accuracy
-from owlapy.owl_individual import OWLNamedIndividual, IRI
-from ontolearn.utils import setup_logging
-from ontosample.lpc_samplers import RandomWalkerJumpsSamplerLPCentralized # pip install ontosample
-setup_logging()
-
-with open('carcinogenesis_lp.json') as json_file:
-    settings = json.load(json_file)
-
-# Initialize kb using the file path stored at carcinogenesis_lp.json
-kb = KnowledgeBase(path=settings['data_path'])
-
-# Define learning problem
-p = set(settings['positive_examples'])
-n = set(settings['negative_examples'])
-typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
-typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
-# Creating an encoded learning problem so that we can later evaluate the generated concepts from the sampled kb
-encoded_lp = kb.encode_learning_problem(PosNegLPStandard(pos=typed_pos, neg=typed_neg))
-
-sampler = RandomWalkerJumpsSamplerLPCentralized(kb, typed_pos.union(typed_neg))
-sampled_kb = sampler.sample(3000)  # create a sample with 3000 individuals
-
-# Removing removed individuals from the learning problem. May not be necessary for an LPC sampler but in case the sample
-# size is less than the number of lp individuals then it is important to remove the excluded individuals from the lp set
-removed_individuals = set(kb.individuals()) - set(sampled_kb.individuals())
-for individual in removed_individuals:
-    individual_as_str = individual.str
-    if individual_as_str in p:
-        p.remove(individual_as_str)
-    if individual_as_str in n:
-        n.remove(individual_as_str)
-typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
-typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
-lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
-
-# Create our model, in this case of EvoLearner using the sampled kb
-model = EvoLearner(knowledge_base=sampled_kb)
-# Fitting the learning problem and finding the best hypotheses
-model.fit(lp, verbose=False)
-hypotheses = list(model.best_hypotheses(n=3))
-
-# Measuring F1-score and Accuracy in the original graph using the hypotheses generated in the sampled graph.
-for hypothesis in hypotheses:
-    f1 = kb.evaluate_concept(hypothesis.concept, F1(), encoded_lp)
-    accuracy = kb.evaluate_concept(hypothesis.concept, Accuracy(), encoded_lp)
-    print(hypothesis)
-    print(f'F1: {f1.q} Accuracy: {accuracy.q} \n')
-
-print("Done!")
-
diff --git a/examples/sml_bench.py b/examples/sml_bench.py
deleted file mode 100644
index f65b5f63..00000000
--- a/examples/sml_bench.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import sys
-
-from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import CELOE
-from ontolearn.learning_problem import PosNegLPStandard
-from ontolearn.metrics import Accuracy, F1
-from ontolearn.utils import setup_logging, read_individuals_file
-from owlapy.owl_reasoner import FastInstanceCheckerReasoner, OntologyReasoner
-from owlapy.iri import IRI
-from owlapy.owl_ontology_manager import OntologyManager
-from owlapy.render import ManchesterOWLSyntaxOWLObjectRenderer, DLSyntaxObjectRenderer  # noqa: F401
-
-
-def run(data_file, pos_file, neg_file):
-    mgr = OntologyManager()
-    onto = mgr.load_ontology(IRI.create("file://" + data_file))
-    base_reasoner = OntologyReasoner(onto)
-    reasoner = FastInstanceCheckerReasoner(onto, base_reasoner,
-                                               negation_default=True)
-
-    kb = KnowledgeBase(ontology=onto, reasoner=reasoner)
-    pos = read_individuals_file(pos_file)
-    neg = read_individuals_file(neg_file)
-
-    lp = PosNegLPStandard(pos, neg)
-
-    pred_acc = Accuracy()
-    f1 = F1()
-    alg = CELOE(kb,
-                max_runtime=600,
-                max_num_of_concepts_tested=1_000_000)
-    alg.fit(lp)
-    # render = ManchesterOWLSyntaxOWLObjectRenderer()
-    render = DLSyntaxObjectRenderer()
-    print("solutions:")
-    i = 1
-    for h in alg.best_hypotheses(3):
-        pred_acc_score = kb.evaluate_concept(h.concept, pred_acc, alg.encoded_learning_problem()).q
-        f1_score = kb.evaluate_concept(h.concept, f1, alg.encoded_learning_problem()).q
-        print(f'{i}: {render.render(h.concept)} ('
-              f'pred. acc.: {pred_acc_score}, '
-              f'F-Measure: {f1_score}'
-              f') [Node '
-              f'quality: {h.quality}, h-exp: {h.h_exp}, RC: {h.refinement_count}'
-              f']')
-        i += 1
-
-
-def main():
-    lp_dir = sys.argv[1]
-    lp_path = lp_dir.split(os.sep)
-    pos_file = os.sep.join((lp_dir, 'pos.txt'))
-    neg_file = os.sep.join((lp_dir, 'neg.txt'))
-    data_file = os.sep.join((*lp_path[:-2], 'data', lp_path[-4] + '.owl'))
-    assert os.path.isfile(pos_file), "Need path to SML-Bench learning problem"
-    assert os.path.isfile(data_file), "Knowledge base not found, skipping"
-
-    setup_logging()
-
-    run(data_file, pos_file, neg_file)
-
-
-if __name__ == '__main__':
-    try:
-        main()
-    except IndexError:
-        print("Syntax:", sys.argv[0], 'path/to/learningtasks/task/owl/lp/problem')
-        raise
diff --git a/examples/sparql_query_learner_example.py b/examples/sparql_query_learner_example.py
new file mode 100644
index 00000000..c2d923e8
--- /dev/null
+++ b/examples/sparql_query_learner_example.py
@@ -0,0 +1,94 @@
+import json
+import os
+import random
+
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.concept_learner import CELOE
+from ontolearn.heuristics import CELOEHeuristic
+from ontolearn.learners.sparql_query_learner import SPARQLQueryLearner
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.lp_generator import LPGen
+from ontolearn.metrics import Accuracy, F1
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+from ontolearn.refinement_operators import ModifiedCELOERefinement
+
+try:
+    os.chdir("examples")
+except FileNotFoundError:
+    pass
+
+if not os.path.exists("PremierLeague"):
+    print("Please prepare the data required by this example")
+    print("1. Download and extract the data (in the examples directory): `wget https://files.dice-research.org/projects/Ontolearn/PremierLeague.zip && unzip PremierLeague.zip`")
+    print("2. Load the knowledge graph found at `PremierLeague/premierleague-hermit-inf.nt` to the triplestore")
+    exit(1)
+
+# premier league
+kb_file="./PremierLeague/premierleague-hermit-inf.owl"
+endpoint_url="http://127.0.0.1:9080/sparql" # the url of the endpoint (change accordingly, the data need to be already loaded)
+lps_file="./PremierLeague/LPs/LPs.json"
+namespace="http://dl-learner.org/res/"
+
+
+kb = KnowledgeBase(path=kb_file)
+
+# is this needed for CELOE?
+random.seed(0)
+
+
+# noinspection DuplicatedCode
+with open(lps_file, "r") as lp:
+    # read LPs file
+    lps_json = json.loads(lp.read())
+    lp_count = 0
+    # iterate over the learning problems
+    for target_concept, lp_json in lps_json.items():
+        lp_count += 1
+        print("######################################")
+        print("Learning Problem: {} (Target Concept: {})".format(lp_count, target_concept))
+        print("######################################")
+        # prepare the positive and negative examples
+        p = set(lp_json['positive examples'])
+        n = set(lp_json['negative examples'])
+        typed_pos = set()
+        typed_neg = set()
+        for i in p:
+            typed_pos.add(OWLNamedIndividual(IRI.create(namespace, i)))
+        for i in n:
+            typed_neg.add(OWLNamedIndividual(IRI.create(namespace, i)))
+        lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+        # prepare CELOE
+        qual = F1()
+        heur = CELOEHeuristic(expansionPenaltyFactor=0.05, startNodeBonus=1.0, nodeRefinementPenalty=0.01)
+        op = ModifiedCELOERefinement(knowledge_base=kb,
+                                     use_negation=False,
+                                     use_card_restrictions=False,
+                                     use_numeric_datatypes = False,
+                                     use_time_datatypes = False,
+                                     use_boolean_datatype = False)
+        model = CELOE(knowledge_base=kb,
+                      max_runtime=10,
+                      refinement_operator=op,
+                      quality_func=qual,
+                      heuristic_func=heur,
+                      max_num_of_concepts_tested=10_000_000_000,
+                      iter_bound=10_000_000_000)
+        try:
+            model.fit(lp)
+        except Exception as e:
+            print("Skipping learning problem (Exception was thrown)")
+            continue
+
+        # Get the top 3 hypotheses found by CELOE
+        hypotheses = list(model.best_hypotheses(n=3))
+        print("--------------------------------------")
+        print("CELOE Results (Top 3)")
+        [print(str(hyp)) for hyp in hypotheses]
+        print("--------------------------------------")
+        # CELOE has finished, proceed with SPARQL learner
+        # create the SPARQL learner by providing the endpoint's URL and the learning problem
+        sparql_learner = SPARQLQueryLearner(endpoint_url=endpoint_url, learning_problem=lp)
+        # the SPARQL learner will try to improve all concepts returned by CELOE (top 3)
+        for hyp in hypotheses:
+            sparql_learner.learn_sparql_query(ce=hyp)
diff --git a/examples/uncle_lp.json b/examples/uncle_lp.json
deleted file mode 100644
index f8a71cb2..00000000
--- a/examples/uncle_lp.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "data_path": "../KGs/Family/family-benchmark_rich_background.owl",
-    "Uncle": {
-      "positive_examples": ["http://www.benchmark.org/family#F2M13"
-,"http://www.benchmark.org/family#F2M11"
-,"http://www.benchmark.org/family#F2M20"
-,"http://www.benchmark.org/family#F2M27"
-,"http://www.benchmark.org/family#F2M32"
-,"http://www.benchmark.org/family#F2M29"
-,"http://www.benchmark.org/family#F2M37"
-,"http://www.benchmark.org/family#F3M44"
-,"http://www.benchmark.org/family#F5M63"
-,"http://www.benchmark.org/family#F6M71"
-,"http://www.benchmark.org/family#F6M80"
-,"http://www.benchmark.org/family#F6M78"
-,"http://www.benchmark.org/family#F6M90"
-,"http://www.benchmark.org/family#F6M85"
-,"http://www.benchmark.org/family#F6M100"
-,"http://www.benchmark.org/family#F6M92"
-,"http://www.benchmark.org/family#F7M113"
-,"http://www.benchmark.org/family#F7M107"
-,"http://www.benchmark.org/family#F7M115"
-,"http://www.benchmark.org/family#F7M120"
-,"http://www.benchmark.org/family#F7M125"
-,"http://www.benchmark.org/family#F7M131"
-,"http://www.benchmark.org/family#F7M122"
-,"http://www.benchmark.org/family#F9M149"
-,"http://www.benchmark.org/family#F9M144"
-,"http://www.benchmark.org/family#F9M151"
-,"http://www.benchmark.org/family#F9M153"
-,"http://www.benchmark.org/family#F9M142"
-,"http://www.benchmark.org/family#F9M159"
-,"http://www.benchmark.org/family#F9M162"
-,"http://www.benchmark.org/family#F9M157"
-,"http://www.benchmark.org/family#F9M167"
-,"http://www.benchmark.org/family#F10M173"
-,"http://www.benchmark.org/family#F10M180"
-,"http://www.benchmark.org/family#F10M182"
-,"http://www.benchmark.org/family#F10M194"
-,"http://www.benchmark.org/family#F10M187"
-,"http://www.benchmark.org/family#F10M196"
-],
-      "negative_examples": [
-"http://www.benchmark.org/family#F10F198"
-,"http://www.benchmark.org/family#F7F108"
-,"http://www.benchmark.org/family#F9M165"
-,"http://www.benchmark.org/family#F6F82"
-,"http://www.benchmark.org/family#F9F148"
-,"http://www.benchmark.org/family#F3M43"
-,"http://www.benchmark.org/family#F7F103"
-,"http://www.benchmark.org/family#F10M188"
-,"http://www.benchmark.org/family#F1F3"
-,"http://www.benchmark.org/family#F9F156"
-,"http://www.benchmark.org/family#F9M147"
-,"http://www.benchmark.org/family#F10F191"
-,"http://www.benchmark.org/family#F9F160"
-,"http://www.benchmark.org/family#F6M95"
-,"http://www.benchmark.org/family#F2F14"
-,"http://www.benchmark.org/family#F6F94"
-,"http://www.benchmark.org/family#F1F2"
-,"http://www.benchmark.org/family#F6F86"
-,"http://www.benchmark.org/family#F10F174"
-,"http://www.benchmark.org/family#F2F12"
-,"http://www.benchmark.org/family#F2F28"
-,"http://www.benchmark.org/family#F5M60"
-,"http://www.benchmark.org/family#F8M134"
-,"http://www.benchmark.org/family#F7M117"
-,"http://www.benchmark.org/family#F10F189"
-,"http://www.benchmark.org/family#F4F55"
-,"http://www.benchmark.org/family#F6F76"
-,"http://www.benchmark.org/family#F7F119"
-,"http://www.benchmark.org/family#F2F36"
-,"http://www.benchmark.org/family#F2M9"
-,"http://www.benchmark.org/family#F2F38"
-,"http://www.benchmark.org/family#F2F22"
-,"http://www.benchmark.org/family#F6F89"
-,"http://www.benchmark.org/family#F5M64"
-,"http://www.benchmark.org/family#F5F67"
-,"http://www.benchmark.org/family#F3F53"
-,"http://www.benchmark.org/family#F2F26"
-,"http://www.benchmark.org/family#F5F65"
-]
-    
-  }
-}
diff --git a/examples/uncle_lp2.json b/examples/uncle_lp2.json
deleted file mode 100644
index 7355e6cd..00000000
--- a/examples/uncle_lp2.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-  "positive_examples": ["http://www.benchmark.org/family#F2M13"
-,"http://www.benchmark.org/family#F2M11"
-,"http://www.benchmark.org/family#F2M20"
-,"http://www.benchmark.org/family#F2M27"
-,"http://www.benchmark.org/family#F2M32"
-,"http://www.benchmark.org/family#F2M29"
-,"http://www.benchmark.org/family#F2M37"
-,"http://www.benchmark.org/family#F3M44"
-,"http://www.benchmark.org/family#F5M63"
-,"http://www.benchmark.org/family#F6M71"
-,"http://www.benchmark.org/family#F6M80"
-,"http://www.benchmark.org/family#F6M78"
-,"http://www.benchmark.org/family#F6M90"
-,"http://www.benchmark.org/family#F6M85"
-,"http://www.benchmark.org/family#F6M100"
-,"http://www.benchmark.org/family#F6M92"
-,"http://www.benchmark.org/family#F7M113"
-,"http://www.benchmark.org/family#F7M107"
-,"http://www.benchmark.org/family#F7M115"
-,"http://www.benchmark.org/family#F7M120"
-,"http://www.benchmark.org/family#F7M125"
-,"http://www.benchmark.org/family#F7M131"
-,"http://www.benchmark.org/family#F7M122"
-,"http://www.benchmark.org/family#F9M149"
-,"http://www.benchmark.org/family#F9M144"
-,"http://www.benchmark.org/family#F9M151"
-,"http://www.benchmark.org/family#F9M153"
-,"http://www.benchmark.org/family#F9M142"
-,"http://www.benchmark.org/family#F9M159"
-,"http://www.benchmark.org/family#F9M162"
-,"http://www.benchmark.org/family#F9M157"
-,"http://www.benchmark.org/family#F9M167"
-,"http://www.benchmark.org/family#F10M173"
-,"http://www.benchmark.org/family#F10M180"
-,"http://www.benchmark.org/family#F10M182"
-,"http://www.benchmark.org/family#F10M194"
-,"http://www.benchmark.org/family#F10M187"
-,"http://www.benchmark.org/family#F10M196"
-],
-  "negative_examples": [
-"http://www.benchmark.org/family#F10F198"
-,"http://www.benchmark.org/family#F7F108"
-,"http://www.benchmark.org/family#F9M165"
-,"http://www.benchmark.org/family#F6F82"
-,"http://www.benchmark.org/family#F9F148"
-,"http://www.benchmark.org/family#F3M43"
-,"http://www.benchmark.org/family#F7F103"
-,"http://www.benchmark.org/family#F10M188"
-,"http://www.benchmark.org/family#F1F3"
-,"http://www.benchmark.org/family#F9F156"
-,"http://www.benchmark.org/family#F9M147"
-,"http://www.benchmark.org/family#F10F191"
-,"http://www.benchmark.org/family#F9F160"
-,"http://www.benchmark.org/family#F6M95"
-,"http://www.benchmark.org/family#F2F14"
-,"http://www.benchmark.org/family#F6F94"
-,"http://www.benchmark.org/family#F1F2"
-,"http://www.benchmark.org/family#F6F86"
-,"http://www.benchmark.org/family#F10F174"
-,"http://www.benchmark.org/family#F2F12"
-,"http://www.benchmark.org/family#F2F28"
-,"http://www.benchmark.org/family#F5M60"
-,"http://www.benchmark.org/family#F8M134"
-,"http://www.benchmark.org/family#F7M117"
-,"http://www.benchmark.org/family#F10F189"
-,"http://www.benchmark.org/family#F4F55"
-,"http://www.benchmark.org/family#F6F76"
-,"http://www.benchmark.org/family#F7F119"
-,"http://www.benchmark.org/family#F2F36"
-,"http://www.benchmark.org/family#F2M9"
-,"http://www.benchmark.org/family#F2F38"
-,"http://www.benchmark.org/family#F2F22"
-,"http://www.benchmark.org/family#F6F89"
-,"http://www.benchmark.org/family#F5M64"
-,"http://www.benchmark.org/family#F5F67"
-,"http://www.benchmark.org/family#F3F53"
-,"http://www.benchmark.org/family#F2F26"
-,"http://www.benchmark.org/family#F5F65"
-]
-  }
diff --git a/examples/verbalization_example.py b/examples/verbalization_example.py
deleted file mode 100644
index c3d8fb4c..00000000
--- a/examples/verbalization_example.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import json
-from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import EvoLearner
-from ontolearn.learning_problem import PosNegLPStandard
-from owlapy.owl_individual import OWLNamedIndividual, IRI
-from ontolearn.utils import setup_logging
-setup_logging()
-
-"""
-Before using verbalizer you have to: 
-1. Install deeponto. `pip install deeponto` + further requirements like JDK, etc. 
-   Check https://krr-oxford.github.io/DeepOnto/ for full instructions.
-2. Install graphviz at https://graphviz.org/download/
-"""
-
-with open("uncle_lp.json") as json_file:
-    settings = json.load(json_file)
-
-kb = KnowledgeBase(path=settings['data_path'])
-
-p = set(settings['problems']['Uncle']['positive_examples'])
-n = set(settings['problems']['Uncle']['negative_examples'])
-typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
-typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
-lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
-
-model = EvoLearner(knowledge_base=kb)
-model.fit(lp)
-model.save_best_hypothesis(n=3, path='Predictions_Uncle')
-
-model.verbalize("Predictions_Uncle.owl")  # at most n=3 .png files will be generated
diff --git a/ontolearn/__init__.py b/ontolearn/__init__.py
index 8b732d2b..aed8540e 100644
--- a/ontolearn/__init__.py
+++ b/ontolearn/__init__.py
@@ -22,4 +22,4 @@
 # SOFTWARE.
 # -----------------------------------------------------------------------------
 
-__version__ = '0.8.0'
+__version__ = '0.8.1'
diff --git a/ontolearn/base_concept_learner.py b/ontolearn/base_concept_learner.py
index 38a5cbc7..693e5c14 100644
--- a/ontolearn/base_concept_learner.py
+++ b/ontolearn/base_concept_learner.py
@@ -37,7 +37,7 @@
 from owlapy.owl_individual import OWLNamedIndividual
 from owlapy.abstracts import AbstractOWLOntology, AbstractOWLOntologyManager, AbstractOWLReasoner
 from owlapy.owl_ontology_manager import AddImport, OWLImportsDeclaration
-from owlapy.owl_reasoner import FastInstanceCheckerReasoner, OntologyReasoner
+from owlapy.owl_reasoner import StructuralReasoner
 
 from ontolearn.heuristics import CELOEHeuristic
 from ontolearn.knowledge_base import KnowledgeBase
@@ -57,7 +57,7 @@
 logger = logging.getLogger(__name__)
 
 
-class BaseConceptLearner(Generic[_N], metaclass=ABCMeta):
+class BaseConceptLearner(metaclass=ABCMeta):
     """
     @TODO: CD: Why should this class inherit from AbstractConceptNode ?
     @TODO: CD: This class should be redefined. An owl class expression learner does not need to be a search based model.
@@ -305,7 +305,7 @@ def predict(self, individuals: List[OWLNamedIndividual],
             for axiom in axioms:
                 ontology.add_axiom(axiom)
             if reasoner is None:
-                reasoner = FastInstanceCheckerReasoner(ontology, base_reasoner=OntologyReasoner(ontology))
+                reasoner = StructuralReasoner(ontology)
 
         if hypotheses is None:
             hypotheses = [hyp.concept for hyp in self.best_hypotheses(n)]
@@ -336,7 +336,7 @@ def save_best_hypothesis(self, n: int = 10, path: str = './Predictions', rdf_for
         """Serialise the best hypotheses to a file.
         @TODO: CD: This function should be deprecated.
         @TODO: CD: Saving owl class expressions into disk should be disentangled from a concept earner
-
+        @TODO:CD: owlapy 1.3.3, we will use save_owl_class_expressions
         Args:
             n: Maximum number of hypotheses to save.
             path: Filename base (extension will be added automatically).
@@ -350,16 +350,14 @@ def save_best_hypothesis(self, n: int = 10, path: str = './Predictions', rdf_for
 
         assert isinstance(self.kb, KnowledgeBase)
 
-        best = self.best_hypotheses(n)
-        if len(best) >= n:
-            logger.warning("There was/were only %d unique result/-s found", len(best))
-
         manager: AbstractOWLOntologyManager = OntologyManager()
 
         ontology: AbstractOWLOntology = manager.create_ontology(IRI.create(NS))
         manager.load_ontology(IRI.create(self.kb.path))
         manager.apply_change(AddImport(ontology, OWLImportsDeclaration(IRI.create('file://' + self.kb.path))))
-        for ith, h in enumerate(self.best_hypotheses(n=n)):
+        best = [self.best_hypotheses(n=n)] if n==1 else self.best_hypotheses(n=n)
+
+        for ith, h in enumerate(best):
             cls_a: OWLClass = OWLClass(IRI.create(NS, "Pred_" + str(ith)))
             equivalent_classes_axiom = OWLEquivalentClassesAxiom([cls_a, h])
             ontology.add_axiom(equivalent_classes_axiom)
@@ -379,8 +377,8 @@ def save_best_hypothesis(self, n: int = 10, path: str = './Predictions', rdf_for
                     OWLAnnotationProperty(IRI.create(SNS, "f1_score")), OWLLiteral(quality)))
                 ontology.add_axiom(f1_score)
             """
-
-        ontology.save(IRI.create(path + '.owl'))
+        # TODO:# must be added for the time being
+        ontology.save(IRI.create(path + '#.owl'))
 
     def load_hypotheses(self, path: str) -> Iterable[OWLClassExpression]:
         """
@@ -402,7 +400,7 @@ def load_hypotheses(self, path: str) -> Iterable[OWLClassExpression]:
                         yield equivalent_c
 
 
-class RefinementBasedConceptLearner(BaseConceptLearner[_N]):
+class RefinementBasedConceptLearner(BaseConceptLearner):
     """
     Base class for refinement based Concept Learning approaches.
 
@@ -501,8 +499,7 @@ def __sanity_checking(self):
         assert self.operator
 
     def terminate(self):
-        if logger.isEnabledFor(oplogging.TRACE):
-            self.show_search_tree('Final')
+        self.show_search_tree('Final')
         return super().terminate()
 
     @abstractmethod
diff --git a/ontolearn/binders.py b/ontolearn/binders.py
index 8be6daf7..a6c59bc1 100644
--- a/ontolearn/binders.py
+++ b/ontolearn/binders.py
@@ -29,7 +29,8 @@
 from .utils import create_experiment_folder
 import re
 import time
-from ontolearn.learning_problem import PosNegLPStandard
+import os
+from .learning_problem import PosNegLPStandard
 
 
 class PredictedConcept:
@@ -46,13 +47,10 @@ class DLLearnerBinder:
     """
 
     def __init__(self, binary_path=None, model=None, kb_path=None, storage_path=".", max_runtime=3):
-        try:
-            assert binary_path
-            assert model
-            assert kb_path
-        except AssertionError:
-            print(f'binary_path:{binary_path}, model:{model} kb_path{kb_path} can not be None')
-            raise
+        assert binary_path, f"binary_path must be given {binary_path}"
+        assert os.path.exists(binary_path), f"binary path {binary_path} does not exist"
+        assert model, "model must be given"
+        assert kb_path, "kb_path must be given"
         self.binary_path = binary_path
         self.kb_path = kb_path
         self.name = model
diff --git a/ontolearn/concept_learner.py b/ontolearn/concept_learner.py
index 9a9d2112..86da215d 100644
--- a/ontolearn/concept_learner.py
+++ b/ontolearn/concept_learner.py
@@ -81,616 +81,11 @@
 import json
 import glob
 from ontolearn.lp_generator import LPGen
-
-logger = logging.getLogger(__name__)
+from .learners import CELOE
 
 _concept_operand_sorter = ConceptOperandSorter()
 
-
-class CELOE(RefinementBasedConceptLearner[OENode]):
-    """Class Expression Learning for Ontology Engineering.
-
-    Attributes:
-        best_descriptions (EvaluatedDescriptionSet[OENode, QualityOrderedNode]): Best hypotheses ordered.
-        best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
-        calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
-        heuristic_func (AbstractHeuristic): Function to guide the search heuristic.
-        heuristic_queue (SortedSet[OENode]): A sorted set that compares the nodes based on Heuristic.
-        iter_bound (int): Limit to stop the algorithm after n refinement steps are done.
-        kb (KnowledgeBase): The knowledge base that the concept learner is using.
-        max_child_length (int): Limit the length of concepts generated by the refinement operator.
-        max_he (int): Maximal value of horizontal expansion.
-        max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested.
-        max_runtime (int): Limit to stop the algorithm after n seconds.
-        min_he (int): Minimal value of horizontal expansion.
-        name (str): Name of the model = 'celoe_python'.
-        _number_of_tested_concepts (int): Yes, you got it. This stores the number of tested concepts.
-        operator (BaseRefinement): Operator used to generate refinements.
-        quality_func (AbstractScorer) The quality function to be used.
-        reasoner (AbstractOWLReasoner): The reasoner that this model is using.
-        search_tree (Dict[OWLClassExpression, TreeNode[OENode]]): Dict to store the TreeNode for a class expression.
-        start_class (OWLClassExpression): The starting class expression for the refinement operation.
-        start_time (float): The time when :meth:`fit` starts the execution. Used to calculate the total time :meth:`fit`
-                            takes to execute.
-        terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found.
-
-    """
-    __slots__ = 'best_descriptions', 'max_he', 'min_he', 'best_only', 'calculate_min_max', 'heuristic_queue', \
-        'search_tree', '_learning_problem', '_max_runtime', '_seen_norm_concepts'
-
-    name = 'celoe_python'
-
-    kb: KnowledgeBase
-    max_he: int
-    min_he: int
-    best_only: bool
-    calculate_min_max: bool
-
-    search_tree: Dict[OWLClassExpression, TreeNode[OENode]]
-    seen_norm_concepts: Set[OWLClassExpression]
-    heuristic_queue: 'SortedSet[OENode]'
-    best_descriptions: EvaluatedDescriptionSet[OENode, QualityOrderedNode]
-    _learning_problem: Optional[EncodedPosNegLPStandardKind]
-
-    def __init__(self,
-                 knowledge_base: KnowledgeBase,
-                 reasoner: Optional[AbstractOWLReasoner] = None,
-                 refinement_operator: Optional[BaseRefinement[OENode]] = None,
-                 quality_func: Optional[AbstractScorer] = None,
-                 heuristic_func: Optional[AbstractHeuristic] = None,
-                 terminate_on_goal: Optional[bool] = None,
-                 iter_bound: Optional[int] = None,
-                 max_num_of_concepts_tested: Optional[int] = None,
-                 max_runtime: Optional[int] = None,
-                 max_results: int = 10,
-                 best_only: bool = False,
-                 calculate_min_max: bool = True):
-        """ Create a new instance of CELOE.
-
-        Args:
-            best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
-                                Defaults to False.
-            calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
-                                        Defaults to True.
-            refinement_operator (BaseRefinement[OENode]): Operator used to generate refinements.
-                                                        Defaults to `ModifiedCELOERefinement`.
-            heuristic_func (AbstractHeuristic): Function to guide the search heuristic. Defaults to `CELOEHeuristic`.
-            iter_bound (int): Limit to stop the algorithm after n refinement steps are done. Defaults to 10'000.
-            knowledge_base (KnowledgeBase): The knowledge base that the concept learner is using.
-            max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested. Defaults to 10'000.
-            max_runtime (int): Limit to stop the algorithm after n seconds. Defaults to 5.
-            max_results (int): Maximum hypothesis to store. Defaults to 10.
-            quality_func (AbstractScorer) The quality function to be used. Defaults to `F1`.
-            reasoner (AbstractOWLReasoner): Optionally use a different reasoner. If reasoner=None, the reasoner of
-                                    the :attr:`knowledge_base` is used.
-            terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found. Defaults to True.
-
-
-        """
-        super().__init__(knowledge_base=knowledge_base,
-                         reasoner=reasoner,
-                         refinement_operator=refinement_operator,
-                         quality_func=quality_func,
-                         heuristic_func=heuristic_func,
-                         terminate_on_goal=terminate_on_goal,
-                         iter_bound=iter_bound,
-                         max_num_of_concepts_tested=max_num_of_concepts_tested,
-                         max_runtime=max_runtime)
-
-        self.search_tree = dict()
-        self.heuristic_queue = SortedSet(key=HeuristicOrderedNode)
-        self._seen_norm_concepts = set()
-        self.best_descriptions = EvaluatedDescriptionSet(max_size=max_results, ordering=QualityOrderedNode)
-
-        self.best_only = best_only
-        self.calculate_min_max = calculate_min_max
-
-        self.max_he = 0
-        self.min_he = 1
-        # TODO: CD: This could be defined in BaseConceptLearner as it is used in all classes that inherits from
-        # TODO: CD: BaseConceptLearner
-        self._learning_problem = None
-        self._max_runtime = None
-
-    def next_node_to_expand(self, step: int) -> OENode:  # pragma: no cover
-        if not self.best_only:
-            for node in reversed(self.heuristic_queue):
-                if node.quality < 1.0:
-                    return node
-            else:
-                raise ValueError("No Node with lesser accuracy found")
-        else:
-            # from reimplementation, pick without quality criterion
-            return self.heuristic_queue[-1]
-
-        # Original reimplementation of CELOE: Sort search tree at each step. Quite inefficient.
-        # self.search_tree.sort_search_tree_by_decreasing_order(key='heuristic')
-        # if self.verbose > 1:
-        #     self.search_tree.show_search_tree(step)
-        # for n in self.search_tree:
-        #     return n
-        # raise ValueError('Search Tree can not be empty.')
-
-    def best_hypotheses(self, n: int = 1, return_node: bool = False) -> Union[Union[
-        OWLClassExpression, Iterable[OWLClassExpression]], Union[OENode, Iterable[OENode]]]:
-        x = islice(self.best_descriptions, n)
-        if n == 1:
-            if return_node:
-                return next(x)
-            else:
-                return next(x).concept
-        else:
-            if return_node:
-                return [i for i in x]
-            else:
-                return [i.concept for i in x]
-
-    def make_node(self, c: OWLClassExpression, parent_node: Optional[OENode] = None, is_root: bool = False) -> OENode:
-        """
-        Create a node for CELOE.
-
-        Args:
-            c: The class expression of this node.
-            parent_node: Parent node.
-            is_root: Is this the root node?
-
-        Returns:
-            OENode: The node.
-        """
-        r = OENode(c, self.kb.concept_len(c), parent_node=parent_node, is_root=is_root)
-        return r
-
-    @contextmanager
-    def updating_node(self, node: OENode):
-        """
-        Removes the node from the heuristic sorted set and inserts it again.
-
-        Args:
-            Node to update.
-
-        Yields:
-            The node itself.
-        """
-        self.heuristic_queue.discard(node)
-        yield node
-        self.heuristic_queue.add(node)
-
-    def downward_refinement(self, node: OENode) -> Iterable[OENode]:
-        assert isinstance(node, OENode)
-
-        with self.updating_node(node):
-            # TODO: NNF
-            refinements = SortedSet(
-                map(_concept_operand_sorter.sort,
-                    self.operator.refine(
-                        node.concept,
-                        max_length=node.h_exp,
-                        current_domain=self.start_class)
-                    )  # noqa: E203
-                ,
-                key=OrderedOWLObject)
-
-            node.increment_h_exp()
-            node.refinement_count = len(refinements)
-            self.heuristic_func.apply(node, None, self._learning_problem)
-
-        def make_node_with_parent(c: OWLClassExpression):
-            return self.make_node(c, parent_node=node)
-
-        return map(make_node_with_parent, refinements)
-
-    def fit(self, *args, **kwargs):
-        """
-        Find hypotheses that explain pos and neg.
-        """
-        self.clean()
-        max_runtime = kwargs.pop("max_runtime", None)
-        learning_problem = self.construct_learning_problem(PosNegLPStandard, args, kwargs)
-
-        assert not self.search_tree
-        self._learning_problem = learning_problem.encode_kb(self.kb)
-
-        if max_runtime is not None:
-            self._max_runtime = max_runtime
-        else:
-            self._max_runtime = self.max_runtime
-
-        root = self.make_node(_concept_operand_sorter.sort(self.start_class), is_root=True)
-        self._add_node(root, None)
-        assert len(self.heuristic_queue) == 1
-        # TODO:CD:suggest to add another assert,e.g. assert #. of instance in root > 1
-
-        self.start_time = time.time()
-        for j in range(1, self.iter_bound):
-            most_promising = self.next_node_to_expand(j)
-            tree_parent = self.tree_node(most_promising)
-            minimum_length = most_promising.h_exp
-            if logger.isEnabledFor(oplogging.TRACE):
-                logger.debug("now refining %s", most_promising)
-            for ref in self.downward_refinement(most_promising):
-                # we ignore all refinements with lower length
-                # (this also avoids duplicate node children)
-                # TODO: ignore too high depth
-                if ref.len < minimum_length:
-                    # ignoring refinement, it does not satisfy minimum_length condition
-                    continue
-
-                # note: tree_parent has to be equal to node_tree_parent(ref.parent_node)!
-                added = self._add_node(ref, tree_parent)
-
-                goal_found = added and ref.quality == 1.0
-
-                if goal_found and self.terminate_on_goal:
-                    return self.terminate()
-
-            if self.calculate_min_max:
-                # This is purely a statistical function, it does not influence CELOE
-                self.update_min_max_horiz_exp(most_promising)
-
-            if time.time() - self.start_time > self._max_runtime:
-                return self.terminate()
-
-            if self.number_of_tested_concepts >= self.max_num_of_concepts_tested:
-                return self.terminate()
-
-            if logger.isEnabledFor(oplogging.TRACE) and j % 100 == 0:
-                self._log_current_best(j)
-
-        return self.terminate()
-
-    async def fit_async(self, *args, **kwargs):  # pragma: no cover
-        """
-        Async method of fit.
-        """
-        self.clean()
-        max_runtime = kwargs.pop("max_runtime", None)
-        learning_problem = self.construct_learning_problem(PosNegLPStandard, args, kwargs)
-
-        assert not self.search_tree
-        self._learning_problem = learning_problem.encode_kb(self.kb)
-
-        if max_runtime is not None:
-            self._max_runtime = max_runtime
-        else:
-            self._max_runtime = self.max_runtime
-
-        root = self.make_node(_concept_operand_sorter.sort(self.start_class), is_root=True)
-        self._add_node(root, None)
-        assert len(self.heuristic_queue) == 1
-        # TODO:CD:suggest to add another assert,e.g. assert #. of instance in root > 1
-
-        self.start_time = time.time()
-        for j in range(1, self.iter_bound):
-            most_promising = self.next_node_to_expand(j)
-            tree_parent = self.tree_node(most_promising)
-            minimum_length = most_promising.h_exp
-            if logger.isEnabledFor(oplogging.TRACE):
-                logger.debug("now refining %s", most_promising)
-            evaluated_refs = []
-            for ref in self.downward_refinement(most_promising):
-                # we ignore all refinements with lower length
-                # (this also avoids duplicate node children)
-                # TODO: ignore too high depth
-                if ref.len < minimum_length:
-                    # ignoring refinement, it does not satisfy minimum_length condition
-                    continue
-                if ref.concept in self.search_tree:
-                    # ignoring refinement, it has been refined from another parent
-                    continue
-
-                evaluated_refs.append(self._eval_node_async(ref))
-
-            import asyncio
-            for task in asyncio.as_completed(evaluated_refs):
-                ref, eval_ = await task
-                # for task in await asyncio.gather(*evaluated_refs):
-                #     ref, eval_ = task
-
-                # note: tree_parent has to be equal to node_tree_parent(ref.parent_node)!
-                added = self._add_node_evald(ref, eval_, tree_parent)
-
-                goal_found = added and ref.quality == 1.0
-
-                if goal_found and self.terminate_on_goal:
-                    return self.terminate()
-
-            if self.calculate_min_max:
-                # This is purely a statistical function, it does not influence CELOE
-                self.update_min_max_horiz_exp(most_promising)
-
-            if time.time() - self.start_time > self._max_runtime:
-                return self.terminate()
-
-            if self.number_of_tested_concepts >= self.max_num_of_concepts_tested:
-                return self.terminate()
-
-            if logger.isEnabledFor(oplogging.TRACE) and j % 100 == 0:
-                self._log_current_best(j)
-
-        return self.terminate()
-
-    def encoded_learning_problem(self) -> Optional[EncodedPosNegLPStandardKind]:
-        """Fetch the most recently used learning problem from the fit method."""
-        return self._learning_problem
-
-    def tree_node(self, node: OENode) -> TreeNode[OENode]:
-        """
-        Get the TreeNode of the given node.
-
-        Args:
-            node: The node.
-
-        Returns:
-            TreeNode of the given node.
-        """
-        tree_parent = self.search_tree[node.concept]
-        return tree_parent
-
-    def _add_node(self, ref: OENode, tree_parent: Optional[TreeNode[OENode]]):
-        # TODO:CD: Why have this constraint ?
-        #  We should not ignore a concept due to this constraint.
-        #  It might be the case that new path to ref.concept is a better path. Hence, we should update its parent
-        #  depending on the new heuristic value.
-        #  Solution: If concept exists we should compare its first heuristic value  with the new one
-        if ref.concept in self.search_tree:
-            # ignoring refinement, it has been refined from another parent
-            return False
-
-        norm_concept = OperandSetTransform().simplify(ref.concept)
-        if norm_concept in self._seen_norm_concepts:
-            norm_seen = True
-        else:
-            norm_seen = False
-            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)
-
-        ref.quality = e.q
-        self._number_of_tested_concepts += 1
-        if ref.quality == 0:  # > too weak
-            return False
-        assert 0 <= ref.quality <= 1.0
-        # TODO: expression rewriting
-        self.heuristic_func.apply(ref, e.inds, self._learning_problem)
-        if not norm_seen and self.best_descriptions.maybe_add(ref):
-            if logger.isEnabledFor(logging.DEBUG):
-                logger.debug("Better description found: %s", ref)
-        self.heuristic_queue.add(ref)
-        # TODO: implement noise
-        return True
-
-    async def _eval_node_async(self, ref: OENode):
-        # TODO:CD: Why have this constraint ?
-        #  We should not ignore a concept due to this constraint.
-        #  It might be the case that new path to ref.concept is a better path. Hence, we should update its parent
-        #  depending on the new heuristic value.
-        #  Solution: If concept exists we should compare its first heuristic value  with the new one
-        res = await self.kb.evaluate_concept_async(ref.concept, self.quality_func, self._learning_problem)
-        return ref, res
-
-    def _add_node_evald(self, ref: OENode, eval_: EvaluatedConcept, tree_parent: Optional[TreeNode[OENode]]):  # pragma: no cover
-        norm_concept = OperandSetTransform().simplify(ref.concept)
-        if norm_concept in self._seen_norm_concepts:
-            norm_seen = True
-        else:
-            norm_seen = False
-            self._seen_norm_concepts.add(norm_concept)
-
-        self.search_tree[ref.concept] = TreeNode(ref, tree_parent, is_root=ref.is_root)
-
-        ref.quality = eval_.q
-        self._number_of_tested_concepts += 1
-        if ref.quality == 0:  # > too weak
-            return False
-        assert 0 <= ref.quality <= 1.0
-        # TODO: expression rewriting
-        self.heuristic_func.apply(ref, eval_.inds, self._learning_problem)
-        if not norm_seen and self.best_descriptions.maybe_add(ref):
-            if logger.isEnabledFor(logging.DEBUG):
-                logger.debug("Better description found: %s", ref)
-        self.heuristic_queue.add(ref)
-        # TODO: implement noise
-        return True
-
-    def _log_current_best(self, heading_step, top_n: int = 10) -> None:
-        logger.debug('######## %s step Best Hypotheses ###########', heading_step)
-
-        predictions = list(self.best_hypotheses(top_n, return_node=True))
-        for ith, node in enumerate(predictions):
-            logger.debug('{0}-\t{1}\t{2}:{3}\tHeuristic:{4}:'.format(
-                ith + 1, DLSyntaxObjectRenderer().render(node.concept),
-                type(self.quality_func).name, node.quality,
-                node.heuristic))
-
-    def show_search_tree(self, heading_step: str, top_n: int = 10) -> None:
-        """
-        Show search tree.
-        """
-        rdr = DLSyntaxObjectRenderer()
-
-        print('######## ', heading_step, 'step Search Tree ###########')
-
-        def tree_node_as_length_ordered_concept(tn: TreeNode[OENode]):
-            return LengthOrderedNode(tn.node, tn.node.len)
-
-        def print_partial_tree_recursive(tn: TreeNode[OENode], depth: int = 0):
-            if tn.node.heuristic is not None:
-                heur_idx = len(self.heuristic_queue) - self.heuristic_queue.index(tn.node)
-            else:
-                heur_idx = None
-
-            if tn.node in self.best_descriptions:
-                best_idx = len(self.best_descriptions.items) - self.best_descriptions.items.index(tn.node)
-            else:
-                best_idx = None
-
-            render_str = rdr.render(tn.node.concept)
-
-            depths = "`" * depth
-
-            if best_idx is not None or heur_idx is not None:
-                if best_idx is None:
-                    best_idx = ""
-                if heur_idx is None:
-                    heur_idx = ""
-
-                print("[%3s] [%4s] %s %s \t HE:%s Q:%f Heur:%s |RC|:%s" % (best_idx, heur_idx, depths, render_str,
-                                                                           tn.node.h_exp, tn.node.quality,
-                                                                           tn.node.heuristic, tn.node.refinement_count))
-
-            for c in sorted(tn.children, key=tree_node_as_length_ordered_concept):
-                print_partial_tree_recursive(c, depth + 1)
-
-        print_partial_tree_recursive(self.search_tree[self.start_class])
-
-        print('######## ', heading_step, 'step Best Hypotheses ###########')
-
-        predictions = list(self.best_hypotheses(top_n, return_node=True))
-        for ith, node in enumerate(predictions):
-            print('{0}-\t{1}\t{2}:{3}\tHeuristic:{4}:'.format(ith + 1, rdr.render(node.concept),
-                                                              type(self.quality_func).name, node.quality,
-                                                              node.heuristic))
-        print('######## Search Tree ###########\n')
-
-    def update_min_max_horiz_exp(self, node: OENode):
-        he = node.h_exp
-        # update maximum value
-        self.max_he = max(self.max_he, he)
-
-        if self.min_he == he - 1:
-            threshold_score = node.heuristic + 1 - node.quality
-
-            for n in reversed(self.heuristic_queue):
-                if n == node:
-                    continue
-                if n.h_exp == self.min_he:
-                    """ we can stop instantly when another node with min. """
-                    return
-                if n.heuristic < threshold_score:
-                    """ we can stop traversing nodes when their score is too low. """
-                    break
-            # inc. minimum since we found no other node which also has min. horiz. exp.
-            self.min_he += 1
-
-            if logger.isEnabledFor(oplogging.TRACE):
-                logger.info("minimum horizontal expansion is now %d", self.min_he)
-
-    def clean(self):
-        self.heuristic_queue.clear()
-        self.best_descriptions.clean()
-        self.search_tree.clear()
-        self._seen_norm_concepts.clear()
-        self.max_he = 0
-        self.min_he = 1
-        self._learning_problem = None
-        self._max_runtime = None
-        super().clean()
-
-
-class OCEL(CELOE):
-    """A limited version of CELOE.
-
-    Attributes:
-        best_descriptions (EvaluatedDescriptionSet[OENode, QualityOrderedNode]): Best hypotheses ordered.
-        best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
-        calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
-        heuristic_func (AbstractHeuristic): Function to guide the search heuristic.
-        heuristic_queue (SortedSet[OENode]): A sorted set that compares the nodes based on Heuristic.
-        iter_bound (int): Limit to stop the algorithm after n refinement steps are done.
-        kb (KnowledgeBase): The knowledge base that the concept learner is using.
-        max_child_length (int): Limit the length of concepts generated by the refinement operator.
-        max_he (int): Maximal value of horizontal expansion.
-        max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested.
-        max_runtime (int): Limit to stop the algorithm after n seconds.
-        min_he (int): Minimal value of horizontal expansion.
-        name (str): Name of the model = 'ocel_python'.
-        _number_of_tested_concepts (int): Yes, you got it. This stores the number of tested concepts.
-        operator (BaseRefinement): Operator used to generate refinements.
-        quality_func (AbstractScorer) The quality function to be used.
-        reasoner (AbstractOWLReasoner): The reasoner that this model is using.
-        search_tree (Dict[OWLClassExpression, TreeNode[OENode]]): Dict to store the TreeNode for a class expression.
-        start_class (OWLClassExpression): The starting class expression for the refinement operation.
-        start_time (float): The time when :meth:`fit` starts the execution. Used to calculate the total time :meth:`fit`
-                            takes to execute.
-        terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found.
-    """
-    __slots__ = ()
-
-    name = 'ocel_python'
-
-    def __init__(self,
-                 knowledge_base: KnowledgeBase,
-                 reasoner: Optional[AbstractOWLReasoner] = None,
-                 refinement_operator: Optional[BaseRefinement[OENode]] = None,
-                 quality_func: Optional[AbstractScorer] = None,
-                 heuristic_func: Optional[AbstractHeuristic] = None,
-                 terminate_on_goal: Optional[bool] = None,
-                 iter_bound: Optional[int] = None,
-                 max_num_of_concepts_tested: Optional[int] = None,
-                 max_runtime: Optional[int] = None,
-                 max_results: int = 10,
-                 best_only: bool = False,
-                 calculate_min_max: bool = True):
-        """ Create a new instance of OCEL.
-
-        Args:
-            best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
-                                Defaults to False.
-            calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
-                                        Defaults to True.
-            refinement_operator (BaseRefinement[OENode]): Operator used to generate refinements.
-                                                        Defaults to `ModifiedCELOERefinement`.
-            heuristic_func (AbstractHeuristic): Function to guide the search heuristic. Defaults to `OCELHeuristic`.
-            iter_bound (int): Limit to stop the algorithm after n refinement steps are done. Defaults to 10'000.
-            knowledge_base (KnowledgeBase): The knowledge base that the concept learner is using.
-            max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested. Defaults to 10'000.
-            max_runtime (int): Limit to stop the algorithm after n seconds. Defaults to 5.
-            max_results (int): Maximum hypothesis to store. Defaults to 10.
-            quality_func (AbstractScorer) The quality function to be used. Defaults to `F1`.
-            reasoner (AbstractOWLReasoner): Optionally use a different reasoner. If reasoner=None, the reasoner of
-                                    the :attr:`knowledge_base` is used.
-            terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found. Defaults to True.
-
-
-        """
-
-        if heuristic_func is None:
-            heuristic_func = OCELHeuristic()
-
-        super().__init__(knowledge_base=knowledge_base,
-                         reasoner=reasoner,
-                         refinement_operator=refinement_operator,
-                         quality_func=quality_func,
-                         heuristic_func=heuristic_func,
-                         terminate_on_goal=terminate_on_goal,
-                         iter_bound=iter_bound,
-                         max_num_of_concepts_tested=max_num_of_concepts_tested,
-                         max_runtime=max_runtime,
-                         max_results=max_results,
-                         best_only=best_only,
-                         calculate_min_max=calculate_min_max)
-
-    def make_node(self, c: OWLClassExpression, parent_node: Optional[OENode] = None, is_root: bool = False) -> OENode:
-        """
-        Create a node for OCEL.
-
-        Args:
-            c: The class expression of this node.
-            parent_node: Parent node.
-            is_root: Is this the root node?
-
-        Returns:
-            OENode: The node.
-        """
-        assert parent_node is None or isinstance(parent_node, LBLNode)
-        r = LBLNode(c, self.kb.concept_len(c), self.kb.individuals_set(c), parent_node=parent_node, is_root=is_root)
-        if parent_node is not None:
-            parent_node.add_child(r)
-        return r
-
-
-class EvoLearner(BaseConceptLearner[EvoLearnerNode]):
+class EvoLearner(BaseConceptLearner):
     """An evolutionary approach to learn concepts in ALCQ(D).
 
     Attributes:
@@ -1348,8 +743,8 @@ def fit(self, *args, **kwargs):
             most_promising = self.next_node_to_expand(j)
             tree_parent = self.tree_node(most_promising)
             minimum_length = most_promising.h_exp
-            if logger.isEnabledFor(oplogging.TRACE):
-                logger.debug("now refining %s", most_promising)
+            # if logger.isEnabledFor(oplogging.TRACE):
+            # logger.debug("now refining %s", most_promising)
             for ref in self.downward_refinement(most_promising):
                 # we ignore all refinements with lower length
                 # (this also avoids duplicate node children)
@@ -1376,8 +771,8 @@ def fit(self, *args, **kwargs):
             if self.number_of_tested_concepts >= self.max_num_of_concepts_tested:
                 return self.terminate()
 
-            if logger.isEnabledFor(oplogging.TRACE) and j % 100 == 0:
-                self._log_current_best(j)
+            # if logger.isEnabledFor(oplogging.TRACE) and j % 100 == 0:
+            #    self._log_current_best(j)
 
         return self.terminate()
 
diff --git a/ontolearn/executor.py b/ontolearn/executor.py
index 122e7b49..60adbad4 100644
--- a/ontolearn/executor.py
+++ b/ontolearn/executor.py
@@ -37,7 +37,8 @@
 
 from ontolearn.abstracts import AbstractNode
 from ontolearn.base_concept_learner import BaseConceptLearner
-from ontolearn.concept_learner import CELOE, OCEL, EvoLearner, NCES
+from .learners import CELOE, OCEL
+from ontolearn.concept_learner import EvoLearner, NCES
 from ontolearn.ea_algorithms import EASimple
 from ontolearn.ea_initialization import EARandomWalkInitialization, EARandomInitialization, RandomInitMethod
 from ontolearn.fitness_functions import LinearPressureFitness
@@ -241,7 +242,7 @@ def execute(args): # pragma: no cover
     model = learner_type(**_get_matching_opts(learner_type, optargs, args_d))
 
     if args.model in ["celoe", "evolearner", "ocel"]:
-        trainer = Trainer(model, kb.reasoner())
+        trainer = Trainer(model, kb.reasoner)
         trainer.fit(lp)
         print(trainer.best_hypotheses(1))
         if args.save:
diff --git a/ontolearn/knowledge_base.py b/ontolearn/knowledge_base.py
index a5632d90..01e58628 100644
--- a/ontolearn/knowledge_base.py
+++ b/ontolearn/knowledge_base.py
@@ -42,7 +42,7 @@
     OWLDataPropertyExpression
 from owlapy.owl_ontology import Ontology
 from owlapy.owl_ontology_manager import OntologyManager
-from owlapy.owl_reasoner import OntologyReasoner, FastInstanceCheckerReasoner
+from owlapy.owl_reasoner import StructuralReasoner
 from owlapy.render import DLSyntaxObjectRenderer
 from ontolearn.search import EvaluatedConcept
 from owlapy.utils import iter_count, LRUCache, OWLClassExpressionLengthMetric
@@ -63,8 +63,7 @@
 
 def depth_Default_ReasonerFactory(onto: AbstractOWLOntology) -> AbstractOWLReasoner:  # pragma: no cover
     assert isinstance(onto, Ontology)
-    base_reasoner = OntologyReasoner(ontology=onto)
-    return FastInstanceCheckerReasoner(ontology=onto, base_reasoner=base_reasoner)
+    return StructuralReasoner(onto)
 
 
 class KnowledgeBase(AbstractKnowledgeBase):
@@ -176,9 +175,7 @@ def __init__(self, *,
         elif reasoner_factory is not None:
             self.reasoner = reasoner_factory(self.ontology)
         else:
-            self.reasoner = FastInstanceCheckerReasoner(ontology=self.ontology,
-                                                            base_reasoner=OntologyReasoner(
-                                                                ontology=self.ontology))
+            self.reasoner = StructuralReasoner(ontology=self.ontology)
 
         self.length_metric = init_length_metric(length_metric, length_metric_factory)
 
@@ -213,7 +210,6 @@ def __init__(self, *,
                                                       ontology=self.ontology,
                                                       individuals_per_concept=(self.individuals(i) for i in
                                                                                self.get_concepts()))
-
         self.describe()
 
     def individuals(self, concept: Optional[OWLClassExpression] = None, named_individuals:bool=False) -> Iterable[OWLNamedIndividual]:
@@ -575,7 +571,7 @@ def cache_individuals(self, ce: OWLClassExpression) -> None:
             raise TypeError
         if ce in self.ind_cache:
             return
-        if isinstance(self.reasoner, FastInstanceCheckerReasoner):
+        if isinstance(self.reasoner, StructuralReasoner):
             self.ind_cache[ce] = self.reasoner._find_instances(ce)  # performance hack
         else:
             temp = self.reasoner.instances(ce)
diff --git a/ontolearn/learners/__init__.py b/ontolearn/learners/__init__.py
index 9b0c39f1..8ac8309e 100644
--- a/ontolearn/learners/__init__.py
+++ b/ontolearn/learners/__init__.py
@@ -24,3 +24,6 @@
 
 from .drill import Drill
 from .tree_learner import TDL
+from .celoe import CELOE
+from .ocel import OCEL
+__all__ = [ 'Drill', 'TDL', 'CELOE', 'OCEL']
\ No newline at end of file
diff --git a/ontolearn/learners/celoe.py b/ontolearn/learners/celoe.py
new file mode 100644
index 00000000..2f1ac08b
--- /dev/null
+++ b/ontolearn/learners/celoe.py
@@ -0,0 +1,377 @@
+from ..base_concept_learner import RefinementBasedConceptLearner
+from ..knowledge_base import KnowledgeBase
+
+from ..abstracts import AbstractScorer, BaseRefinement, AbstractHeuristic, EncodedPosNegLPStandardKind
+from ..learning_problem import PosNegLPStandard
+from ..search import OENode, TreeNode, EvaluatedConcept, HeuristicOrderedNode, QualityOrderedNode, LengthOrderedNode
+
+from typing import Optional, Union, Iterable, Dict
+import owlapy
+
+from owlapy.class_expression import OWLClassExpression
+from contextlib import contextmanager
+from sortedcontainers import SortedSet
+from owlapy.utils import OrderedOWLObject
+from owlapy.utils import EvaluatedDescriptionSet, ConceptOperandSorter, OperandSetTransform
+import time
+from itertools import islice
+from owlapy.render import DLSyntaxObjectRenderer
+
+_concept_operand_sorter = ConceptOperandSorter()
+
+class CELOE(RefinementBasedConceptLearner):
+    """Class Expression Learning for Ontology Engineering.
+    Attributes:
+        best_descriptions (EvaluatedDescriptionSet[OENode, QualityOrderedNode]): Best hypotheses ordered.
+        best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
+        calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
+        heuristic_func (AbstractHeuristic): Function to guide the search heuristic.
+        heuristic_queue (SortedSet[OENode]): A sorted set that compares the nodes based on Heuristic.
+        iter_bound (int): Limit to stop the algorithm after n refinement steps are done.
+        kb (KnowledgeBase): The knowledge base that the concept learner is using.
+        max_child_length (int): Limit the length of concepts generated by the refinement operator.
+        max_he (int): Maximal value of horizontal expansion.
+        max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested.
+        max_runtime (int): Limit to stop the algorithm after n seconds.
+        min_he (int): Minimal value of horizontal expansion.
+        name (str): Name of the model = 'celoe_python'.
+        _number_of_tested_concepts (int): Yes, you got it. This stores the number of tested concepts.
+        operator (BaseRefinement): Operator used to generate refinements.
+        quality_func (AbstractScorer) The quality function to be used.
+        reasoner (AbstractOWLReasoner): The reasoner that this model is using.
+        search_tree (Dict[OWLClassExpression, TreeNode[OENode]]): Dict to store the TreeNode for a class expression.
+        start_class (OWLClassExpression): The starting class expression for the refinement operation.
+        start_time (float): The time when :meth:`fit` starts the execution. Used to calculate the total time :meth:`fit`
+                            takes to execute.
+        terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found.
+
+    """
+    __slots__ = 'best_descriptions', 'max_he', 'min_he', 'best_only', 'calculate_min_max', 'heuristic_queue', \
+        'search_tree', '_learning_problem', '_max_runtime', '_seen_norm_concepts'
+
+    name = 'celoe_python'
+
+    def __init__(self,
+                 knowledge_base: KnowledgeBase=None,
+                 reasoner: Optional[owlapy.abstracts.AbstractOWLReasoner] = None,
+                 refinement_operator: Optional[BaseRefinement[OENode]] = None,
+                 quality_func: Optional[AbstractScorer] = None,
+                 heuristic_func: Optional[AbstractHeuristic] = None,
+                 terminate_on_goal: Optional[bool] = None,
+                 iter_bound: Optional[int] = None,
+                 max_num_of_concepts_tested: Optional[int] = None,
+                 max_runtime: Optional[int] = None,
+                 max_results: int = 10,
+                 best_only: bool = False,
+                 calculate_min_max: bool = True):
+        """ Create a new instance of CELOE.
+
+        Args:
+            best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
+                                Defaults to False.
+            calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
+                                        Defaults to True.
+            refinement_operator (BaseRefinement[OENode]): Operator used to generate refinements.
+                                                        Defaults to `ModifiedCELOERefinement`.
+            heuristic_func (AbstractHeuristic): Function to guide the search heuristic. Defaults to `CELOEHeuristic`.
+            iter_bound (int): Limit to stop the algorithm after n refinement steps are done. Defaults to 10'000.
+            knowledge_base (KnowledgeBase): The knowledge base that the concept learner is using.
+            max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested. Defaults to 10'000.
+            max_runtime (int): Limit to stop the algorithm after n seconds. Defaults to 5.
+            max_results (int): Maximum hypothesis to store. Defaults to 10.
+            quality_func (AbstractScorer) The quality function to be used. Defaults to `F1`.
+            reasoner (AbstractOWLReasoner): Optionally use a different reasoner. If reasoner=None, the reasoner of
+                                    the :attr:`knowledge_base` is used.
+            terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found. Defaults to True.
+
+
+        """
+        super().__init__(knowledge_base=knowledge_base,
+                         reasoner=reasoner,
+                         refinement_operator=refinement_operator,
+                         quality_func=quality_func,
+                         heuristic_func=heuristic_func,
+                         terminate_on_goal=terminate_on_goal,
+                         iter_bound=iter_bound,
+                         max_num_of_concepts_tested=max_num_of_concepts_tested,
+                         max_runtime=max_runtime)
+        self.search_tree: Dict[OWLClassExpression, TreeNode[OENode]] = dict()
+        self.heuristic_queue = SortedSet(key=HeuristicOrderedNode)
+        self._seen_norm_concepts = set()
+        self.best_descriptions = EvaluatedDescriptionSet(max_size=max_results, ordering=QualityOrderedNode)
+
+        self.best_only = best_only
+        self.calculate_min_max = calculate_min_max
+
+        self.max_he = 0
+        self.min_he = 1
+        # TODO: CD: This could be defined in BaseConceptLearner as it is used in all classes that inherits from
+        # TODO: CD: BaseConceptLearner
+        self._learning_problem = None
+        self._max_runtime = None
+
+    def next_node_to_expand(self, step: int) -> OENode:  # pragma: no cover
+        if not self.best_only:
+            for node in reversed(self.heuristic_queue):
+                if node.quality < 1.0:
+                    return node
+            else:
+                raise ValueError("No Node with lesser accuracy found")
+        else:
+            # from reimplementation, pick without quality criterion
+            return self.heuristic_queue[-1]
+
+
+    def best_hypotheses(self, n: int = 1, return_node: bool = False) -> Union[
+        OWLClassExpression | Iterable[OWLClassExpression],
+        OENode | Iterable[OENode]]:
+        x = islice(self.best_descriptions, n)
+        if n == 1:
+            if return_node:
+                return next(x)
+            else:
+                return next(x).concept
+        else:
+            if return_node:
+                return [i for i in x]
+            else:
+                return [i.concept for i in x]
+
+    def make_node(self, c: OWLClassExpression, parent_node: Optional[OENode] = None, is_root: bool = False) -> OENode:
+        return OENode(c, self.kb.concept_len(c), parent_node=parent_node, is_root=is_root)
+    # TODO:CD: Why do we need this ?
+    @contextmanager
+    def updating_node(self, node: OENode):
+        """
+        Removes the node from the heuristic sorted set and inserts it again.
+
+        Args:
+            Node to update.
+
+        Yields:
+            The node itself.
+        """
+        try:
+            self.heuristic_queue.discard(node)
+        except ValueError:
+            # TODO:CD: We need to understand this
+            pass
+        yield node
+        self.heuristic_queue.add(node)
+
+    def downward_refinement(self, node: OENode) -> Iterable[OENode]:
+        with self.updating_node(node):
+            downward_refinements = self.operator.refine(node.concept, max_length=node.h_exp,
+                                                        current_domain=self.start_class)
+            sorted_downward_refinements = SortedSet((_concept_operand_sorter.sort(i) for i in downward_refinements),
+                                                    key=OrderedOWLObject)
+            node.increment_h_exp()
+            node.refinement_count = len(sorted_downward_refinements)
+            self.heuristic_func.apply(node, None, self._learning_problem)
+        return [ self.make_node(i, parent_node=node) for i in sorted_downward_refinements]
+
+    def fit(self, *args, **kwargs):
+        """
+        Find hypotheses that explain pos and neg.
+        """
+        self.clean()
+        max_runtime = kwargs.pop("max_runtime", None)
+        learning_problem = self.construct_learning_problem(PosNegLPStandard, args, kwargs)
+        assert not self.search_tree, "search_tree cannot be None"
+        self._learning_problem = learning_problem.encode_kb(self.kb)
+        self._max_runtime = max_runtime if max_runtime is not None else self.max_runtime
+        root = self.make_node(_concept_operand_sorter.sort(self.start_class), is_root=True)
+        self._add_node(root, None)
+        assert len(self.heuristic_queue) == 1, "The length of heuristic_queue must be equal to 1 after root init."
+        self.start_time = time.time()
+        for j in range(1, self.iter_bound):
+            most_promising = self.next_node_to_expand(j)
+            tree_parent = self.tree_node(most_promising)
+            minimum_length = most_promising.h_exp
+            # print("now refining %s", most_promising)
+            for ref in self.downward_refinement(most_promising):
+                # we ignore all refinements with lower length
+                # (this also avoids duplicate node children)
+                if ref.len < minimum_length:
+                    continue
+                # note: tree_parent has to be equal to node_tree_parent(ref.parent_node)!
+                added = self._add_node(ref, tree_parent)
+                goal_found = added and ref.quality == 1.0
+                if goal_found and self.terminate_on_goal:
+                    return self.terminate()
+            if self.calculate_min_max:
+                # This is purely a statistical function, it does not influence CELOE
+                self.update_min_max_horiz_exp(most_promising)
+            if time.time() - self.start_time > self._max_runtime:
+                return self.terminate()
+            if self.number_of_tested_concepts >= self.max_num_of_concepts_tested:
+                return self.terminate()
+
+        return self.terminate()
+
+    def encoded_learning_problem(self) -> Optional[EncodedPosNegLPStandardKind]:
+        """Fetch the most recently used learning problem from the fit method."""
+        return self._learning_problem
+
+    def tree_node(self, node: OENode) -> TreeNode[OENode]:
+        """
+        Get the TreeNode of the given node.
+
+        Args:
+            node: The node.
+
+        Returns:
+            TreeNode of the given node.
+        """
+        return self.search_tree[node.concept]
+
+    def _add_node(self, ref: OENode, tree_parent: Optional[TreeNode[OENode]]):
+        # TODO:CD: Why have this constraint ?
+        #  We should not ignore a concept due to this constraint.
+        #  It might be the case that new path to ref.concept is a better path. Hence, we should update its parent
+        #  depending on the new heuristic value.
+        #  Solution: If concept exists we should compare its first heuristic value  with the new one
+        if ref.concept in self.search_tree:
+            # ignoring refinement, it has been refined from another parent
+            return False
+
+        norm_concept = OperandSetTransform().simplify(ref.concept)
+        if norm_concept in self._seen_norm_concepts:
+            norm_seen = True
+        else:
+            norm_seen = False
+            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)
+
+        ref.quality = e.q
+        self._number_of_tested_concepts += 1
+        if ref.quality == 0:  # > too weak
+            return False
+        assert 0 <= ref.quality <= 1.0
+        # TODO: expression rewriting
+        self.heuristic_func.apply(ref, e.inds, self._learning_problem)
+        if not norm_seen and self.best_descriptions.maybe_add(ref):
+            #if logger.isEnabledFor(logging.DEBUG):
+            # print("Better description found: %s", ref)
+            pass
+        self.heuristic_queue.add(ref)
+        # TODO: implement noise
+        return True
+
+    def _add_node_evald(self, ref: OENode, eval_: EvaluatedConcept, tree_parent: Optional[TreeNode[OENode]]):  # pragma: no cover
+        norm_concept = OperandSetTransform().simplify(ref.concept)
+        if norm_concept in self._seen_norm_concepts:
+            norm_seen = True
+        else:
+            norm_seen = False
+            self._seen_norm_concepts.add(norm_concept)
+
+        self.search_tree[ref.concept] = TreeNode(ref, tree_parent, is_root=ref.is_root)
+
+        ref.quality = eval_.q
+        self._number_of_tested_concepts += 1
+        if ref.quality == 0:  # > too weak
+            return False
+        assert 0 <= ref.quality <= 1.0
+        # TODO: expression rewriting
+        self.heuristic_func.apply(ref, eval_.inds, self._learning_problem)
+        if not norm_seen and self.best_descriptions.maybe_add(ref):
+            print("Better description found: %s", ref)
+        self.heuristic_queue.add(ref)
+        # TODO: implement noise
+        return True
+
+    def _log_current_best(self, heading_step:int, top_n: int = 10) -> None:
+        print(f'######## {heading_step} step Best Hypotheses ###########')
+
+        predictions = list(self.best_hypotheses(top_n, return_node=True))
+        for ith, node in enumerate(predictions):
+            print('{0}-\t{1}\t{2}:{3}\tHeuristic:{4}:'.format(
+                ith + 1, DLSyntaxObjectRenderer().render(node.concept),
+                type(self.quality_func).name, node.quality,
+                node.heuristic))
+
+    def show_search_tree(self, heading_step: str, top_n: int = 10) -> None:
+        """
+        Show search tree.
+        """
+        rdr = DLSyntaxObjectRenderer()
+
+        print(f'######## {heading_step} step Search Tree ###########')
+
+        def tree_node_as_length_ordered_concept(tn: TreeNode[OENode]):
+            return LengthOrderedNode(tn.node, tn.node.len)
+
+        def print_partial_tree_recursive(tn: TreeNode[OENode], depth: int = 0):
+            if tn.node.heuristic is not None:
+                heur_idx = len(self.heuristic_queue) - self.heuristic_queue.index(tn.node)
+            else:
+                heur_idx = None
+
+            if tn.node in self.best_descriptions:
+                best_idx = len(self.best_descriptions.items) - self.best_descriptions.items.index(tn.node)
+            else:
+                best_idx = None
+
+            render_str = rdr.render(tn.node.concept)
+
+            depths = "`" * depth
+
+            if best_idx is not None or heur_idx is not None:
+                if best_idx is None:
+                    best_idx = ""
+                if heur_idx is None:
+                    heur_idx = ""
+
+                print("[%3s] [%4s] %s %s \t HE:%s Q:%f Heur:%s |RC|:%s" % (best_idx, heur_idx, depths, render_str,
+                                                                           tn.node.h_exp, tn.node.quality,
+                                                                           tn.node.heuristic, tn.node.refinement_count))
+
+            for c in sorted(tn.children, key=tree_node_as_length_ordered_concept):
+                print_partial_tree_recursive(c, depth + 1)
+
+        # print_partial_tree_recursive(self.search_tree[self.start_class])
+
+        print('######## ', heading_step, 'step Best Hypotheses ###########')
+
+        predictions = list(self.best_hypotheses(top_n, return_node=True))
+        for ith, node in enumerate(predictions):
+            print('{0}-\t{1}\t{2}:{3}\tHeuristic:{4}:'.format(ith + 1, rdr.render(node.concept),
+                                                              type(self.quality_func).name, node.quality,
+                                                              node.heuristic))
+        print('######## Search Tree ###########\n')
+
+    def update_min_max_horiz_exp(self, node: OENode):
+        he = node.h_exp
+        # update maximum value
+        self.max_he = max(self.max_he, he)
+
+        if self.min_he == he - 1:
+            threshold_score = node.heuristic + 1 - node.quality
+
+            for n in reversed(self.heuristic_queue):
+                if n == node:
+                    continue
+                if n.h_exp == self.min_he:
+                    """ we can stop instantly when another node with min. """
+                    return
+                if n.heuristic < threshold_score:
+                    """ we can stop traversing nodes when their score is too low. """
+                    break
+            # inc. minimum since we found no other node which also has min. horiz. exp.
+            self.min_he += 1
+
+            # print("minimum horizontal expansion is now %d", self.min_he)
+
+    def clean(self):
+        self.heuristic_queue.clear()
+        self.best_descriptions.clean()
+        self.search_tree.clear()
+        self._seen_norm_concepts.clear()
+        self.max_he = 0
+        self.min_he = 1
+        self._learning_problem = None
+        self._max_runtime = None
+        super().clean()
diff --git a/ontolearn/learners/drill.py b/ontolearn/learners/drill.py
index 8e46aefa..e417b1fa 100644
--- a/ontolearn/learners/drill.py
+++ b/ontolearn/learners/drill.py
@@ -29,10 +29,10 @@
 from owlapy import owl_expression_to_dl
 from ontolearn.base_concept_learner import RefinementBasedConceptLearner
 from ontolearn.refinement_operators import LengthBasedRefinement
-from ontolearn.abstracts import AbstractScorer, AbstractNode
+from ontolearn.abstracts import AbstractNode
 from ontolearn.search import RL_State
 from typing import Set, List, Tuple, Optional, Generator, SupportsFloat, Iterable, FrozenSet, Callable, Union
-from ontolearn.learning_problem import PosNegLPStandard, EncodedPosNegLPStandard
+from ontolearn.learning_problem import PosNegLPStandard
 import torch
 from ontolearn.data_struct import Experience
 from ontolearn.search import DRILLSearchTreePriorityQueue
@@ -40,16 +40,12 @@
 from collections import Counter, deque
 from itertools import chain
 import time
-import dicee
 import os
-from owlapy import owl_expression_to_dl
 # F1 class will be deprecated to become compute_f1_score function.
-from ontolearn.metrics import F1
 from ontolearn.utils.static_funcs import compute_f1_score
 import random
 from ontolearn.heuristics import CeloeBasedReward
-import torch
-from ontolearn.data_struct import PrepareBatchOfTraining, PrepareBatchOfPrediction
+from ontolearn.data_struct import PrepareBatchOfPrediction
 from tqdm import tqdm
 from ..utils.static_funcs import make_iterable_verbose
 from owlapy.utils import get_expression_length
@@ -334,7 +330,7 @@ def fit(self, learning_problem: PosNegLPStandard, max_runtime=None):
             x.heuristic = x.quality
             if x.quality > best_found_quality:
                 best_found_quality = x.quality
-            self.search_tree.add(x)
+                self.search_tree.add(x)
             if ith_bias == self.positive_type_bias:
                 break
 
@@ -433,8 +429,8 @@ def compute_quality_of_class_expression(self, state: RL_State) -> None:
         # (3) Increment the number of tested concepts attribute.
 
         """
-        individuals = frozenset({i for i in self.kb.individuals(state.concept)})
 
+        individuals = frozenset([i for i in self.kb.individuals(state.concept)])
         quality = self.quality_func(individuals=individuals, pos=self.pos, neg=self.neg)
         state.quality = quality
         self._number_of_tested_concepts += 1
@@ -592,49 +588,6 @@ def get_embeddings_individuals(self, individuals: List[str]) -> torch.FloatTenso
     def get_individuals(self, rl_state: RL_State) -> List[str]:
         return [owl_individual.str.strip() for owl_individual in self.kb.individuals(rl_state.concept)]
 
-    def get_embeddings(self, instances) -> None:
-        if self.representation_mode == 'averaging':
-            # (2) if input node has not seen before, assign embeddings.
-            if rl_state.embeddings is None:
-                assert isinstance(rl_state.concept, OWLClassExpression)
-                # (3) Retrieval instances via our retrieval function (R(C)). Be aware Open World and Closed World
-
-                rl_state.instances = set(self.kb.individuals(rl_state.concept))
-                # (4) Retrieval instances in terms of bitset.
-                rl_state.instances_bitset = self.kb.individuals_set(rl_state.concept)
-                # (5) |R(C)|=\emptyset ?
-                if len(rl_state.instances) == 0:
-                    # If|R(C)|=\emptyset, then represent C with zeros
-                    if self.pre_trained_kge is not None:
-                        emb = torch.zeros(1, self.sample_size, self.embedding_dim)
-                    else:
-                        emb = torch.rand(size=(1, self.sample_size, self.embedding_dim))
-                else:
-                    # If|R(C)| \not= \emptyset, then take the mean of individuals.
-                    str_individuals = [i.str for i in rl_state.instances]
-                    assert len(str_individuals) > 0
-                    if self.pre_trained_kge is not None:
-                        emb = self.pre_trained_kge.get_entity_embeddings(str_individuals)
-                        emb = torch.mean(emb, dim=0)
-                        emb = emb.view(1, self.sample_size, self.embedding_dim)
-                    else:
-                        emb = torch.rand(size=(1, self.sample_size, self.embedding_dim))
-                # (6) Assign embeddings
-                rl_state.embeddings = emb
-            else:
-                """ Embeddings already assigned."""
-                try:
-                    assert rl_state.embeddings.shape == (1, self.sample_size, self.embedding_dim)
-                except AssertionError as e:
-                    print(e)
-                    print(rl_state)
-                    print(rl_state.embeddings.shape)
-                    print((1, self.sample_size, self.instance_embeddings.shape[1]))
-                    raise
-        else:
-            """ No embeddings available assigned."""""
-            assert self.representation_mode is None
-
     def assign_embeddings(self, rl_state: RL_State) -> None:
         """
         Assign embeddings to a rl state. A rl state is represented with vector representation of
@@ -866,40 +819,6 @@ def terminate_training(self):
             json.dump(self.seen_examples, f, ensure_ascii=False, indent=4)
         return self
 
-    # TODO: CD:Should be deprecated
-    def fit_from_iterable(self,
-                          dataset: List[Tuple[object, Set[OWLNamedIndividual], Set[OWLNamedIndividual]]],
-                          max_runtime: int = None) -> List:
-        """
-        Dataset is a list of tuples where the first item is either str or OWL class expression indicating target
-        concept.
-        """
-        if max_runtime:
-            self.max_runtime = max_runtime
-        renderer = DLSyntaxObjectRenderer()
-
-        results = []
-        for (target_ce, p, n) in dataset:
-            print(f'TARGET OWL CLASS EXPRESSION:\n{target_ce}')
-            print(f'|Sampled Positive|:{len(p)}\t|Sampled Negative|:{len(n)}')
-            start_time = time.time()
-            self.fit(pos=p, neg=n, max_runtime=max_runtime)
-            rn = time.time() - start_time
-            h: RL_State = next(iter(self.best_hypotheses()))
-            # TODO:CD: We need to remove this first returned boolean for the sake of readability.
-            _, f_measure = F1().score_elp(instances=h.instances_bitset, learning_problem=self._learning_problem)
-            _, accuracy = Accuracy().score_elp(instances=h.instances_bitset, learning_problem=self._learning_problem)
-
-            report = {'Target': str(target_ce),
-                      'Prediction': renderer.render(h.concept),
-                      'F-measure': f_measure,
-                      'Accuracy': accuracy,
-                      'NumClassTested': self._number_of_tested_concepts,
-                      'Runtime': rn}
-            results.append(report)
-
-        return results
-
 
 class DrillHeuristic:  # pragma: no cover
     """
diff --git a/ontolearn/learners/ocel.py b/ontolearn/learners/ocel.py
new file mode 100644
index 00000000..3319421c
--- /dev/null
+++ b/ontolearn/learners/ocel.py
@@ -0,0 +1,109 @@
+from .celoe import CELOE
+from ..knowledge_base import KnowledgeBase
+from typing import Optional
+import owlapy
+from ..abstracts import AbstractScorer, BaseRefinement, AbstractHeuristic
+from ..search import OENode, LBLNode
+from owlapy.class_expression import OWLClassExpression
+from ..heuristics import OCELHeuristic
+
+class OCEL(CELOE):
+    """A limited version of CELOE.
+
+    Attributes:
+        best_descriptions (EvaluatedDescriptionSet[OENode, QualityOrderedNode]): Best hypotheses ordered.
+        best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
+        calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
+        heuristic_func (AbstractHeuristic): Function to guide the search heuristic.
+        heuristic_queue (SortedSet[OENode]): A sorted set that compares the nodes based on Heuristic.
+        iter_bound (int): Limit to stop the algorithm after n refinement steps are done.
+        kb (KnowledgeBase): The knowledge base that the concept learner is using.
+        max_child_length (int): Limit the length of concepts generated by the refinement operator.
+        max_he (int): Maximal value of horizontal expansion.
+        max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested.
+        max_runtime (int): Limit to stop the algorithm after n seconds.
+        min_he (int): Minimal value of horizontal expansion.
+        name (str): Name of the model = 'ocel_python'.
+        _number_of_tested_concepts (int): Yes, you got it. This stores the number of tested concepts.
+        operator (BaseRefinement): Operator used to generate refinements.
+        quality_func (AbstractScorer) The quality function to be used.
+        reasoner (AbstractOWLReasoner): The reasoner that this model is using.
+        search_tree (Dict[OWLClassExpression, TreeNode[OENode]]): Dict to store the TreeNode for a class expression.
+        start_class (OWLClassExpression): The starting class expression for the refinement operation.
+        start_time (float): The time when :meth:`fit` starts the execution. Used to calculate the total time :meth:`fit`
+                            takes to execute.
+        terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found.
+    """
+    __slots__ = ()
+
+    name = 'ocel_python'
+
+    def __init__(self,
+                 knowledge_base: KnowledgeBase,
+                 reasoner: Optional[owlapy.abstracts.AbstractOWLReasoner] = None,
+                 refinement_operator: Optional[BaseRefinement[OENode]] = None,
+                 quality_func: Optional[AbstractScorer] = None,
+                 heuristic_func: Optional[AbstractHeuristic] = None,
+                 terminate_on_goal: Optional[bool] = None,
+                 iter_bound: Optional[int] = None,
+                 max_num_of_concepts_tested: Optional[int] = None,
+                 max_runtime: Optional[int] = None,
+                 max_results: int = 10,
+                 best_only: bool = False,
+                 calculate_min_max: bool = True):
+        """ Create a new instance of OCEL.
+
+        Args:
+            best_only (bool): If False pick only nodes with quality < 1.0, else pick without quality restrictions.
+                                Defaults to False.
+            calculate_min_max (bool): Calculate minimum and maximum horizontal expansion? Statistical purpose only.
+                                        Defaults to True.
+            refinement_operator (BaseRefinement[OENode]): Operator used to generate refinements.
+                                                        Defaults to `ModifiedCELOERefinement`.
+            heuristic_func (AbstractHeuristic): Function to guide the search heuristic. Defaults to `OCELHeuristic`.
+            iter_bound (int): Limit to stop the algorithm after n refinement steps are done. Defaults to 10'000.
+            knowledge_base (KnowledgeBase): The knowledge base that the concept learner is using.
+            max_num_of_concepts_tested (int) Limit to stop the algorithm after n concepts tested. Defaults to 10'000.
+            max_runtime (int): Limit to stop the algorithm after n seconds. Defaults to 5.
+            max_results (int): Maximum hypothesis to store. Defaults to 10.
+            quality_func (AbstractScorer) The quality function to be used. Defaults to `F1`.
+            reasoner (AbstractOWLReasoner): Optionally use a different reasoner. If reasoner=None, the reasoner of
+                                    the :attr:`knowledge_base` is used.
+            terminate_on_goal (bool): Whether to stop the algorithm if a perfect solution is found. Defaults to True.
+
+
+        """
+
+        if heuristic_func is None:
+            heuristic_func = OCELHeuristic()
+
+        super().__init__(knowledge_base=knowledge_base,
+                         reasoner=reasoner,
+                         refinement_operator=refinement_operator,
+                         quality_func=quality_func,
+                         heuristic_func=heuristic_func,
+                         terminate_on_goal=terminate_on_goal,
+                         iter_bound=iter_bound,
+                         max_num_of_concepts_tested=max_num_of_concepts_tested,
+                         max_runtime=max_runtime,
+                         max_results=max_results,
+                         best_only=best_only,
+                         calculate_min_max=calculate_min_max)
+
+    def make_node(self, c: OWLClassExpression, parent_node: Optional[OENode] = None, is_root: bool = False) -> OENode:
+        """
+        Create a node for OCEL.
+
+        Args:
+            c: The class expression of this node.
+            parent_node: Parent node.
+            is_root: Is this the root node?
+
+        Returns:
+            OENode: The node.
+        """
+        assert parent_node is None or isinstance(parent_node, LBLNode)
+        r = LBLNode(c, self.kb.concept_len(c), self.kb.individuals_set(c), parent_node=parent_node, is_root=is_root)
+        if parent_node is not None:
+            parent_node.add_child(r)
+        return r
diff --git a/ontolearn/learners/sparql_query_learner.py b/ontolearn/learners/sparql_query_learner.py
new file mode 100644
index 00000000..e10d7474
--- /dev/null
+++ b/ontolearn/learners/sparql_query_learner.py
@@ -0,0 +1,252 @@
+# -----------------------------------------------------------------------------
+# MIT License
+#
+# Copyright (c) 2024 Ontolearn Team
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# -----------------------------------------------------------------------------
+from typing import List
+
+import requests
+from owlapy.class_expression import OWLClassExpression
+from owlapy.converter import converter
+from sortedcontainers import SortedSet
+
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.metrics import F1
+from itertools import chain, combinations
+from more_itertools import powerset
+
+
+class SPARQLQueryLearner:
+    """Learning SPARQL queries: Given a description logic concept (potentially generated by a concept learner),
+    try to improve the fittness (e.g., F1) of the corresponding SPARQL query.
+
+        Attributes:
+            name (str): Name of the model = 'SPARQL Query Learner'
+            endpoint_url (string): The URL of the SPARQL endpoint to use
+            max_number_of_filters (int): Limit the number of filters combined during the improvement process
+            learning_problem (PosNegLPStandard): the learning problem (sets of positive and negative examples)
+            uses_complex_filters (bool): Denotes whether the learner uses complex filters
+                                        (i.e., makes use of the values of data properties) to improve the quality
+            _root_var (str): The root variable to be used in the OWL2SPARQL conversion
+            _possible_filters (List[str]): A list of possible FILTERs to use to improve the quality
+    """
+    __slots__ = ('endpoint_url',
+                 'max_number_of_filters',
+                 'uses_complex_filters',
+                 'learning_problem',
+                 '_possible_filters',
+                 )
+
+    # public
+    name = 'SPARQL Query Learner'
+    endpoint_url: str
+    max_number_of_filters: int
+    learning_problem: PosNegLPStandard
+    uses_complex_filters: bool
+
+    # private
+    _root_var = "?x"
+    _possible_filters: List[str]
+
+    def __init__(
+            self,
+            learning_problem: PosNegLPStandard,
+            endpoint_url: str,
+            max_number_of_filters: int = 3,
+            use_complex_filters: bool = True):
+
+        # init slots
+        self.learning_problem = learning_problem
+        self.endpoint_url = endpoint_url
+        self.max_number_of_filters = max_number_of_filters
+        self._possible_filters = list()
+        self.uses_complex_filters = use_complex_filters
+
+        str_properties = list()
+        property_positive_values_dict = dict()
+        property_negative_values_dict = dict()
+
+        # find all data properties having string literals as objects
+        properties_response = self._http_request("SELECT DISTINCT ?p WHERE { "
+                                                 "   ?s ?p ?o"
+                                                 "   FILTER(DATATYPE(?o) = <http://www.w3.org/2001/XMLSchema#string>)"
+                                                 "}")
+        for result in properties_response.json()["results"]["bindings"]:
+            str_properties.append(result["p"]["value"])
+
+        # find the set of str properties that are used by ALL positive examples
+        str_properties_used_by_all_positive_examples = set(str_properties)
+        for property_str in str_properties:
+            for positive_example in self.learning_problem.pos:
+                query_response = self._http_request("SELECT ?o WHERE {{ "
+                                                    "   <{}> <{}> ?o"
+                                                    "}}".format(positive_example.str, property_str))
+                if len(query_response.json()["results"]["bindings"]) > 0:
+                    # the positive example uses the property
+                    continue
+                # the positive example does not use the property -> remove it
+                str_properties_used_by_all_positive_examples.discard(property_str)
+
+        # find the set of str properties that are used by ANY negative examples
+        str_properties_used_by_any_negative_examples = set()
+        for property_str in str_properties:
+            for negative_example in self.learning_problem.neg:
+                query_response = self._http_request("SELECT ?o WHERE {{ "
+                                                    "   <{}> <{}> ?o"
+                                                    "}}".format(negative_example.str, property_str))
+                if len(query_response.json()["results"]["bindings"]) > 0:
+                    # the negative example uses the property
+                    str_properties_used_by_any_negative_examples.add(property_str)
+
+        # create possible filters using the sets str_properties_used_by_all_positive_examples and str_properties_used_by_all_negative_examples
+        for property_str in str_properties_used_by_all_positive_examples:
+            self._possible_filters.append("FILTER EXISTS {{ {} <{}> [] }}".format(self._root_var, property_str))
+        for property_str in str_properties_used_by_any_negative_examples:
+            self._possible_filters.append("FILTER NOT EXISTS {{ {} <{}> [] }}".format(self._root_var, property_str))
+
+        # if enabled, the learner tries to leverage also the values of the data properties
+        if self.uses_complex_filters:
+            # for each property in str_properties, find its possible values
+            for property_str in str_properties:
+
+                # stores the STR values found by combining property_str with positive examples
+                property_positive_values_dict[property_str] = SortedSet()
+                for positive_example in self.learning_problem.pos:
+                    query_response = self._http_request("SELECT DISTINCT ?o WHERE {{ "
+                                                         "   <{}> <{}> ?o"
+                                                         "}}".format(positive_example.str, property_str))
+                    for result in query_response.json()["results"]["bindings"]:
+                        property_positive_values_dict[property_str].add(result["o"]["value"])
+
+                # stores the STR values found by combining property_str with negative examples
+                property_negative_values_dict[property_str] = SortedSet()
+                for negative_example in self.learning_problem.neg:
+                    query_response = self._http_request("SELECT DISTINCT ?o WHERE {{ "
+                                                         "   <{}> <{}> ?o"
+                                                         "}}".format(negative_example.str, property_str))
+                    for result in query_response.json()["results"]["bindings"]:
+                        property_negative_values_dict[property_str].add(result["o"]["value"])
+
+
+            # prepare possible filters by utilizing the values of the data properties pointing to str literals
+            var_id = 0
+            # iterate over the (property, positive values) pairs
+            for property_str, values in property_positive_values_dict.items():
+                var_id += 1
+                if len(values) == 0:
+                    # there are no positive examples using property_str
+                    # create a FILTER that looks for individuals that do not use property_str
+                    self._possible_filters.append("FILTER NOT EXISTS {{ {} <{}> [] }}".format(self._root_var, property_str))
+                else:
+                    # there are positive examples that use property_str
+                    # create a FILTER that looks for individuals using the values encountered with positive examples
+                    in_list = [ '"{}"\n'.format(v) for v in property_positive_values_dict[property_str]]
+                    self._possible_filters.append("{} <{}> ?value_{} "
+                                                 "FILTER(?value_{} IN ({}))"
+                                                 .format(self._root_var, property_str, var_id, var_id, ",".join(in_list)))
+
+            # iterate over the (property, negative values) pairs
+            for property_str in property_negative_values_dict:
+                var_id += 1
+                if len(property_negative_values_dict[property_str]) == 0:
+                    # there are no negative examples using property_str
+                    # create a FILTER that looks for individuals that use property_str
+                    self._possible_filters.append("FILTER EXISTS {{ {} <{}> [] }}".format(self._root_var, property_str))
+                else:
+                    # there are negative examples that use property_str
+                    # create a FILTER that looks for individuals not using the values encountered with positive examples
+                    in_list = [ '"{}"\n'.format(v) for v in property_negative_values_dict[property_str]]
+                    self._possible_filters.append("{} <{}> ?value_{} "
+                                                 "FILTER(?value_{} NOT IN ({}))"
+                                                 .format(self._root_var, property_str, var_id, var_id, ",".join(in_list)))
+
+    # the provided concept should be generated by a concept learner that was supplied the same learning problem
+    def learn_sparql_query(self, ce: OWLClassExpression):
+        # compute the f1 score of the provided query
+        confusion_matrix_query = converter.as_confusion_matrix_query(ce=ce,
+                                                                     root_variable=self._root_var,
+                                                                     positive_examples=self.learning_problem.pos,
+                                                                     negative_examples=self.learning_problem.neg)
+        original_query = converter.as_query(ce=ce, root_variable="?x")
+        original_f1_score = self._compute_f1_score(confusion_matrix_query)
+        print("Trying to improve the class expression `{}`"
+              "and its corresponding SPARQL query `{}` with F1 score: {}"
+              .format(ce, ' '.join(original_query.split()), original_f1_score))
+
+        best_score = original_f1_score
+        # template query: it will be assigned the FILTERs
+        confusion_matrix_query = converter.as_confusion_matrix_query(ce=ce,
+                                                                     root_variable="?x",
+                                                                     positive_examples=self.learning_problem.pos,
+                                                                     negative_examples=self.learning_problem.neg)
+        best_query = original_query
+
+        # start the search
+        # we try combinations of FILTERs stored in self._possible_filters by iterating their powerset
+        # we first try single sets
+        # we then try their combinations
+        # the maximum number of sets that are combined is specified by self.max_number_of_filters
+        for combination_of_filters in powerset([i for i in range(len(self._possible_filters))]):
+            if len(combination_of_filters) == 0:
+                continue
+            if len(combination_of_filters) > self.max_number_of_filters:
+                break
+
+            # create a SPARQL query using the FILTERs specified in combination_of_filters
+            learned_query_cm = confusion_matrix_query.replace("VALUES", " ".join([self._possible_filters[idx] for idx in combination_of_filters]) + " VALUES")
+            f1_score = self._compute_f1_score(learned_query_cm)
+            # update the best score and its corresponding query
+            if f1_score > best_score:
+                best_score = f1_score
+                best_query = original_query[:-1] + " ".join([self._possible_filters[idx] for idx in combination_of_filters]) + "}"
+
+        # print if we have found a better query
+        if (best_score > original_f1_score):
+            print("Found a better SPARQL query `{}`, with F1 score: {}".format(' '.join(best_query.split()), best_score))
+        else:
+            print("The provided class expression and its corresponding SPARQL query could not be improved")
+
+    def _http_request(self, query_str) -> requests.Response:
+        response = requests.post(url=self.endpoint_url,
+                                 headers={"Content-Type": "application/sparql-query"},
+                                 data=query_str.replace('\\', '\\\\')) # in nctrer there are str literals that contain `\\`, which can be parsed by SPARQL (need to be escaped)
+        status_code = response.status_code
+        if not (200 <= status_code < 204):
+            raise Exception("Query failed")
+        return response
+
+
+    def _compute_f1_score(self, query_str: str):
+        try:
+            stats = self._http_request(query_str).json()["results"]["bindings"][0]
+            return F1().score2(tp=int(stats["tp"]["value"]),
+                               fp=int(stats["fp"]["value"]),
+                               fn=int(stats["fn"]["value"]),
+                               tn=int(stats["tn"]["value"]))
+        except Exception:
+            print("Exception occurred!")
+            # query failed (e.g., due to malformed string; continue)
+            return F1().score2(tp=0, fp=0, fn=0, tn=0)
+
+    def _powerset_of_filters(self):
+        set_of_indices = [i for i in range(len(self._possible_filters))]
+        # lazily construction of the powerset (can be early terminated)
+        return chain.from_iterable(combinations(set_of_indices, r) for r in range(len(set_of_indices) + 1))
diff --git a/ontolearn/learners/tree_learner.py b/ontolearn/learners/tree_learner.py
index 2d534212..0e089592 100644
--- a/ontolearn/learners/tree_learner.py
+++ b/ontolearn/learners/tree_learner.py
@@ -22,56 +22,26 @@
 # SOFTWARE.
 # -----------------------------------------------------------------------------
 
-from typing import Dict, Set, Tuple, List, Union, Callable, Iterable
+from typing import Dict, Set, Tuple, List, Union, Callable
 import numpy as np
 import pandas as pd
 from owlapy.class_expression import (
     OWLObjectIntersectionOf,
     OWLClassExpression,
     OWLObjectUnionOf,
-    OWLDataHasValue,
-    OWLDataSomeValuesFrom,
-    OWLClass,
 )
 from owlapy.owl_individual import OWLNamedIndividual
-from owlapy.owl_literal import OWLLiteral
-from owlapy.owl_property import OWLDataProperty
 import ontolearn.triple_store
 from ontolearn.knowledge_base import KnowledgeBase
-from owlapy.class_expression import OWLDataOneOf
 from ontolearn.learning_problem import PosNegLPStandard
-from tqdm import tqdm
 import sklearn
 from sklearn import tree
-from owlapy.render import DLSyntaxObjectRenderer, ManchesterOWLSyntaxOWLObjectRenderer
 
 from ..utils.static_funcs import plot_umap_reduced_embeddings, plot_decision_tree_of_expressions, \
     plot_topk_feature_importance
 
 import itertools
-from owlapy.class_expression import (
-    OWLDataMinCardinality,
-    OWLDataMaxCardinality,
-    OWLObjectOneOf,
-)
-from owlapy.class_expression import (
-    OWLDataMinCardinality,
-    OWLDataOneOf,
-    OWLDataSomeValuesFrom,
-)
-from owlapy.providers import (
-    owl_datatype_min_inclusive_restriction,
-    owl_datatype_max_inclusive_restriction,
-)
-from owlapy.providers import (
-    owl_datatype_min_exclusive_restriction,
-    owl_datatype_max_exclusive_restriction,
-    owl_datatype_min_inclusive_restriction,
-)
-import scipy
-from owlapy import owl_expression_to_dl, owl_expression_to_sparql
-from owlapy.class_expression import OWLObjectSomeValuesFrom, OWLObjectMinCardinality
-from owlapy.providers import owl_datatype_min_max_exclusive_restriction
+from owlapy import owl_expression_to_dl
 from ..utils.static_funcs import make_iterable_verbose
 
 
@@ -242,7 +212,7 @@ def extract_expressions_from_owl_individuals(self, individuals: List[OWLNamedInd
                     # A mapping from str dl representation to owl object.
                     features[str_dl_concept] = owl_class_expression
 
-        assert len(features) > 0, f"First hop features cannot be extracted. Ensure that there are axioms about the examples."
+        assert len(features) > 0, "First hop features cannot be extracted. Ensure that there are axioms about the examples."
         if self.verbose > 0:
             print("Unique OWL Class Expressions as features :", len(features))
         # () Iterate over features/extracted owl expressions.
diff --git a/ontolearn/owl_neural_reasoner.py b/ontolearn/owl_neural_reasoner.py
index 05231c34..cb237061 100644
--- a/ontolearn/owl_neural_reasoner.py
+++ b/ontolearn/owl_neural_reasoner.py
@@ -7,21 +7,36 @@
 from owlapy.owl_individual import OWLNamedIndividual
 from owlapy.owl_literal import OWLLiteral
 from owlapy.class_expression import *
-from typing import Generator, Tuple
+from typing import Generator, Tuple, Iterable, List, Set
 from dicee.knowledge_graph_embeddings import KGE
 import os
 import re
-from collections import Counter
+from collections import Counter, OrderedDict
+from owlapy.iri import IRI
+from functools import lru_cache
 
+# TODO:
+def is_valid_entity(text_input: str):
+    return True if "/" in text_input else False
 
-# Neural Reasoner
 class TripleStoreNeuralReasoner:
     """ OWL Neural Reasoner uses a neural link predictor to retrieve instances of an OWL Class Expression"""
-    model: KGE
-    gamma: float
-
-    def __init__(self, path_of_kb: str = None,
-                 path_neural_embedding: str = None, gamma: float = 0.25):
+    def __init__(self, path_of_kb: str = None, path_neural_embedding: str = None, gamma: float = 0.25, max_cache_size: int = 2**20):
+        assert gamma is None or 0 <= gamma <= 1, "Confidence threshold (gamma) must be in the range [0, 1]."
+        self.gamma = gamma
+        self._prediction_cache = OrderedDict()
+        self._max_cache_size = max_cache_size
+        self.str_iri_subclassof="http://www.w3.org/2000/01/rdf-schema#subClassOf"
+        self.str_iri_type="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
+        self.str_iri_owl_class = "http://www.w3.org/2002/07/owl#Class"
+        self.str_iri_object_property="http://www.w3.org/2002/07/owl#ObjectProperty"
+        self.str_iri_range="http://www.w3.org/2000/01/rdf-schema#range"
+        self.str_iri_double = "http://www.w3.org/2001/XMLSchema#double"
+        self.str_iri_boolean = "http://www.w3.org/2001/XMLSchema#boolean"
+        self.str_iri_data_property="http://www.w3.org/2002/07/owl#DatatypeProperty"
+
+        if isinstance(max_cache_size,int) and max_cache_size>0:
+           self.predict=lru_cache(maxsize=max_cache_size)(self.predict)
 
         if path_neural_embedding:  # pragma: no cover
             assert os.path.isdir(
@@ -57,98 +72,67 @@ def __init__(self, path_of_kb: str = None,
             raise RuntimeError(
                 f"path_neural_embedding {path_neural_embedding} and path_of_kb {path_of_kb} cannot be both None")
 
-        self.gamma = gamma
-        # Caching for the sake of memory usage.
-        # TODO:CD: We may want to use  @functools.lru_cache(maxsize=?, typed=?)
-        #  https://docs.python.org/3/library/functools.html
-        self.inferred_owl_individuals = None
         self.inferred_object_properties = None
         self.inferred_named_owl_classes = None
 
-    @property
-    def set_inferred_individuals(self):
-        if self.inferred_owl_individuals is None:
-            # self.inferred_owl_individuals is filled in here
-            return {i for i in self.individuals_in_signature()}
-        else:
-            return self.inferred_owl_individuals
+    def __str__(self):
+        return f"TripleStoreNeuralReasoner:{self.model} with likelihood threshold gamma : {self.gamma}"
 
     @property
     def set_inferred_object_properties(self):  # pragma: no cover
-        if self.inferred_object_properties is None:
-            # self.inferred_owl_individuals is filled in here
-            return {i for i in self.object_properties_in_signature()}
-        else:
-            return self.inferred_object_properties
+        return {i for i in self.object_properties_in_signature()} if self.inferred_object_properties is None else self.inferred_object_properties
 
     @property
     def set_inferred_owl_classes(self):  # pragma: no cover
-        if self.inferred_named_owl_classes is None:
-            # self.inferred_owl_individuals is filled in here
-            return {i for i in self.classes_in_signature()}
-        else:
-            return self.inferred_named_owl_classes
-
-    def __str__(self):
-        return f"TripleStoreNeuralReasoner:{self.model} with likelihood threshold gamma : {self.gamma}"
+        return {i for i in self.classes_in_signature()} if self.inferred_named_owl_classes is None else self.inferred_named_owl_classes
 
-    def get_predictions(self, h: str = None, r: str = None, t: str = None, confidence_threshold: float = None,
-                        ) -> Generator[Tuple[str, float], None, None]:
-        """
-        Generate predictions for a given head entity (h), relation (r), or tail entity (t) with an optional confidence threshold. The method yields predictions that exceed the specified confidence threshold. If no threshold is provided, it defaults to the model's gamma value.
-
-        Parameters:
-        - h (str, optional): The identifier for the head entity.
-        - r (str, optional): The identifier for the relation.
-        - t (str, optional): The identifier for the tail entity.
-        - confidence_threshold (float, optional): The minimum confidence level required for a prediction to be returned.
-
-        Returns:
-        - Generator[Tuple[str, float], None, None]: A generator of tuples, where each tuple contains a predicted entity or relation identifier (IRI) as a string and its corresponding confidence level as a float.
-
-        Raises:
-        - Exception: If an error occurs during prediction.
-        """
+    def predict(self, h: str = None, r: str = None, t: str = None) -> List[Tuple[str,float]]:
         # sanity check
         assert h is not None or r is not None or t is not None, "At least one of h, r, or t must be provided."
-        assert confidence_threshold is None or 0 <= confidence_threshold <= 1, "Confidence threshold must be in the range [0, 1]."
         assert h is None or isinstance(h, str), "Head entity must be a string."
         assert r is None or isinstance(r, str), "Relation must be a string."
         assert t is None or isinstance(t, str), "Tail entity must be a string."
 
         if h is not None:
-            if (self.model.entity_to_idx.get(h, None)) is None:
-                return
+            if h not in self.model.entity_to_idx:
+                # raise KeyError(f"Head entity '{h}' not found in model entity indices.")
+                return []
             h = [h]
 
         if r is not None:
-            if (self.model.relation_to_idx.get(r, None)) is None:
-                return
+            if r not in self.model.relation_to_idx:
+                #raise KeyError(f"Relation '{r}' not found in model relation indices.")
+                return []
             r = [r]
+
         if t is not None:
-            if (self.model.entity_to_idx.get(t, None)) is None:
-                return
+            if t not in self.model.entity_to_idx:
+                # raise KeyError(f"Tail entity '{t}' not found in model entity indices.")
+                return []
             t = [t]
 
-        if confidence_threshold is None:
-            confidence_threshold = self.gamma
-
         if r is None:
             topk = len(self.model.relation_to_idx)
         else:
             topk = len(self.model.entity_to_idx)
-        try:
-            predictions = self.model.predict_topk(h=h, r=r, t=t, topk=topk)
-            for prediction in predictions:
-                confidence = prediction[1]
-                predicted_iri_str = prediction[0]
-                if confidence >= confidence_threshold:
-                    yield predicted_iri_str, confidence
-                else:
-                    #todo: replace with return or break?
-                    continue
-        except Exception as e:  # pragma: no cover
-            print(f"Error at getting predictions: {e}")
+
+
+        return [ (top_entity, score)  for top_entity, score in self.model.predict_topk(h=h, r=r, t=t, topk=topk) if score >= self.gamma and is_valid_entity(top_entity)]
+
+    def predict_individuals_of_owl_class(self, owl_class: OWLClass) -> List[OWLNamedIndividual]:
+        top_entities=set()
+        # Find all subconcepts
+        owl_classes = [owl_class] + self.subconcepts(owl_class)
+        c:OWLClass
+        for c in owl_classes:
+            assert isinstance(c, OWLClass)
+            top_entity:str
+            score:float
+            for top_entity, score in self.predict(h=None,
+                                                  r=self.str_iri_type,
+                                                  t=c.iri.str):
+                top_entities.add(top_entity)
+        return [OWLNamedIndividual(i) for i in top_entities]
 
     def abox(self, str_iri: str) -> Generator[
         Tuple[
@@ -166,150 +150,121 @@ def abox(self, str_iri: str) -> Generator[
             for o in self.get_object_property_values(str_iri, op):
                 yield subject_, op, o
 
-        # Return a triple based on a data property.
-        for dp in self.data_properties_in_signature():  # pragma: no cover
-            print("these data properties are in the signature: ", dp.str)
-            for l in self.get_data_property_values(str_iri, dp):
-                yield subject_, dp, l
-
-    def classes_in_signature(
-            self, confidence_threshold: float = None) -> Generator[OWLClass, None, None]:
-        if self.inferred_named_owl_classes is None:
-            self.inferred_named_owl_classes = set()
-            for prediction in self.get_predictions(
-                    h=None,
-                    r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-                    t="http://www.w3.org/2002/07/owl#Class",
-                    confidence_threshold=confidence_threshold):
-                try:
-                    owl_class = OWLClass(prediction[0])
-                    self.inferred_named_owl_classes.add(owl_class)
-                    yield owl_class
-                except Exception as e:
-                    # Log the invalid IRI
-                    print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                    continue
-        else:
-            yield from self.inferred_named_owl_classes
-
-    def most_general_classes(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLClass, None, None]:  # pragma: no cover
+        # Return a triple based on a data property. TODO: LF: fix if support for data properties is added.
+        # for dp in self.data_properties_in_signature():  # pragma: no cover
+        #     print("these data properties are in the signature: ", dp.str)
+        #     for l in self.get_data_property_values(str_iri, dp):
+        #         yield subject_, dp, l
+
+    def classes_in_signature(self) -> List[OWLClass]:
+        return [OWLClass(top_entity) for top_entity, score in self.predict(h=None,
+                                                                   r=self.str_iri_type,
+                                                                   t=self.str_iri_owl_class)]
+    def direct_subconcepts(self, named_concept: OWLClass) -> List[OWLClass]:
+        return [OWLClass(top_entity) for top_entity, score in self.predict(h=None,
+                                                                           r=self.str_iri_subclassof,
+                                                                           t=named_concept.str)]
+
+    def subconcepts(self, named_concept: OWLClass, visited=None) -> List[OWLClass]:
+        if visited is None:
+            visited = set()
+        all_subconcepts = []
+        for subconcept in self.direct_subconcepts(named_concept):
+            if subconcept not in self.classes_in_signature() or subconcept in visited:
+                continue  # Skip to the next subconcept
+            visited.add(subconcept)
+            all_subconcepts.append(subconcept)
+            all_subconcepts.extend(self.subconcepts(subconcept, visited))
+        return all_subconcepts
+    
+    def most_general_classes(self) -> List[OWLClass]:  # pragma: no cover
         """At least it has single subclass and there is no superclass"""
-
-        for c in self.classes_in_signature(confidence_threshold):
-            for x in self.get_direct_parents(c, confidence_threshold):
+        owl_concepts_not_having_parents=set()
+        for c in self.classes_in_signature():
+            direct_parents=set()
+            for x in self.get_direct_parents(c):
                 # Ignore c if (c subclass x) \in KG.
+                direct_parents.add(x)
                 break
-            else:
+            if len(direct_parents) ==0:
+                # c does not have any parents
+                # Check whether it has at least one sub
                 # checks if subconcepts is not empty -> there is at least one subclass
                 # c should have at least a single subclass.
-                if subconcepts := list(
-                        self.subconcepts(
-                            named_concept=c, confidence_threshold=confidence_threshold
-                        )
-                ):
-                    yield c
+                for sub_c in self.subconcepts(named_concept=c):
+                    owl_concepts_not_having_parents.add(sub_c)
+                    break
+        return [i for i in owl_concepts_not_having_parents]
 
-    def least_general_named_concepts(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLClass, None, None]:  # pragma: no cover
+    def least_general_named_concepts(self) -> Generator[OWLClass, None, None]:  # pragma: no cover
         """At least it has single superclass and there is no subclass"""
-        for _class in self.classes_in_signature(confidence_threshold):
+        for _class in self.classes_in_signature():
             for concept in self.subconcepts(
-                    named_concept=_class, confidence_threshold=confidence_threshold
+                    named_concept=_class
             ):
                 break
             else:
                 # checks if superclasses is not empty -> there is at least one superclass
                 if superclasses := list(
-                        self.get_direct_parents(_class, confidence_threshold)
+                        self.get_direct_parents(_class)
                 ):
                     yield _class
 
-    def subconcepts(
-            self, named_concept: OWLClass, confidence_threshold: float = None
-    ) -> Generator[OWLClass, None, None]:
-        for prediction in self.get_predictions(
+    def get_direct_parents(self, named_concept: OWLClass)-> List[OWLClass] :  # pragma: no cover
+        return [OWLClass(entity) for entity, score in self.predict(h=named_concept.str, r=self.str_iri_subclassof,
+                                                                   t=None)]
+
+    def get_type_individuals(self, individual: str) -> List[OWLClass]:
+        return [OWLClass(top_entity) for top_entity,score in self.predict(h=individual, r=self.str_iri_type, t=None)]
+    def individuals_in_signature(self) -> List[OWLNamedIndividual]:
+        set_str_entities=set()
+        for owl_class in self.classes_in_signature():
+            for top_entity, score in self.predict(h=None,
+                                                  r=self.str_iri_type,
+                                                  t=owl_class.iri.str):
+                set_str_entities.add(top_entity)
+        return [OWLNamedIndividual(entity) for entity in set_str_entities]
+
+    def data_properties_in_signature(self) -> List[OWLDataProperty]:
+        return [OWLDataProperty(top_entity) for top_entity, score in self.predict(h=None,
+                                                     r=self.str_iri_type,
+                                                     t=self.str_iri_data_property)]
+
+    def object_properties_in_signature(self) -> List[OWLObjectProperty]:
+        return [OWLObjectProperty(top_entity) for top_entity, score in self.predict(h=None,
+                                                     r=self.str_iri_type,
+                                                     t=self.str_iri_object_property)]
+
+    def boolean_data_properties(self) -> Generator[OWLDataProperty, None, None]:  # pragma: no cover
+        return [OWLDataProperty(top_entity) for top_entity,score  in self.predict(h=None, r=self.str_iri_range,
+                                                                                  t=self.str_iri_boolean)]
+
+    def double_data_properties(self) -> List[OWLDataProperty]:  # pragma: no cover
+        return [OWLDataProperty(top_entity) for top_entity, score in self.predict(
                 h=None,
-                r="http://www.w3.org/2000/01/rdf-schema#subClassOf",
-                t=named_concept.str,
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLClass(prediction[0])
-            except Exception as e:  # pragma: no cover
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
-
-    def get_direct_parents(
-            self, named_concept: OWLClass, direct=True, confidence_threshold: float = None
-    ):  # pragma: no cover
-        for prediction in self.get_predictions(
-                h=named_concept.str,
-                r="http://www.w3.org/2000/01/rdf-schema#subClassOf",
-                t=None,
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLClass(prediction[0])
-            except Exception as e:
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
-
-    def get_type_individuals(
-            self, individual: str, confidence_threshold: float = None
-    ) -> Generator[OWLClass, None, None]:
-        for prediction in self.get_predictions(
-                h=individual,
-                r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-                t=None,
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLClass(prediction[0])
-            except Exception as e:  # pragma: no cover
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
-
-    def individuals(self, expression: OWLClassExpression = None, named_individuals: bool = False,
-                    confidence_threshold: float = None, ) -> Generator[OWLNamedIndividual, None, None]:
+                r=self.str_iri_range,
+                t=self.str_iri_double)]
+    def individuals(self, expression: OWLClassExpression = None, named_individuals: bool = False) -> Generator[OWLNamedIndividual, None, None]:
         if expression is None or expression.is_owl_thing():
             yield from self.individuals_in_signature()
         else:
-            yield from self.instances(expression, confidence_threshold=confidence_threshold)
-
-    def instances(self, expression: OWLClassExpression, named_individuals=False,
-                  confidence_threshold: float = None, ) -> Generator[OWLNamedIndividual, None, None]:
-        """
-        if expression.is_owl_thing():
-            yield from self.individuals_in_signature()
-        """
-
-        """
-        if isinstance(expression, OWLNamedIndividual):
-            # TODO: CD: expression should not be an instance of  OWLNamedIndividual
-            # TODO: CD: Perhaps, we need to ensure that is never the case :).
-            yield expression
-        """
+            yield from self.instances(expression)
 
+    def instances(self, expression: OWLClassExpression, named_individuals=False) -> Generator[OWLNamedIndividual, None, None]:
         if isinstance(expression, OWLClass):
             """ Given an OWLClass A, retrieve its instances Retrieval(A)={ x | phi(x, type, A) ≥ γ } """
-            yield from self.get_individuals_of_class(
-                owl_class=expression, confidence_threshold=confidence_threshold
-            )
-        # Handling complement of class expressions
+            yield from self.predict_individuals_of_owl_class(expression)
         elif isinstance(expression, OWLObjectComplementOf):
-            """ Given an OWLObjectComplementOf ¬A, hence (A is an OWLClass),
+            """ Handling complement of class expressions:
+            Given an OWLObjectComplementOf ¬A, hence (A is an OWLClass),
             retrieve its instances => Retrieval(¬A)= All Instance Set-DIFF { x | phi(x, type, A) ≥ γ } """
-            excluded_individuals = set(self.instances(expression.get_operand(), confidence_threshold))
-            yield from self.set_inferred_individuals - excluded_individuals
-        # Handling intersection of class expressions
+            excluded_individuals:Set[OWLNamedIndividual]
+            excluded_individuals = set(self.instances(expression.get_operand()))
+            all_individuals= {i for i in self.individuals_in_signature()}
+            yield from all_individuals - excluded_individuals
         elif isinstance(expression, OWLObjectIntersectionOf):
-            """ Given an OWLObjectIntersectionOf (C ⊓ D),  
+            """ Handling intersection of class expressions:
+            Given an OWLObjectIntersectionOf (C ⊓ D),  
             retrieve its instances by intersecting the instance of each operands.
             {x | phi(x, type, C) ≥ γ} ∩ {x | phi(x, type, D) ≥ γ}
             """
@@ -317,35 +272,12 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
             #
             result = None
             for op in expression.operands():
-                retrieval_of_op = {_ for _ in self.instances(expression=op, confidence_threshold=confidence_threshold)}
+                retrieval_of_op = {_ for _ in self.instances(expression=op)}
                 if result is None:
                     result = retrieval_of_op
                 else:
                     result = result.intersection(retrieval_of_op)
             yield from result
-            """
-            operands = list(expression.operands())
-            sets_of_individuals = [
-                set(
-                    self.instances(
-                        expression=operand, confidence_threshold=confidence_threshold
-                    )
-                )
-                for operand in operands
-            ]
-
-            if sets_of_individuals:
-                # Start with the set of individuals from the first operand
-                common_individuals = sets_of_individuals[0]
-
-                # Update the common individuals set with the intersection of subsequent sets
-                for individuals in sets_of_individuals[1:]:
-                    common_individuals.intersection_update(individuals)
-
-                # Yield individuals that are common across all operands
-                for individual in common_individuals:
-                    yield individual
-            """
         elif isinstance(expression, OWLObjectAllValuesFrom):
             """
             Given an OWLObjectAllValuesFrom ∀ r.C, retrieve its instances => 
@@ -354,16 +286,8 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
             """
             object_property = expression.get_property()
             filler_expression = expression.get_filler()
-
-            filler_individuals = set(self.instances(filler_expression, confidence_threshold))
-            to_yield_individuals = set()
-
-            for individual in self.set_inferred_individuals:
-                related_individuals = set(self.get_object_property_values(individual.str, object_property))
-                if not related_individuals or related_individuals <= filler_individuals:
-                    to_yield_individuals.add(individual)
-
-            yield from to_yield_individuals
+            yield from self.instances(OWLObjectComplementOf(OWLObjectSomeValuesFrom(object_property, OWLObjectComplementOf(filler_expression))))
+            
         elif isinstance(expression, OWLObjectMinCardinality) or isinstance(expression, OWLObjectSomeValuesFrom):
             """
             Given an OWLObjectSomeValuesFrom ∃ r.C, retrieve its instances => 
@@ -376,7 +300,7 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
             if isinstance(expression, OWLObjectMinCardinality):
                 cardinality = expression.get_cardinality()
 
-            object_individuals = self.instances(filler_expression, confidence_threshold)
+            object_individuals = self.instances(filler_expression)
 
             # Initialize counter to keep track of individual occurrences
             result = Counter()
@@ -385,9 +309,7 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
             for object_individual in object_individuals:
                 subjects = self.get_individuals_with_object_property(
                     obj=object_individual,
-                    object_property=object_property,
-                    confidence_threshold=confidence_threshold
-                )
+                    object_property=object_property)
                 # Update the counter for all subjects found
                 result.update(subjects)
 
@@ -408,17 +330,16 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
             # Get all individuals that are instances of the filler expression.
             owl_individual:OWLNamedIndividual
             object_individuals = { owl_individual for owl_individual
-                                   in self.instances(filler_expression, confidence_threshold)}
+                                   in self.instances(filler_expression)}
 
             # Initialize a dictionary to keep track of counts of related individuals for each entity.
             owl_individual:OWLNamedIndividual
-            str_subject_individuals_to_count = {owl_individual.str: (owl_individual,0) for owl_individual in self.set_inferred_individuals}
+            str_subject_individuals_to_count = {owl_individual.str: (owl_individual,0) for owl_individual in self.individuals_in_signature()}
 
             for object_individual in object_individuals:
                 # Get all individuals related to the object individual via the object property.
                 subject_individuals = self.get_individuals_with_object_property(obj=object_individual,
-                                                              object_property=object_property,
-                                                              confidence_threshold=confidence_threshold)
+                                                              object_property=object_property)
 
                 # Update the count of related individuals for each object individual.
                 for subject_individual in subject_individuals:
@@ -436,7 +357,7 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
 
             result = None
             for op in expression.operands():
-                retrieval_of_op = {_ for _ in self.instances(expression=op, confidence_threshold=confidence_threshold)}
+                retrieval_of_op = {_ for _ in self.instances(expression=op)}
                 if result is None:
                     result = retrieval_of_op
                 else:
@@ -446,137 +367,26 @@ def instances(self, expression: OWLClassExpression, named_individuals=False,
         elif isinstance(expression, OWLObjectOneOf):
             yield from expression.individuals()
         else:
-            raise NotImplementedError(
-                f"Instances for {type(expression)} are not implemented yet"
-            )
-
-    def individuals_in_signature(self) -> Generator[OWLNamedIndividual, None, None]:
-
-        if self.inferred_owl_individuals is None:
-            seen_individuals = set()
-            try:
-                for cl in self.classes_in_signature():
-                    predictions = self.get_predictions(
-                        h=None, r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", t=cl.str,
-                        confidence_threshold=self.gamma)
-                    for prediction in predictions:
-                        try:
-                            owl_named_individual = OWLNamedIndividual(prediction[0])
-                            if owl_named_individual not in seen_individuals:
-                                seen_individuals.add(owl_named_individual)
-                                yield owl_named_individual
-                        except Exception as e:  # pragma: no cover
-                            print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-
-                    self.inferred_owl_individuals = seen_individuals
-            except Exception as e:  # pragma: no cover
-                print(f"Error processing classes in signature: {e}")
-        else:
-            yield from self.inferred_owl_individuals
-
-    def data_properties_in_signature(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLDataProperty, None, None]:
-        for prediction in self.get_predictions(
-                h=None,
-                r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-                t="http://www.w3.org/2002/07/owl#DatatypeProperty",
-                confidence_threshold=confidence_threshold,
-        ):
-            try:  # pragma: no cover
-                yield OWLDataProperty(prediction[0])
-            except Exception as e:
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
+            raise NotImplementedError(f"Instances for {type(expression)} are not implemented yet")
 
-    def object_properties_in_signature(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLObjectProperty, None, None]:
-        if self.inferred_object_properties is None:
-            self.inferred_object_properties = set()
-            for prediction in self.get_predictions(
-                    h=None,
-                    r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-                    t="http://www.w3.org/2002/07/owl#ObjectProperty",
-                    confidence_threshold=confidence_threshold,
-            ):
-                try:
-                    owl_obj_property = OWLObjectProperty(prediction[0])
-                    self.inferred_object_properties.add(owl_obj_property)
-                    yield owl_obj_property
-                except Exception as e:  # pragma: no cover
-                    # Log the invalid IRI
-                    print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                    continue
-
-    def boolean_data_properties(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLDataProperty, None, None]:  # pragma: no cover
-        for prediction in self.get_predictions(
-                h=None,
-                r="http://www.w3.org/2000/01/rdf-schema#range",
-                t="http://www.w3.org/2001/XMLSchema#boolean",
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLDataProperty(prediction[0])
-            except Exception as e:
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
-
-    def double_data_properties(
-            self, confidence_threshold: float = None
-    ) -> Generator[OWLDataProperty, None, None]:  # pragma: no cover
-        for prediction in self.get_predictions(
-                h=None,
-                r="http://www.w3.org/2000/01/rdf-schema#range",
-                t="http://www.w3.org/2001/XMLSchema#double",
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLDataProperty(prediction[0])
-            except Exception as e:
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
 
     ### additional functions for neural reasoner
 
     def get_object_property_values(
-            self,
-            subject: str,
-            object_property: OWLObjectProperty,
-            confidence_threshold: float = None,
-    ) -> Generator[OWLNamedIndividual, None, None]:
+            self, subject: str, object_property: OWLObjectProperty=None) -> List[OWLNamedIndividual]:
+        assert isinstance(object_property, OWLObjectProperty) or isinstance(object_property, OWLObjectInverseOf)
         if is_inverse := isinstance(object_property, OWLObjectInverseOf):
             object_property = object_property.get_inverse()
-        for prediction in self.get_predictions(
+        return [OWLNamedIndividual(top_entity) for top_entity, score in self.predict(
                 h=None if is_inverse else subject,
-                r=object_property.str,
-                t=subject if is_inverse else None,
-                confidence_threshold=confidence_threshold,
-        ):
-            try:
-                yield OWLNamedIndividual(prediction[0])
-            except Exception as e:  # pragma: no cover
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
+                r=object_property.iri.str,
+                t=subject if is_inverse else None)]
 
-    def get_data_property_values(
-            self,
-            subject: str,
-            data_property: OWLDataProperty,
-            confidence_threshold: float = None,
-    ) -> Generator[OWLLiteral, None, None]:  # pragma: no cover
-        for prediction in self.get_predictions(
+    def get_data_property_values(self, subject: str, data_property: OWLDataProperty) -> Generator[OWLLiteral, None, None]:  # pragma: no cover
+        for prediction in self.predict(
                 h=subject,
                 r=data_property.str,
-                t=None,
-                confidence_threshold=confidence_threshold,
-        ):
+                t=None):
             try:
                 # TODO: check the datatype and convert it to the correct type
                 # like in abox triplestore line 773ff
@@ -590,53 +400,22 @@ def get_data_property_values(
                 print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
                 continue
 
-    def get_individuals_of_class(
-            self, owl_class: OWLClass, confidence_threshold: float = None
-    ) -> Generator[OWLNamedIndividual, None, None]:
-        predictions = self.get_predictions(
-            h=None,
-            r="http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-            t=owl_class.str,
-            confidence_threshold=confidence_threshold,
-        )
-        seen = set()
-        for prediction in predictions:
-            try:
-                owl_named_individual = OWLNamedIndividual(prediction[0])
-                if owl_named_individual not in seen:
-                    seen.add(owl_named_individual)
-                    yield owl_named_individual
-            except Exception as e:  # pragma: no cover
-                # Log the invalid IRI
-                print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
-                continue
-
-        if len(list(predictions)) == 0:
-            # abstract class / class that does not have any instances -> get all child classes and make predictions
-            for child_class in self.subconcepts(owl_class, confidence_threshold=confidence_threshold):
-                for individual in self.get_individuals_of_class(child_class, confidence_threshold=confidence_threshold):
-                    if individual not in seen:
-                        seen.add(individual)
-                        yield individual
-
     def get_individuals_with_object_property(
             self,
-            object_property: OWLObjectProperty, obj: OWLClass, confidence_threshold: float = None ) \
+            object_property: OWLObjectProperty, obj: OWLClass) \
             -> Generator[OWLNamedIndividual, None, None]:
-
         is_inverse = isinstance(object_property, OWLObjectInverseOf)
 
         if is_inverse:
             object_property = object_property.get_inverse()
 
-        for prediction in self.get_predictions(
+        for entity, score in self.predict(
                 h=obj.str if is_inverse else None,
                 r=object_property.str,
-                t=None if is_inverse else obj.str,
-                confidence_threshold=confidence_threshold):
+                t=None if is_inverse else obj.str):
 
             try:
-                yield OWLNamedIndividual(prediction[0])
+                yield OWLNamedIndividual(entity)
             except Exception as e:  # pragma: no cover
                 # Log the invalid IRI
                 print(f"Invalid IRI detected: {prediction[0]}, error: {e}")
diff --git a/ontolearn/scripts/litserve_neural_reasoner.py b/ontolearn/scripts/litserve_neural_reasoner.py
new file mode 100644
index 00000000..bbb96884
--- /dev/null
+++ b/ontolearn/scripts/litserve_neural_reasoner.py
@@ -0,0 +1,94 @@
+import argparse
+import litserve as ls
+from ontolearn.owl_neural_reasoner import TripleStoreNeuralReasoner
+from owlapy import dl_to_owl_expression
+from owlapy import owl_expression_to_dl
+
+class NeuralReasonerAPI(ls.LitAPI):
+    """
+    NeuralReasonerAPI is a LitAPI implementation that handles requests to a neural reasoner 
+    using OWL expressions. It utilizes a neural embedding model for reasoning 
+    over ontology data.
+    
+    Attributes:
+        path_neural_embedding (str): Path to the neural embedding.
+        gamma (float): Minimum confidence threshold for the reasoning model, defaults to 0.9.
+    """
+    def __init__(self, path_neural_embedding, gamma=0.9):
+        """
+        Initializes the NeuralReasonerAPI with the path to the neural embedding and gamma value.
+        
+        Args:
+            path_neural_embedding (str): Path to the neural embedding model.
+			gamma (float): Minimum confidence threshold for the reasoning model, defaults to 0.9.
+        """
+        super().__init__()
+        self.path_neural_embedding = path_neural_embedding
+        self.gamma = gamma
+    
+    def setup(self, device):
+        """
+        Sets up the neural reasoner instance.
+        """
+        self.neural_owl_reasoner = TripleStoreNeuralReasoner(
+            path_neural_embedding=self.path_neural_embedding, gamma=self.gamma)
+        
+    def decode_request(self, request):
+        """
+        Decodes an incoming request to extract the DL expression and namespace.
+        
+        Args:
+            request (dict): A dictionary containing the request data, with 'expression' and 'namespace' keys.
+        
+        Returns:
+            tuple: A tuple with the DL expression (str) and namespace (str).
+        """
+        expression = request["expression"]  
+        namespace = request["namespace"]    
+        return expression, namespace
+        
+    def predict(self, data):
+        """
+        Predicts individuals of the given OWL expression using the neural reasoner.
+        
+        Args:
+            data (tuple): A tuple containing the DL expression (str) and namespace (str).
+        
+        Returns:
+            set: A set of individuals satisfying the given OWL expression.
+        """
+        expressions, namespace = data
+        owl_expression = dl_to_owl_expression(
+            namespace=namespace,
+            dl_expression=expressions
+        )  # Convert DL string to OWLClassExpression
+        return set(self.neural_owl_reasoner.individuals(owl_expression))
+
+    def encode_response(self, output):
+        """
+        Encodes the output from the reasoner back into a DL expression format for response.
+        
+        Args:
+            output (set): A set of OWL expressions representing the individuals.
+        
+        Returns:
+            dict: A dictionary with 'retrieval_result' key containing a list of DL expressions as strings.
+        """
+        return {'retrieval_result': [owl_expression_to_dl(out) for out in output]}
+
+
+if __name__ == "__main__":
+    # Parse command-line arguments
+    parser = argparse.ArgumentParser(description="Neural Reasoner API")
+    parser.add_argument("--path_kge_model", type=str, default="KGs_Family_father_owl",
+                        help="Path to the neural embedding folder")
+    parser.add_argument("--workers", type=int, default=1,
+                        help="Number of model copies for reasoning per device")
+    parser.add_argument("--path_kg", type=str, default="KGs/Family/father.owl",
+                        help="Path to the ontology file")
+    args = parser.parse_args()
+
+    # Initialize API and server
+    api = NeuralReasonerAPI(path_neural_embedding=args.path_kge_model)
+    server = ls.LitServer(api, accelerator="auto", workers_per_device=args.workers, track_requests=True)
+    server.run(port=8000)
diff --git a/ontolearn/triple_store.py b/ontolearn/triple_store.py
index 2bc2eb60..3e4726e0 100644
--- a/ontolearn/triple_store.py
+++ b/ontolearn/triple_store.py
@@ -46,7 +46,7 @@
 from owlapy.owl_individual import OWLNamedIndividual
 from owlapy.owl_literal import OWLLiteral
 from owlapy.owl_ontology import OWLOntologyID
-from owlapy.abstracts import AbstractOWLOntology, AbstractOWLReasonerEx
+from owlapy.abstracts import AbstractOWLOntology, AbstractOWLReasoner
 from owlapy.owl_property import (
     OWLDataProperty,
     OWLObjectPropertyExpression,
@@ -290,7 +290,7 @@ def __repr__(self):
         return f"TripleStoreOntology({self.url})"
 
 
-class TripleStoreReasoner(AbstractOWLReasonerEx):
+class TripleStoreReasoner(AbstractOWLReasoner):
     __slots__ = "ontology"
 
     def __init__(self, ontology: TripleStoreOntology):
@@ -1168,14 +1168,9 @@ def individuals(
         if concept is None or concept.is_owl_thing():
             yield from self.reasoner.individuals_in_signature()
         else:
+            yield from self.reasoner.instances(concept, named_individuals=named_individuals)
 
-            yield from self.reasoner.instances(
-                concept, named_individuals=named_individuals
-            )
-
-    def get_types(
-            self, ind: OWLNamedIndividual, direct: True
-    ) -> Generator[OWLClass, None, None]:
+    def get_types(self, ind: OWLNamedIndividual, direct: True) -> Generator[OWLClass, None, None]:
         if not direct:
             raise NotImplementedError("Inferring indirect types not available")
         return self.reasoner.get_type_individuals(ind.str)
diff --git a/ontolearn/utils/static_funcs.py b/ontolearn/utils/static_funcs.py
index 47a17783..15553dc2 100644
--- a/ontolearn/utils/static_funcs.py
+++ b/ontolearn/utils/static_funcs.py
@@ -205,7 +205,7 @@ def compute_tp_fn_fp_tn(individuals, pos, neg):  # pragma: no cover
     return tp, fn, fp, tn
 
 
-def compute_f1_score(individuals, pos, neg) -> float:  # pragma: no cover
+def compute_f1_score(individuals, pos, neg) -> float:
     """ Compute F1-score of a concept
     """
     assert type(individuals) == type(pos) == type(neg), f"Types must match:{type(individuals)},{type(pos)},{type(neg)}"
diff --git a/setup.py b/setup.py
index 5b3a6d05..2b39b2cf 100644
--- a/setup.py
+++ b/setup.py
@@ -38,6 +38,7 @@
     "scikit-learn>=1.4.1",
     "torch==2.2.0",
     "rdflib>=6.0.2",
+    "ruff>=0.7.2",
     "pandas>=1.5.0",
     "sortedcontainers>=2.4.0",
     "deap>=1.3.1",
@@ -46,7 +47,7 @@
     "tqdm>=4.64.0",
     "transformers>=4.38.1",
     "pytest>=7.2.2",
-    "owlapy==1.3.1",
+    "owlapy==1.3.3",
     "dicee==0.1.4",
     "ontosample>=0.2.2",
     "sphinx>=7.2.6",
@@ -89,12 +90,12 @@ def deps_list(*pkgs):
                            "sphinxcontrib-plantuml",
                            "plantuml-local-client", "myst-parser"))
 
-extras["full"] = (extras["min"] + deps_list("httpx", "pytest", "ontosample"))
+extras["full"] = (extras["min"] + deps_list("httpx", "pytest", "ontosample", "ruff"))
 
 setup(
     name="ontolearn",
     description="Ontolearn is an open-source software library for structured machine learning in Python. Ontolearn includes modules for processing knowledge bases, inductive logic programming and ontology engineering.",
-    version="0.8.0",
+    version="0.8.1",
     packages=find_packages(),
     install_requires=extras["min"],
     extras_require=extras,


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d469601f..2f167842 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,8 +18,11 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          pip3 install -e .
-          
+          pip3 install -e .["full"]      
+
+      - name: Lint with ruff
+        run: |
+          ruff check ontolearn/learners/ --line-length=200
 
       - name: Test with pytest
         run: |
diff --git a/tests/test_base.py b/tests/test_base.py
deleted file mode 100644
index 550d8859..00000000
--- a/tests/test_base.py
+++ /dev/null
@@ -1,43 +0,0 @@
-""" Test the base module"""
-# import os
-
-from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.utils import setup_logging
-
-PATH_FAMILY = 'KGs/Family/family-benchmark_rich_background.owl'
-PATH_FATHER = 'KGs/Family/father.owl'
-
-
-def test_knowledge_base():
-    kb = KnowledgeBase(path=PATH_FAMILY)
-    assert len([i for i in kb.tbox()]) > 0
-    assert len([set(kb.tbox(j)) for j in kb.get_concepts()]) > 0
-
-    # assert kb.name == 'family-benchmark_rich_background'
-    #
-    # assert kb.property_hierarchy
-    # assert kb.property_hierarchy.all_properties
-    # assert len(kb.property_hierarchy.all_properties) >= \
-    #        len(kb.property_hierarchy.data_properties)
-    # assert len(kb.property_hierarchy.all_properties) >= \
-    #        len(kb.property_hierarchy.object_properties)
-
-
-def test_multiple_knowledge_bases():
-    KnowledgeBase(path=PATH_FAMILY)
-
-    # There should not be an exception here
-    # (that refers to the family ontology)
-
-
-# def test_knowledge_base_save():
-#     kb = KnowledgeBase(path=PATH_FAMILY)
-#     kb.save('test_kb_save', rdf_format='nt')
-#     assert os.stat('test_kb_save.nt').st_size > 0
-#     os.remove('test_kb_save.nt')
-
-
-if __name__ == '__main__':
-    test_knowledge_base()
-    test_multiple_knowledge_bases()
-    # test_knowledge_base_save()
diff --git a/tests/test_base_concept_learner.py b/tests/test_base_concept_learner.py
index 9d9a9151..cc408a8c 100644
--- a/tests/test_base_concept_learner.py
+++ b/tests/test_base_concept_learner.py
@@ -118,7 +118,7 @@ def test_learn_predict_workflow(self):
         with tempfile.TemporaryDirectory() as tmpdirname:
             file_path = tmpdirname + '/Predictions'
             model.save_best_hypothesis(n=2, path=file_path)
-            hypotheses = list(model.load_hypotheses(file_path + '.owl'))
+            hypotheses = list(model.load_hypotheses(file_path + '#.owl'))
 
         # New Individuals
         julia = OWLNamedIndividual(IRI.create(self.namespace, 'julia'))
diff --git a/tests/test_celoe.py b/tests/test_celoe.py
index d10947c5..0d7b861f 100644
--- a/tests/test_celoe.py
+++ b/tests/test_celoe.py
@@ -1,15 +1,21 @@
 """ Test the default pipeline for structured machine learning"""
 import json
-
 from owlapy.class_expression import OWLClass
 from owlapy.iri import IRI
 from owlapy.owl_individual import OWLNamedIndividual
-
 from ontolearn.knowledge_base import KnowledgeBase
 from ontolearn.concept_learner import CELOE
 from ontolearn.learning_problem import PosNegLPStandard
 from ontolearn.utils import compute_f1_score
 from owlapy.render import DLSyntaxObjectRenderer
+import json
+import os
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.learners import OCEL
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.utils import setup_logging
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+from owlapy.class_expression import OWLClass
 
 PATH_FAMILY = 'KGs/Family/family-benchmark_rich_background.owl'
 PATH_MUTAGENESIS = 'KGs/Mutagenesis/mutagenesis.owl'
@@ -19,7 +25,55 @@
     settings = json.load(json_file)
 
 
-class Test_Celoe:
+class TestCeloe:
+
+    def test_ocel_example(self):
+
+        kb = KnowledgeBase(path=PATH_FAMILY)
+
+        for str_target_concept, examples in settings['problems'].items():
+            p = set(examples['positive_examples'])
+            n = set(examples['negative_examples'])
+            print('Target concept: ', str_target_concept)
+            concepts_to_ignore = set()
+            # lets inject more background info
+            if str_target_concept in ['Granddaughter', 'Aunt', 'Sister']:
+                NS = 'http://www.benchmark.org/family#'
+                concepts_to_ignore = {
+                    OWLClass(IRI(NS, 'Brother')),
+                    OWLClass(IRI(NS, 'Sister')),
+                    OWLClass(IRI(NS, 'Daughter')),
+                    OWLClass(IRI(NS, 'Mother')),
+                    OWLClass(IRI(NS, 'Grandmother')),
+                    OWLClass(IRI(NS, 'Father')),
+                    OWLClass(IRI(NS, 'Grandparent')),
+                    OWLClass(IRI(NS, 'PersonWithASibling')),
+                    OWLClass(IRI(NS, 'Granddaughter')),
+                    OWLClass(IRI(NS, 'Son')),
+                    OWLClass(IRI(NS, 'Child')),
+                    OWLClass(IRI(NS, 'Grandson')),
+                    OWLClass(IRI(NS, 'Grandfather')),
+                    OWLClass(IRI(NS, 'Grandchild')),
+                    OWLClass(IRI(NS, 'Parent')),
+                }
+                target_kb = kb.ignore_and_copy(ignored_classes=concepts_to_ignore)
+            else:
+                target_kb = kb
+
+            typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
+            typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
+            lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+            model = OCEL(knowledge_base=target_kb,
+                         max_runtime=3,
+                         max_num_of_concepts_tested=10_000_000_000,
+                         iter_bound=10_000_000_000)
+            model.fit(lp)
+
+            model.save_best_hypothesis(n=3, path='Predictions_{0}'.format(str_target_concept))
+            hypotheses = model.best_hypotheses(n=3)
+            [print(_) for _ in hypotheses]
+
     def test_celoe(self):
         kb = KnowledgeBase(path=PATH_FAMILY)
 
diff --git a/tests/test_clip.py b/tests/test_clip.py
new file mode 100644
index 00000000..3ba00a9d
--- /dev/null
+++ b/tests/test_clip.py
@@ -0,0 +1,50 @@
+from ontolearn.concept_learner import CLIP
+from ontolearn.refinement_operators import ExpressRefinement
+from ontolearn.knowledge_base import KnowledgeBase
+from owlapy.parser import DLSyntaxParser
+import sys
+from ontolearn.metrics import F1
+import time
+import random
+import unittest
+import os
+import warnings
+warnings.filterwarnings("ignore")
+
+class TestCLIP:
+
+    def test_prediction_quality_family(self):
+        knowledge_base_path="./CLIPData/family/family.owl"
+        path_of_embeddings="./CLIPData/family/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            KB = KnowledgeBase(path="./CLIPData/family/family.owl")
+            op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
+                              use_numeric_datatypes=False)
+            clip = CLIP(knowledge_base=KB, path_of_embeddings=path_of_embeddings,
+                 refinement_operator=op, load_pretrained=True, max_runtime=60)
+            kb_namespace = list(KB.ontology.classes_in_signature())[0].iri.get_namespace()
+            dl_parser = DLSyntaxParser(kb_namespace)
+            brother = dl_parser.parse('Brother')
+            daughter = dl_parser.parse('Daughter')
+            pos = set(KB.individuals(brother)).union(set(KB.individuals(daughter)))
+            neg = set(KB.individuals())-set(pos)
+            node = list(clip.fit(pos, neg).best_descriptions)[0]
+            assert node.quality > 0.9
+
+    def test_prediction_quality_mutagenesis(self):
+        knowledge_base_path="./CLIPData/mutagenesis/mutagenesis.owl"
+        path_of_embeddings="./CLIPData/mutagenesis/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            KB = KnowledgeBase(path=knowledge_base_path)
+            op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
+                              use_numeric_datatypes=False)
+            clip = CLIP(knowledge_base=KB, path_of_embeddings=path_of_embeddings,
+                 refinement_operator=op, load_pretrained=True, max_runtime=60)
+            kb_namespace = list(KB.ontology.classes_in_signature())[0].iri.get_namespace()
+            dl_parser = DLSyntaxParser(kb_namespace)
+            exists_inbond = dl_parser.parse('∃ inBond.Carbon-10')
+            not_bond7 = dl_parser.parse('¬Bond-7')
+            pos = set(KB.individuals(exists_inbond)).intersection(set(KB.individuals(not_bond7)))
+            neg = set(KB.individuals())-set(pos)
+            node = list(clip.fit(pos, neg).best_descriptions)[0]
+            assert node.quality > 0.25
diff --git a/tests/test_clip.txt b/tests/test_clip.txt
deleted file mode 100644
index 039f9018..00000000
--- a/tests/test_clip.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-from ontolearn.concept_learner import CLIP
-from ontolearn.refinement_operators import ExpressRefinement
-from ontolearn.knowledge_base import KnowledgeBase
-from owlapy.parser import DLSyntaxParser
-import sys
-from ontolearn.metrics import F1
-import time
-import random
-import unittest
-
-import warnings
-warnings.filterwarnings("ignore")
-
-class TestCLIP(unittest.TestCase):
-
-    def test_prediction_quality_family(self):
-        KB = KnowledgeBase(path="./CLIPData/family/family.owl")
-        op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
-                          use_numeric_datatypes=False)
-        clip = CLIP(knowledge_base=KB, path_of_embeddings="./CLIPData/family/embeddings/ConEx_entity_embeddings.csv",
-             refinement_operator=op, load_pretrained=True, max_runtime=60)
-        kb_namespace = list(KB.ontology.classes_in_signature())[0].iri.get_namespace()
-        dl_parser = DLSyntaxParser(kb_namespace)
-        brother = dl_parser.parse('Brother')
-        daughter = dl_parser.parse('Daughter')
-        pos = set(KB.individuals(brother)).union(set(KB.individuals(daughter)))
-        neg = set(KB.individuals())-set(pos)
-        node = list(clip.fit(pos, neg).best_descriptions)[0]
-        assert node.quality > 0.9
-
-    def test_prediction_quality_mutagenesis(self):
-        KB = KnowledgeBase(path="./CLIPData/mutagenesis/mutagenesis.owl")
-        op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
-                          use_numeric_datatypes=False)
-        clip = CLIP(knowledge_base=KB, path_of_embeddings="./CLIPData/mutagenesis/embeddings/ConEx_entity_embeddings.csv",
-             refinement_operator=op, load_pretrained=True, max_runtime=60)
-        kb_namespace = list(KB.ontology.classes_in_signature())[0].iri.get_namespace()
-        dl_parser = DLSyntaxParser(kb_namespace)
-        exists_inbond = dl_parser.parse('∃ inBond.Carbon-10')
-        not_bond7 = dl_parser.parse('¬Bond-7')
-        pos = set(KB.individuals(exists_inbond)).intersection(set(KB.individuals(not_bond7)))
-        neg = set(KB.individuals())-set(pos)
-        node = list(clip.fit(pos, neg).best_descriptions)[0]
-        assert node.quality > 0.95
-if __name__ == "__main__":
-    test = TestCLIP()
-    test.test_prediction_quality_family()
-    test.test_prediction_quality_mutagenesis()
diff --git a/tests/test_clip_trainer.py b/tests/test_clip_trainer.py
new file mode 100644
index 00000000..0148400d
--- /dev/null
+++ b/tests/test_clip_trainer.py
@@ -0,0 +1,44 @@
+from ontolearn.concept_learner import CLIP
+from ontolearn.refinement_operators import ExpressRefinement
+from ontolearn.knowledge_base import KnowledgeBase
+import time
+import numpy as np
+import torch
+import os
+import json
+import random
+import unittest
+import warnings
+warnings.filterwarnings("ignore")
+import os
+def seed_everything():
+    seed = 42
+    os.environ['PYTHONHASHSEED'] = str(seed)
+    os.environ['TOKENIZERS_PARALLELISM'] = 'true'
+    random.seed(seed)
+    np.random.seed(seed)
+    torch.manual_seed(seed)
+    if torch.cuda.is_available():
+        torch.cuda.manual_seed(seed)
+        torch.cuda.manual_seed_all(seed)
+        torch.backends.cudnn.deterministic = True
+        torch.backends.cudnn.benchmark = False
+    print('-----Seed Set!-----')
+
+seed_everything()
+
+
+class TestCLIPTrainer:
+
+    def test_trainer_family(self):
+        knowledge_base_path="./CLIPData/family/family.owl"
+        path_of_embeddings="./CLIPData/family/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            KB = KnowledgeBase(path="./CLIPData/family/family.owl")
+            op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
+                              use_numeric_datatypes=False)
+            clip = CLIP(knowledge_base=KB, path_of_embeddings="./CLIPData/family/embeddings/ConEx_entity_embeddings.csv",
+                 refinement_operator=op, load_pretrained=True, max_runtime=60)
+            with open("./CLIPData/family/LPs.json") as f:
+                data = json.load(f)
+            clip.train(list(data.items())[-100:], epochs=5, learning_rate=0.001, save_model=False, record_runtime=False, storage_path=f"./CLIP-{time.time()}/")
diff --git a/tests/test_clip_trainer.txt b/tests/test_clip_trainer.txt
deleted file mode 100644
index 132970b6..00000000
--- a/tests/test_clip_trainer.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-from ontolearn.concept_learner import CLIP
-from ontolearn.refinement_operators import ExpressRefinement
-from ontolearn.knowledge_base import KnowledgeBase
-import time
-import numpy as np
-import torch
-import os
-import json
-import random
-import unittest
-import warnings
-warnings.filterwarnings("ignore")
-
-def seed_everything():
-    seed = 42
-    os.environ['PYTHONHASHSEED'] = str(seed)
-    os.environ['TOKENIZERS_PARALLELISM'] = 'true'
-    random.seed(seed)
-    np.random.seed(seed)
-    torch.manual_seed(seed)
-    if torch.cuda.is_available():
-        torch.cuda.manual_seed(seed)
-        torch.cuda.manual_seed_all(seed)
-        torch.backends.cudnn.deterministic = True
-        torch.backends.cudnn.benchmark = False
-    print('-----Seed Set!-----')
-
-seed_everything()
-
-
-class TestCLIPTrainer(unittest.TestCase):
-
-    def test_trainer_family(self):
-        KB = KnowledgeBase(path="./CLIPData/family/family.owl")
-        op = ExpressRefinement(knowledge_base=KB, use_inverse=False,
-                          use_numeric_datatypes=False)
-        clip = CLIP(knowledge_base=KB, path_of_embeddings="./CLIPData/family/embeddings/ConEx_entity_embeddings.csv",
-             refinement_operator=op, load_pretrained=True, max_runtime=60)
-        with open("./CLIPData/family/LPs.json") as f:
-            data = json.load(f)
-        clip.train(list(data.items())[-100:], epochs=5, learning_rate=0.001, save_model=False, record_runtime=False, storage_path=f"./CLIP-{time.time()}/")
-
-#if __name__ == "__main__":
-#    test = TestCLIPTrainer()
-    # test.test_trainer_family()
\ No newline at end of file
diff --git a/tests/test_concept.py b/tests/test_concept.py
index 4ddc974f..8df9e209 100644
--- a/tests/test_concept.py
+++ b/tests/test_concept.py
@@ -6,14 +6,14 @@
 
 from ontolearn.knowledge_base import KnowledgeBase
 from ontolearn.utils import setup_logging
-from owlapy.owl_reasoner import OntologyReasoner
+from owlapy.owl_reasoner import StructuralReasoner
 
 setup_logging("ontolearn/logging_test.conf")
 
 PATH_FAMILY = 'KGs/Family/family-benchmark_rich_background.owl'
 with open('examples/synthetic_problems.json') as json_file:
     settings = json.load(json_file)
-kb = KnowledgeBase(path=PATH_FAMILY, reasoner_factory=OntologyReasoner)
+kb = KnowledgeBase(path=PATH_FAMILY, reasoner_factory=StructuralReasoner)
 
 
 def test_concept():
diff --git a/tests/test_core_owl_hierarchy.py b/tests/test_core_owl_hierarchy.py
index 6fd61158..830d8ff7 100644
--- a/tests/test_core_owl_hierarchy.py
+++ b/tests/test_core_owl_hierarchy.py
@@ -7,7 +7,7 @@
 
 from owlapy.owl_hierarchy import ClassHierarchy, ObjectPropertyHierarchy, AbstractHierarchy
 from ontolearn.utils import setup_logging
-from owlapy.owl_reasoner import OntologyReasoner
+from owlapy.owl_reasoner import StructuralReasoner
 from owlapy.owl_ontology_manager import OntologyManager
 
 _T = TypeVar('_T')  #:
@@ -20,7 +20,7 @@ def test_object_property_hierarchy(self):
         NS = "http://www.biopax.org/examples/glycolysis#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Biopax/biopax.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         oph = ObjectPropertyHierarchy(reasoner)
         # for k in sorted(oph.roots()):
@@ -56,7 +56,7 @@ def test_class_hierarchy_restrict(self):
         NS = "http://www.benchmark.org/family#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Family/family-benchmark_rich_background.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         ch = ClassHierarchy(reasoner).restrict_and_copy(remove=frozenset({OWLClass(IRI(NS, 'Grandchild'))}))
 
@@ -70,7 +70,7 @@ def test_class_hierarchy_children(self):
         NS = "http://example.com/father#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Family/father.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         ch = ClassHierarchy(reasoner)
         # for k in sorted(ch.roots()):
@@ -84,7 +84,7 @@ def test_class_hierarchy_parents_roots(self):
         NS = "http://www.benchmark.org/family#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Family/family-benchmark_rich_background.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         ch = ClassHierarchy(reasoner)
         grandmother = OWLClass(IRI(NS, 'Grandmother'))
@@ -101,7 +101,7 @@ def test_class_hierarchy_siblings(self):
         NS = "http://www.benchmark.org/family#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Family/family-benchmark_rich_background.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         ch = ClassHierarchy(reasoner)
         child = OWLClass(IRI(NS, 'Child'))
@@ -115,7 +115,7 @@ def test_class_hierarchy_leaves(self):
         NS = "http://www.benchmark.org/family#"
         mgr = OntologyManager()
         onto = mgr.load_ontology(IRI.create("file://KGs/Family/family-benchmark_rich_background.owl"))
-        reasoner = OntologyReasoner(onto)
+        reasoner = StructuralReasoner(onto)
 
         ch = ClassHierarchy(reasoner)
         # for k in sorted(ch.roots()):
diff --git a/tests/test_drill.py b/tests/test_drill.py
index 11d86500..e4f6f72b 100644
--- a/tests/test_drill.py
+++ b/tests/test_drill.py
@@ -18,7 +18,7 @@ class TestDrill(unittest.TestCase):
 
     kg_path = "KGs/Family/family-benchmark_rich_background.owl"
     embeddings_path = "embeddings/Keci_entity_embeddings.csv"
-    lp_path = "examples/uncle_lp2.json"
+    lp_path = "examples/synthetic_problems.json"
 
     def test_regression_family(self):
         kb = KnowledgeBase(path=self.kg_path)
@@ -36,8 +36,8 @@ def test_regression_family(self):
 
         with open(self.lp_path) as json_file:
             examples = json.load(json_file)
-        p = examples['positive_examples']
-        n = examples['negative_examples']
+        p = examples["problems"]["Uncle"]['positive_examples']
+        n = examples["problems"]["Uncle"]['negative_examples']
 
         kf = StratifiedKFold(n_splits=5, shuffle=True, random_state=1)
         X = np.array(p + n)
diff --git a/tests/test_evolearner.py b/tests/test_evolearner.py
index 3f6ed879..82acb8be 100644
--- a/tests/test_evolearner.py
+++ b/tests/test_evolearner.py
@@ -9,6 +9,14 @@
 from ontolearn.concept_learner import EvoLearner
 from ontolearn.utils import setup_logging
 
+import json
+import os
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.concept_learner import EvoLearner
+from ontolearn.learning_problem import PosNegLPStandard
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+from owlapy.class_expression import OWLClass
+
 random.seed(1)
 
 
@@ -21,25 +29,17 @@ def test_regression_family(self):
         # @TODO: Explicitly define params
         model = EvoLearner(knowledge_base=kb, max_runtime=10)
 
-        regression_test_evolearner = {'Aunt': .99, 'Brother': .99,
-                                      'Cousin': 0.992, 'Granddaughter': .99,
-                                      'Uncle': 0.89, 'Grandgrandfather': .99}
+        regression_test_evolearner = {'Aunt': .90, 'Brother': .89,
+                                      'Cousin': 0.90, 'Granddaughter': .89,
+                                      'Uncle': 0.79, 'Grandgrandfather': .89}
         for str_target_concept, examples in settings['problems'].items():
             pos = set(map(OWLNamedIndividual, map(IRI.create, set(examples['positive_examples']))))
             neg = set(map(OWLNamedIndividual, map(IRI.create, set(examples['negative_examples']))))
-            print('Target concept: ', str_target_concept)
-
             lp = PosNegLPStandard(pos=pos, neg=neg)
             returned_model = model.fit(learning_problem=lp)
             assert returned_model == model
             hypotheses = list(returned_model.best_hypotheses(n=3, return_node=True))
             assert hypotheses[0].quality >= regression_test_evolearner[str_target_concept]
-            # best_hypotheses returns distinct hypotheses and sometimes the model will not find 'n' distinct hypothesis,
-            # hence the checks
-            if len(hypotheses) == 2:
-                assert hypotheses[0].quality >= hypotheses[1].quality
-            if len(hypotheses) == 3:
-                assert hypotheses[1].quality >= hypotheses[2].quality
 
     def test_regression_mutagenesis_multiple_fits(self):
         kb = KnowledgeBase(path='KGs/Mutagenesis/mutagenesis.owl')
@@ -61,3 +61,56 @@ def test_regression_mutagenesis_multiple_fits(self):
         returned_model = model.fit(learning_problem=lp)
         best_pred = returned_model.best_hypotheses(n=1, return_node=True)
         assert best_pred.quality == 1.00
+
+    def test_example(self):
+
+        with open('examples/synthetic_problems.json') as json_file:
+            settings = json.load(json_file)
+
+        kb = KnowledgeBase(path="KGs/Family/family-benchmark_rich_background.owl")
+
+        # noinspection DuplicatedCode
+        for str_target_concept, examples in settings['problems'].items():
+            p = set(examples['positive_examples'])
+            n = set(examples['negative_examples'])
+            print('Target concept: ', str_target_concept)
+
+            # lets inject more background info
+            if str_target_concept in ['Granddaughter', 'Aunt', 'Sister']:
+                NS = 'http://www.benchmark.org/family#'
+                concepts_to_ignore = {
+                    OWLClass(IRI(NS, 'Brother')),
+                    OWLClass(IRI(NS, 'Sister')),
+                    OWLClass(IRI(NS, 'Daughter')),
+                    OWLClass(IRI(NS, 'Mother')),
+                    OWLClass(IRI(NS, 'Grandmother')),
+                    OWLClass(IRI(NS, 'Father')),
+                    OWLClass(IRI(NS, 'Grandparent')),
+                    OWLClass(IRI(NS, 'PersonWithASibling')),
+                    OWLClass(IRI(NS, 'Granddaughter')),
+                    OWLClass(IRI(NS, 'Son')),
+                    OWLClass(IRI(NS, 'Child')),
+                    OWLClass(IRI(NS, 'Grandson')),
+                    OWLClass(IRI(NS, 'Grandfather')),
+                    OWLClass(IRI(NS, 'Grandchild')),
+                    OWLClass(IRI(NS, 'Parent')),
+                }
+                target_kb = kb.ignore_and_copy(ignored_classes=concepts_to_ignore)
+            else:
+                target_kb = kb
+
+            typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
+            typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
+            lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+            model = EvoLearner(knowledge_base=target_kb, max_runtime=600)
+            model.fit(lp, verbose=False)
+
+            model.save_best_hypothesis(n=3, path=f"Predictions_{str_target_concept}")
+            # Get Top n hypotheses
+            hypotheses = list(model.best_hypotheses(n=3))
+            # Use hypotheses as binary function to label individuals.
+            predictions = model.predict(individuals=list(typed_pos | typed_neg),
+                                        hypotheses=hypotheses)
+            # print(predictions)
+            [print(_) for _ in hypotheses]
diff --git a/tests/test_example_celoe.py b/tests/test_example_celoe.py
new file mode 100644
index 00000000..24b56995
--- /dev/null
+++ b/tests/test_example_celoe.py
@@ -0,0 +1,147 @@
+import json
+import os
+import random
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.learners import CELOE
+from ontolearn.heuristics import CELOEHeuristic
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.metrics import Accuracy
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+from owlapy.class_expression import OWLClass
+from ontolearn.refinement_operators import ModifiedCELOERefinement
+import json
+import os
+import random
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.learners import CELOE
+from ontolearn.heuristics import CELOEHeuristic
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.metrics import Accuracy
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+from owlapy.class_expression import OWLClass
+from ontolearn.refinement_operators import ModifiedCELOERefinement
+
+
+class TestCELOE:
+    def test_example(self):
+        with open('examples/synthetic_problems.json') as json_file:
+            settings = json.load(json_file)
+
+        kb = KnowledgeBase(path="KGs/Family/family-benchmark_rich_background.owl")
+
+        random.seed(0)
+
+        # noinspection DuplicatedCode
+        for str_target_concept, examples in settings['problems'].items():
+            p = set(examples['positive_examples'])
+            n = set(examples['negative_examples'])
+            print('Target concept: ', str_target_concept)
+
+            # lets inject more background info
+            if str_target_concept in ['Granddaughter', 'Aunt', 'Sister']:
+                NS = 'http://www.benchmark.org/family#'
+                concepts_to_ignore = {
+                    OWLClass(IRI(NS, 'Brother')),
+                    OWLClass(IRI(NS, 'Sister')),
+                    OWLClass(IRI(NS, 'Daughter')),
+                    OWLClass(IRI(NS, 'Mother')),
+                    OWLClass(IRI(NS, 'Grandmother')),
+                    OWLClass(IRI(NS, 'Father')),
+                    OWLClass(IRI(NS, 'Grandparent')),
+                    OWLClass(IRI(NS, 'PersonWithASibling')),
+                    OWLClass(IRI(NS, 'Granddaughter')),
+                    OWLClass(IRI(NS, 'Son')),
+                    OWLClass(IRI(NS, 'Child')),
+                    OWLClass(IRI(NS, 'Grandson')),
+                    OWLClass(IRI(NS, 'Grandfather')),
+                    OWLClass(IRI(NS, 'Grandchild')),
+                    OWLClass(IRI(NS, 'Parent')),
+                }
+                target_kb = kb.ignore_and_copy(ignored_classes=concepts_to_ignore)
+            else:
+                target_kb = kb
+
+            typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
+            typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
+            lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+            qual = Accuracy()
+            heur = CELOEHeuristic(expansionPenaltyFactor=0.05, startNodeBonus=1.0, nodeRefinementPenalty=0.01)
+            op = ModifiedCELOERefinement(knowledge_base=target_kb, use_negation=False, use_all_constructor=False)
+
+            model = CELOE(knowledge_base=target_kb,
+                          max_runtime=600,
+                          refinement_operator=op,
+                          quality_func=qual,
+                          heuristic_func=heur,
+                          max_num_of_concepts_tested=100,
+                          iter_bound=100)
+            model.fit(lp)
+            model.save_best_hypothesis(n=1, path=f'Predictions_{str_target_concept}')
+            # Get Top n hypotheses
+            hypotheses = list(model.best_hypotheses(n=3))
+            # Use hypotheses as binary function to label individuals.
+            predictions = model.predict(individuals=list(typed_pos | typed_neg),
+                                        hypotheses=hypotheses)
+
+    def text_second_ma_example(self):
+
+        with open('examples/synthetic_problems.json') as json_file:
+            settings = json.load(json_file)
+
+        kb = KnowledgeBase(path="KGs/Family/family-benchmark_rich_background.owl")
+
+        random.seed(0)
+
+        # noinspection DuplicatedCode
+        for str_target_concept, examples in settings['problems'].items():
+            p = set(examples['positive_examples'])
+            n = set(examples['negative_examples'])
+            print('Target concept: ', str_target_concept)
+
+            # lets inject more background info
+            if str_target_concept in ['Granddaughter', 'Aunt', 'Sister']:
+                NS = 'http://www.benchmark.org/family#'
+                concepts_to_ignore = {
+                    OWLClass(IRI(NS, 'Brother')),
+                    OWLClass(IRI(NS, 'Sister')),
+                    OWLClass(IRI(NS, 'Daughter')),
+                    OWLClass(IRI(NS, 'Mother')),
+                    OWLClass(IRI(NS, 'Grandmother')),
+                    OWLClass(IRI(NS, 'Father')),
+                    OWLClass(IRI(NS, 'Grandparent')),
+                    OWLClass(IRI(NS, 'PersonWithASibling')),
+                    OWLClass(IRI(NS, 'Granddaughter')),
+                    OWLClass(IRI(NS, 'Son')),
+                    OWLClass(IRI(NS, 'Child')),
+                    OWLClass(IRI(NS, 'Grandson')),
+                    OWLClass(IRI(NS, 'Grandfather')),
+                    OWLClass(IRI(NS, 'Grandchild')),
+                    OWLClass(IRI(NS, 'Parent')),
+                }
+                target_kb = kb.ignore_and_copy(ignored_classes=concepts_to_ignore)
+            else:
+                target_kb = kb
+
+            typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
+            typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
+            lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+            qual = Accuracy()
+            heur = CELOEHeuristic(expansionPenaltyFactor=0.05, startNodeBonus=1.0, nodeRefinementPenalty=0.01)
+            op = ModifiedCELOERefinement(knowledge_base=target_kb, use_negation=False, use_all_constructor=False)
+
+            model = CELOE(knowledge_base=target_kb,
+                          max_runtime=600,
+                          refinement_operator=op,
+                          quality_func=qual,
+                          heuristic_func=heur,
+                          max_num_of_concepts_tested=100,
+                          iter_bound=100)
+            model.fit(lp)
+            model.save_best_hypothesis(n=1, path=f'Predictions_{str_target_concept}')
+            # Get Top n hypotheses
+            hypotheses = list(model.best_hypotheses(n=3))
+            # Use hypotheses as binary function to label individuals.
+            predictions = model.predict(individuals=list(typed_pos | typed_neg),
+                                        hypotheses=hypotheses)
diff --git a/tests/test_example_concept_learning_evaluation.py b/tests/test_example_concept_learning_evaluation.py
new file mode 100644
index 00000000..c9567c90
--- /dev/null
+++ b/tests/test_example_concept_learning_evaluation.py
@@ -0,0 +1,142 @@
+"""
+Regression Test for the example.
+Fitting OWL Class Expression Learners:
+
+Given positive examples (E^+)  and negative examples (E^-),
+Evaluate the performances of OWL Class Expression Learners  w.r.t. the quality of learned/found OWL Class Expression
+
+Example to run the script
+python examples/concept_learning_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family-benchmark_rich_background.owl --max_runtime 3 --report family.csv
+
+"""
+import json
+import time
+import pandas as pd
+from ontolearn.knowledge_base import KnowledgeBase
+from ontolearn.learners import CELOE, OCEL
+from ontolearn.concept_learner import EvoLearner
+from ontolearn.learners import Drill, TDL
+from ontolearn.learning_problem import PosNegLPStandard
+from ontolearn.metrics import F1
+from owlapy.owl_individual import OWLNamedIndividual, IRI
+import argparse
+from ontolearn.utils.static_funcs import compute_f1_score
+pd.set_option("display.precision", 5)
+
+class TestConceptLearning:
+    def test_learning(self):
+
+        with open('LPs/Family/lps.json') as json_file:
+            settings = json.load(json_file)
+
+        path_kb="KGs/Family/family-benchmark_rich_background.owl"
+        max_runtime=1
+        kb = KnowledgeBase(path=path_kb)
+        ocel = OCEL(knowledge_base=kb, quality_func=F1(), max_runtime=max_runtime)
+        celoe = CELOE(knowledge_base=kb, quality_func=F1(), max_runtime=max_runtime)
+        drill = Drill(knowledge_base=KnowledgeBase(path=path_kb),
+                      quality_func=F1(),
+                      max_runtime=max_runtime)
+        tdl = TDL(knowledge_base=KnowledgeBase(path=path_kb),
+                  kwargs_classifier={"random_state": 0},
+                  max_runtime=max_runtime)
+        # dictionary to store the data
+        data = dict()
+        for str_target_concept, examples in settings['problems'].items():
+            p = set(examples['positive_examples'])
+            n = set(examples['negative_examples'])
+            print('\n\n')
+
+            print('Target concept: ', str_target_concept)
+            data.setdefault("LP", []).append(str_target_concept)
+
+            typed_pos = set(map(OWLNamedIndividual, map(IRI.create, p)))
+            typed_neg = set(map(OWLNamedIndividual, map(IRI.create, n)))
+            lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg)
+
+            print("OCEL starts..", end="\t")
+            start_time = time.time()
+            pred_ocel = ocel.fit(lp).best_hypotheses(n=1)
+            print("OCEL ends..", end="\t")
+            rt_ocel = time.time() - start_time
+            f1_ocel = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_ocel)}), pos=lp.pos, neg=lp.neg)
+            data.setdefault("F1-OCEL", []).append(f1_ocel)
+            data.setdefault("RT-OCEL", []).append(rt_ocel)
+            print(f"OCEL Quality: {f1_ocel:.3f}", end="\t")
+            print(f"OCEL Runtime: {rt_ocel:.3f}")
+            print("CELOE starts..", end="\t")
+            start_time = time.time()
+            pred_celoe = celoe.fit(lp).best_hypotheses(n=1)
+            print("CELOE Ends..", end="\t")
+            rt_celoe = time.time() - start_time
+            f1_celoe = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_celoe)}), pos=lp.pos, neg=lp.neg)
+            data.setdefault("F1-CELOE", []).append(f1_celoe)
+            data.setdefault("RT-CELOE", []).append(rt_celoe)
+            print(f"CELOE Quality: {f1_celoe:.3f}", end="\t")
+            print(f"CELOE Runtime: {rt_celoe:.3f}")
+
+            print("Evo starts..", end="\t")
+            start_time = time.time()
+            # Evolearner has a bug and KB needs to be reloaded
+            evo = EvoLearner(knowledge_base=KnowledgeBase(path=path_kb), quality_func=F1(), max_runtime=max_runtime)
+            pred_evo = evo.fit(lp).best_hypotheses(n=1)
+            print("Evo ends..", end="\t")
+            rt_evo = time.time() - start_time
+            f1_evo = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_evo)}), pos=lp.pos, neg=lp.neg)
+            data.setdefault("F1-Evo", []).append(f1_evo)
+            data.setdefault("RT-Evo", []).append(rt_evo)
+            print(f"Evo Quality: {f1_evo:.3f}", end="\t")
+            print(f"Evo Runtime: {rt_evo:.3f}")
+
+            print("DRILL starts..", end="\t")
+            start_time = time.time()
+            pred_drill = drill.fit(lp).best_hypotheses(n=1)
+            print("DRILL ends..", end="\t")
+            rt_drill = time.time() - start_time
+            f1_drill = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_drill)}), pos=lp.pos, neg=lp.neg)
+            data.setdefault("F1-DRILL", []).append(f1_drill)
+            data.setdefault("RT-DRILL", []).append(rt_drill)
+            print(f"DRILL Quality: {f1_drill:.3f}", end="\t")
+            print(f"DRILL Runtime: {rt_drill:.3f}")
+
+            print("TDL starts..", end="\t")
+            start_time = time.time()
+            # () Fit model training dataset
+            pred_tdl = tdl.fit(lp).best_hypotheses(n=1)
+            print("TDL ends..", end="\t")
+            rt_tdl = time.time() - start_time
+
+            # () Quality on the training data
+            f1_tdl = compute_f1_score(individuals=frozenset({i for i in kb.individuals(pred_tdl)}),
+                                            pos=lp.pos,
+                                            neg=lp.neg)
+
+            data.setdefault("F1-TDL", []).append(f1_tdl)
+            data.setdefault("RT-TDL", []).append(rt_tdl)
+            print(f"TDL Quality: {f1_tdl:.3f}", end="\t")
+            print(f"TDL Runtime: {rt_tdl:.3f}")
+        df = pd.DataFrame.from_dict(data)
+        """
+        F1-OCEL     0.96130
+        RT-OCEL     0.32531
+        F1-CELOE    0.96744
+        RT-CELOE    0.19228
+        F1-Evo      0.98937
+        RT-Evo      0.55212
+        F1-DRILL    0.94741
+        RT-DRILL    0.52200
+        F1-TDL      1.00000
+        RT-TDL      0.35019
+        """
+        for i, (x,y) in enumerate(zip(df.select_dtypes(include="number").mean().values.tolist(), [0.94,
+                                                                                                  0.3,
+                                                                                                  0.95,
+                                                                                                  0.2,
+                                                                                                  0.97,
+                                                                                                  0.1,
+                                                                                                  0.92,
+                                                                                                  0.4,
+                                                                                                  0.95,
+                                                                                                  0.3])):
+            if i==0 or i%2==0:
+                assert x>y
\ No newline at end of file
diff --git a/tests/test_example_concept_learning_neural_evaluation.py b/tests/test_example_concept_learning_neural_evaluation.py
new file mode 100644
index 00000000..4c7fbcfc
--- /dev/null
+++ b/tests/test_example_concept_learning_neural_evaluation.py
@@ -0,0 +1,195 @@
+""" StratifiedKFold Cross Validating DL Concept Learning Algorithms
+
+dicee --path_single_kg "KGs/Family/family-benchmark_rich_background.owl" --model Keci --path_to_store_single_run KeciFamilyRun --backend rdflib
+
+
+python examples/concept_learning_neural_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family-benchmark_rich_background.owl --kge KeciFamilyRun --max_runtime 3 --report family.csv
+
+
+"""
+
+# import json
+# import time
+# import os
+# import subprocess
+# import platform
+# import pandas as pd
+# from ontolearn.knowledge_base import KnowledgeBase
+# from ontolearn.learners import CELOE, OCEL, Drill, TDL
+# from ontolearn.concept_learner import EvoLearner, NCES, CLIP
+# from ontolearn.refinement_operators import ExpressRefinement
+# from ontolearn.learning_problem import PosNegLPStandard
+# from ontolearn.metrics import F1
+# from owlapy.owl_individual import OWLNamedIndividual, IRI
+# import argparse
+# from sklearn.model_selection import StratifiedKFold
+# import numpy as np
+# from ontolearn.utils.static_funcs import compute_f1_score
+# from ontolearn.triple_store import TripleStore
+# from ontolearn.owl_neural_reasoner import TripleStoreNeuralReasoner
+# from owlapy import owl_expression_to_dl
+
+# pd.set_option("display.precision", 5)
+
+"""
+Regression Test for the example.
+Fitting OWL Class Expression Learners:
+
+Given positive examples (E^+)  and negative examples (E^-),
+Evaluate the performances of OWL Class Expression Learners  w.r.t. the quality of learned/found OWL Class Expression
+
+Example to run the script
+python examples/concept_learning_evaluation.py --lps LPs/Family/lps.json --kb KGs/Family/family-benchmark_rich_background.owl --max_runtime 3 --report family.csv
+
+"""
+# import json
+# import time
+# import os
+# import subprocess
+# import platform
+# import pandas as pd
+# from ontolearn.knowledge_base import KnowledgeBase
+# from ontolearn.learners import CELOE, OCEL, Drill, TDL
+# from ontolearn.concept_learner import EvoLearner, NCES, CLIP
+# from ontolearn.refinement_operators import ExpressRefinement
+# from ontolearn.learning_problem import PosNegLPStandard
+# from ontolearn.metrics import F1
+# from owlapy.owl_individual import OWLNamedIndividual, IRI
+# import argparse
+# from sklearn.model_selection import StratifiedKFold
+# import numpy as np
+# from ontolearn.utils.static_funcs import compute_f1_score
+# from ontolearn.triple_store import TripleStore
+# from ontolearn.owl_neural_reasoner import TripleStoreNeuralReasoner
+# from owlapy import owl_expression_to_dl
+
+# pd.set_option("display.precision", 5)
+
+# class TestConceptLearningCV:
+#     def test_cv(self):
+
+#         with open('LPs/Family/lps.json') as json_file:
+#             settings = json.load(json_file)
+
+#         path_kb="KGs/Family/family-benchmark_rich_background.owl"
+#         max_runtime=1
+#         random_seed=1
+#         folds=2
+
+#         from dicee.executer import Execute
+#         from dicee.config import Namespace
+#         args = Namespace()
+#         args.model = 'Keci'
+#         args.scoring_technique = "KvsAll"  # 1vsAll, or AllvsAll, or NegSample
+#         args.path_single_kg = path_kb
+#         args.path_to_store_single_run = "KeciFamilyRun"
+#         args.backend="rdflib"
+#         Execute(args).start()
+#         path_kge=args.path_to_store_single_run
+
+#         kb = KnowledgeBase(path=path_kb)
+#         drill_with_symbolic_retriever = Drill(knowledge_base=kb,
+#                                               quality_func=F1(), max_runtime=max_runtime,verbose=0)
+#         neural_kb = TripleStore(reasoner=TripleStoreNeuralReasoner(path_neural_embedding=path_kge))
+#         drill_with_neural_retriever = Drill(knowledge_base=neural_kb,
+#                                             quality_func=F1(), max_runtime=max_runtime, verbose=0)
+
+#         # dictionary to store the data
+#         data = dict()
+#         if "problems" in settings:
+#             problems = settings["problems"].items()
+#             positives_key = "positive_examples"
+#             negatives_key = "negative_examples"
+#         else:
+#             problems = settings.items()
+#             positives_key = "positive examples"
+#             negatives_key = "negative examples"
+
+#         for str_target_concept, examples in problems:
+#             print("Target concept: ", str_target_concept)
+#             p = examples[positives_key]
+#             n = examples[negatives_key]
+
+#             kf = StratifiedKFold(n_splits=folds, shuffle=True, random_state=random_seed)
+#             X = np.array(p + n)
+#             y = np.array([1.0 for _ in p] + [0.0 for _ in n])
+
+#             for ith, (train_index, test_index) in enumerate(kf.split(X, y)):
+#                 #
+#                 data.setdefault("LP", []).append(str_target_concept)
+#                 data.setdefault("Fold", []).append(ith)
+#                 # () Extract positive and negative examples from train fold
+#                 train_pos = {pos_individual for pos_individual in X[train_index][y[train_index] == 1]}
+#                 train_neg = {neg_individual for neg_individual in X[train_index][y[train_index] == 0]}
+
+#                 # Sanity checking for individuals used for training.
+#                 assert train_pos.issubset(examples[positives_key])
+#                 assert train_neg.issubset(examples[negatives_key])
+
+#                 # () Extract positive and negative examples from test fold
+#                 test_pos = {pos_individual for pos_individual in X[test_index][y[test_index] == 1]}
+#                 test_neg = {neg_individual for neg_individual in X[test_index][y[test_index] == 0]}
+
+#                 # Sanity checking for individuals used for testing.
+#                 assert test_pos.issubset(examples[positives_key])
+#                 assert test_neg.issubset(examples[negatives_key])
+#                 train_lp = PosNegLPStandard(
+#                     pos={OWLNamedIndividual(i) for i in train_pos},
+#                     neg={OWLNamedIndividual(i) for i in train_neg})
+
+#                 test_lp = PosNegLPStandard(
+#                     pos={OWLNamedIndividual(i) for i in test_pos},
+#                     neg={OWLNamedIndividual(i) for i in test_neg})
+#                 print("DRILL Symbolic starts..", end=" ")
+#                 start_time = time.time()
+#                 # Prediction of DRILL through symbolic retriever.
+#                 pred_symbolic_drill = drill_with_symbolic_retriever.fit(train_lp).best_hypotheses()
+#                 symbolic_rt_drill = time.time() - start_time
+#                 print("DRILL Symbolic ends..", end="\t")
+#                 # Quality of prediction through symbolic retriever on the train split.
+#                 symbolic_train_f1_drill = compute_f1_score(
+#                     individuals=frozenset({i for i in kb.individuals(pred_symbolic_drill)}),
+#                     pos=train_lp.pos,
+#                     neg=train_lp.neg)
+#                 # Quality of prediction through symbolic retriever on the test split.
+#                 symbolic_test_f1_drill = compute_f1_score(
+#                     individuals=frozenset({i for i in kb.individuals(pred_symbolic_drill)}),
+#                     pos=test_lp.pos,
+#                     neg=test_lp.neg)
+#                 print(f"DRILL Symbolic Train Quality: {symbolic_train_f1_drill:.3f}", end="\t")
+#                 print(f"DRILL Symbolic Test Quality: {symbolic_test_f1_drill:.3f}", end="\t")
+#                 print(f"DRILL Symbolic Runtime: {symbolic_rt_drill:.3f}", end="\t")
+#                 print(f"Prediction: {owl_expression_to_dl(pred_symbolic_drill)}")
+#                 data.setdefault("Train-F1-Symbolic-DRILL", []).append(symbolic_train_f1_drill)
+#                 data.setdefault("Test-F1-Symbolic-DRILL", []).append(symbolic_test_f1_drill)
+#                 data.setdefault("RT-Symbolic-DRILL", []).append(symbolic_rt_drill)
+#                 data.setdefault("Prediction-Symbolic-DRILL", []).append(owl_expression_to_dl(pred_symbolic_drill))
+
+#                 print("DRILL Neural starts..", end=" ")
+#                 start_time = time.time()
+#                 # Prediction of DRILL through symbolic retriever.
+#                 pred_neural_drill = drill_with_neural_retriever.fit(train_lp).best_hypotheses()
+#                 neural_rt_drill = time.time() - start_time
+#                 print("DRILL Neural ends..", end="\t")
+#                 # Quality of prediction through symbolic retriever on the train split.
+#                 neural_train_f1_drill = compute_f1_score(
+#                     individuals=frozenset({i for i in neural_kb.individuals(pred_neural_drill)}),
+#                     pos=train_lp.pos,
+#                     neg=train_lp.neg)
+#                 # Quality of prediction through symbolic retriever on the test split.
+#                 neural_test_f1_drill = compute_f1_score(
+#                     individuals=frozenset({i for i in neural_kb.individuals(pred_neural_drill)}),
+#                     pos=test_lp.pos,
+#                     neg=test_lp.neg)
+#                 print(f"DRILL Neural Train Quality: {neural_train_f1_drill:.3f}", end="\t")
+#                 print(f"DRILL Neural Test Quality: {neural_test_f1_drill:.3f}", end="\t")
+#                 print(f"DRILL Neural Runtime: {neural_rt_drill:.3f}", end="\t")
+#                 print(f"Prediction: {owl_expression_to_dl(pred_neural_drill)}")
+
+#                 data.setdefault("Train-F1-Neural-DRILL", []).append(neural_train_f1_drill)
+#                 data.setdefault("Test-F1-Neural-DRILL", []).append(neural_test_f1_drill)
+#                 data.setdefault("RT-Neural-DRILL", []).append(neural_rt_drill)
+#                 data.setdefault("Prediction-Neural-DRILL", []).append(owl_expression_to_dl(pred_neural_drill))
+
+#         df = pd.DataFrame.from_dict(data)
+#         assert df.select_dtypes(include="number").mean()["Train-F1-Symbolic-DRILL"] >= 0.93
diff --git a/tests/test_knowledge_base.py b/tests/test_knowledge_base.py
index be140478..0d793bbe 100644
--- a/tests/test_knowledge_base.py
+++ b/tests/test_knowledge_base.py
@@ -1,654 +1,29 @@
-import unittest
-from itertools import repeat
-
-from owlapy.class_expression import OWLObjectUnionOf, OWLThing, OWLClass, OWLDataAllValuesFrom, OWLDataHasValue, \
-    OWLNothing, OWLDataSomeValuesFrom, OWLObjectComplementOf, OWLObjectExactCardinality, OWLObjectMaxCardinality, \
-    OWLObjectAllValuesFrom, OWLObjectHasValue, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom, OWLObjectMinCardinality
-from owlapy.iri import IRI
-from owlapy.owl_axiom import OWLSubDataPropertyOfAxiom, OWLSubObjectPropertyOfAxiom, OWLClassAssertionAxiom, \
-    OWLEquivalentClassesAxiom, OWLSubClassOfAxiom, OWLObjectPropertyAssertionAxiom, OWLObjectPropertyDomainAxiom, \
-    OWLObjectPropertyRangeAxiom, OWLDataPropertyDomainAxiom
-from owlapy.owl_individual import OWLNamedIndividual
-from owlapy.owl_literal import BooleanOWLDatatype, IntegerOWLDatatype, DoubleOWLDatatype, OWLLiteral
-from owlapy.owl_property import OWLDataProperty, OWLObjectInverseOf, OWLObjectProperty
-
-from ontolearn.concept_generator import ConceptGenerator
 from ontolearn.knowledge_base import KnowledgeBase
-
-
-class KnowledgeBaseTest(unittest.TestCase):
-
-    def setUp(self):
-        self.namespace = "http://dl-learner.org/mutagenesis#"
-        self.kb = KnowledgeBase(path="KGs/Mutagenesis/mutagenesis.owl")
-        self.onto = self.kb.ontology
-        self.mgr = self.onto.get_owl_ontology_manager()
-        self.generator = ConceptGenerator()
-        # Classes
-        self.atom = OWLClass(IRI.create(self.namespace, 'Atom'))
-        self.bond = OWLClass(IRI.create(self.namespace, 'Bond'))
-        self.compound = OWLClass(IRI(self.namespace, 'Compound'))
-        self.ring_structure = OWLClass(IRI(self.namespace, 'RingStructure'))
-        self.bond1 = OWLClass(IRI.create(self.namespace, 'Bond-1'))
-        self.bond2 = OWLClass(IRI.create(self.namespace, 'Bond-2'))
-        self.bond3 = OWLClass(IRI.create(self.namespace, 'Bond-3'))
-        self.bond4 = OWLClass(IRI.create(self.namespace, 'Bond-4'))
-        self.bond5 = OWLClass(IRI.create(self.namespace, 'Bond-5'))
-        self.bond7 = OWLClass(IRI.create(self.namespace, 'Bond-7'))
-
-        self.anthracene = OWLClass(IRI.create(self.namespace, 'Anthracene'))
-        self.ball3 = OWLClass(IRI.create(self.namespace, 'Ball3'))
-        self.benzene = OWLClass(IRI.create(self.namespace, 'Benzene'))
-        self.carbon_5_aromatic_ring = OWLClass(IRI.create(self.namespace, 'Carbon_5_aromatic_ring'))
-        self.carbon_6_ring = OWLClass(IRI.create(self.namespace, 'Carbon_6_ring'))
-        self.hetero_aromatic_5_ring = OWLClass(IRI.create(self.namespace, 'Hetero_aromatic_5_ring'))
-        self.hetero_aromatic_6_ring = OWLClass(IRI.create(self.namespace, 'Hetero_aromatic_6_ring'))
-        self.methyl = OWLClass(IRI.create(self.namespace, 'Methyl'))
-        self.nitro = OWLClass(IRI.create(self.namespace, 'Nitro'))
-        self.phenanthrene = OWLClass(IRI.create(self.namespace, 'Phenanthrene'))
-        self.ring_size_5 = OWLClass(IRI.create(self.namespace, 'Ring_size_5'))
-        self.ring_size_6 = OWLClass(IRI.create(self.namespace, 'Ring_size_6'))
-
-        # Object Properties
-        self.in_bond = OWLObjectProperty(IRI.create(self.namespace, 'inBond'))
-        self.has_bond = OWLObjectProperty(IRI.create(self.namespace, 'hasBond'))
-        self.has_atom = OWLObjectProperty(IRI.create(self.namespace, 'hasAtom'))
-        self.in_structure = OWLObjectProperty(IRI.create(self.namespace, 'inStructure'))
-        self.has_structure = OWLObjectProperty(IRI.create(self.namespace, 'hasStructure'))
-        self.object_properties = {self.in_bond, self.has_bond, self.has_atom, self.in_structure, self.has_structure}
-
-        # Data Properties
-        self.charge = OWLDataProperty(IRI.create(self.namespace, 'charge'))
-        self.act = OWLDataProperty(IRI.create(self.namespace, 'act'))
-        self.logp = OWLDataProperty(IRI.create(self.namespace, 'logp'))
-        self.lumo = OWLDataProperty(IRI.create(self.namespace, 'lumo'))
-        self.has_fife_examples = OWLDataProperty(IRI.create(self.namespace, 'hasFifeExamplesOfAcenthrylenes'))
-        self.has_three = OWLDataProperty(IRI.create(self.namespace, 'hasThreeOrMoreFusedRings'))
-        self.data_properties = {self.charge, self.act, self.logp, self.lumo, self.has_fife_examples,
-                                self.has_three}
-        self.boolean_data_properties = {self.has_fife_examples, self.has_three}
-        self.numeric_data_properties = {self.charge, self.act, self.logp, self.lumo}
-
-        # Individuals
-        self.bond5225 = OWLNamedIndividual(IRI.create(self.namespace, 'bond5225'))
-        self.d91_17 = OWLNamedIndividual(IRI.create(self.namespace, 'd91_17'))
-        self.d91_32 = OWLNamedIndividual(IRI.create(self.namespace, 'd91_32'))
-
-    def test_classes_retrieval(self):
-        # get concepts
-        self.assertEqual(86, len(list(self.kb.get_concepts())))
-
-        # direct sub concepts
-        classes = frozenset(self.kb.get_direct_sub_concepts(OWLThing))
-        true_classes = {self.atom, self.bond, self.compound, self.ring_structure}
-        self.assertEqual(true_classes, classes)
-
-        # all sub concepts
-        classes = frozenset(self.kb.get_all_sub_concepts(self.bond))
-        true_classes = {self.bond1, self.bond2, self.bond3, self.bond4, self.bond5, self.bond7}
-        self.assertEqual(true_classes, classes)
-
-        # all leaf concepts
-        classes = frozenset(self.kb.get_leaf_concepts(self.bond))
-        self.assertEqual(true_classes, classes)
-
-        # get direct parents
-        classes = frozenset(self.kb.get_direct_parents(self.bond1))
-        true_classes = {self.bond}
-        self.assertEqual(true_classes, classes)
-
-        # types of an individual
-        classes = frozenset(self.kb.get_types(self.bond5225, direct=True))
-        true_classes = {self.bond1}
-        self.assertEqual(true_classes, classes)
-
-        classes = frozenset(self.kb.get_types(self.bond5225))
-        true_classes = {self.bond, self.bond1, OWLThing}
-        self.assertEqual(true_classes, classes)
-
-        # direct sub-concepts
-        classes = frozenset(self.kb.get_all_direct_sub_concepts(self.ring_structure))
-        true_classes = {self.anthracene, self.ball3, self.benzene, self.carbon_5_aromatic_ring, self.carbon_6_ring,
-                        self.hetero_aromatic_5_ring, self.hetero_aromatic_6_ring, self.methyl, self.nitro,
-                        self.phenanthrene, self.ring_size_5, self.ring_size_6}
-        self.assertEqual(true_classes, classes)
-
-    def test_property_retrieval(self):
-        self.assertEqual(self.object_properties, frozenset(self.kb.get_object_properties()))
-        self.assertEqual(self.data_properties, frozenset(self.kb.get_data_properties()))
-        self.assertEqual(self.boolean_data_properties, frozenset(self.kb.get_boolean_data_properties()))
-        self.assertEqual(self.numeric_data_properties, frozenset(self.kb.get_numeric_data_properties()))
-        self.assertFalse(frozenset(self.kb.get_time_data_properties()))
-
-        # most general data properties
-        self.assertEqual(self.boolean_data_properties,
-                         frozenset(self.kb.most_general_boolean_data_properties(domain=self.compound)))
-        self.assertFalse(frozenset(self.kb.most_general_boolean_data_properties(domain=self.bond)))
-
-        self.assertEqual({self.charge},
-                         frozenset(self.kb.most_general_numeric_data_properties(domain=self.atom)))
-        self.assertFalse(frozenset(self.kb.most_general_numeric_data_properties(domain=self.bond)))
-
-        self.data_properties.remove(self.charge)
-        self.assertEqual(self.data_properties,
-                         frozenset(self.kb.most_general_data_properties(domain=self.compound)))
-        self.assertFalse(frozenset(self.kb.most_general_data_properties(domain=self.bond)))
-
-        self.assertFalse(frozenset(self.kb.most_general_time_data_properties(domain=OWLThing)))
-
-        # object property values of an individual
-        inds = frozenset(self.kb.get_object_property_values(self.bond5225, self.in_bond, direct=True))
-        true_inds = {self.d91_32, self.d91_17}
-        self.assertEqual(true_inds, inds)
-
-        # indirect object property values of an individual
-        super_in_bond = OWLObjectProperty(IRI.create(self.namespace, 'super_inBond'))
-        self.onto.add_axiom(OWLSubObjectPropertyOfAxiom(self.in_bond, super_in_bond))
-        inds = frozenset(self.kb.get_object_property_values(self.bond5225, super_in_bond, direct=False))
-        true_inds = {self.d91_32, self.d91_17}
-        self.assertEqual(true_inds, inds)
-        inds = frozenset(self.kb.get_object_property_values(self.bond5225, super_in_bond, direct=True))
-        self.assertEqual(frozenset(), inds)
-        self.onto.remove_axiom(OWLSubObjectPropertyOfAxiom(self.in_bond, super_in_bond))
-
-        # data property values of an individual
-        values = frozenset(self.kb.get_data_property_values(self.d91_32, self.charge, direct=True))
-        true_values = {OWLLiteral(0.146)}
-        self.assertEqual(true_values, values)
-
-        # indirect data property values of an individual
-        super_charge = OWLDataProperty(IRI.create(self.namespace, 'super_charge'))
-        self.onto.add_axiom(OWLSubDataPropertyOfAxiom(self.charge, super_charge))
-        values = frozenset(self.kb.get_data_property_values(self.d91_32, super_charge, direct=False))
-        true_values = {OWLLiteral(0.146)}
-        self.assertEqual(true_values, values)
-        values = frozenset(self.kb.get_data_property_values(self.d91_32, super_charge, direct=True))
-        self.assertEqual(frozenset(), values)
-        self.onto.remove_axiom(OWLSubDataPropertyOfAxiom(self.charge, super_charge))
-
-        # object properties of an individual
-        properties = frozenset(self.kb.get_object_properties_for_ind(self.bond5225, direct=True))
-        true_properties = {self.in_bond}
-        self.assertEqual(true_properties, properties)
-
-        # indirect object properties of an individual
-        self.onto.add_axiom(OWLSubObjectPropertyOfAxiom(self.in_bond, self.has_bond))
-        properties = frozenset(self.kb.get_object_properties_for_ind(self.bond5225, direct=False))
-        true_properties = {self.in_bond, self.has_bond}
-        self.assertEqual(true_properties, properties)
-        properties = frozenset(self.kb.get_object_properties_for_ind(self.bond5225, direct=True))
-        true_properties = {self.in_bond}
-        self.assertEqual(true_properties, properties)
-        self.onto.remove_axiom(OWLSubObjectPropertyOfAxiom(self.in_bond, self.has_bond))
-
-        # data properties of an individual
-        properties = frozenset(self.kb.get_data_properties_for_ind(self.d91_32, direct=True))
-        true_properties = {self.charge}
-        self.assertEqual(true_properties, properties)
-
-        # indirect data properties of an individual
-        self.onto.add_axiom(OWLSubDataPropertyOfAxiom(self.charge, self.act))
-        properties = frozenset(self.kb.get_data_properties_for_ind(self.d91_32, direct=False))
-        true_properties = {self.charge, self.act}
-        self.assertEqual(true_properties, properties)
-        properties = frozenset(self.kb.get_data_properties_for_ind(self.d91_32, direct=True))
-        true_properties = {self.charge}
-        self.assertEqual(true_properties, properties)
-        self.onto.remove_axiom(OWLSubDataPropertyOfAxiom(self.charge, self.act))
-
-    def test_ignore(self):
-        concepts_to_ignore = {self.bond1, self.compound}
-        object_properties_to_ignore = {self.in_bond, self.has_structure}
-        data_properties_to_ignore = {self.act, self.has_fife_examples}
-        self.kb.class_hierarchy = self.kb.class_hierarchy.restrict_and_copy(remove=concepts_to_ignore)
-        self.kb.object_property_hierarchy = (
-            self.kb.object_property_hierarchy.restrict_and_copy(remove=object_properties_to_ignore)
-        )
-        self.kb.data_property_hierarchy = (
-            self.kb.data_property_hierarchy.restrict_and_copy(remove=data_properties_to_ignore)
-        )
-
-        # get concepts
-        concepts = frozenset(self.kb.get_concepts())
-        self.assertEqual(84, len(concepts))
-        self.assertTrue(self.bond1 not in concepts)
-        self.assertTrue(self.compound not in concepts)
-
-        # direct sub concepts
-        classes = frozenset(self.kb.get_direct_sub_concepts(OWLThing))
-        true_classes = {self.atom, self.bond, self.ring_structure}
-        self.assertEqual(true_classes, classes)
-
-        # all sub concepts
-        classes = frozenset(self.kb.get_all_sub_concepts(self.bond))
-        true_classes = {self.bond2, self.bond3, self.bond4, self.bond5, self.bond7}
-        self.assertEqual(true_classes, classes)
-
-        # all leaf concepts
-        classes = frozenset(self.kb.get_leaf_concepts(self.bond))
-        self.assertEqual(true_classes, classes)
-
-        # types of an individual
-        classes = frozenset(self.kb.get_types(self.bond5225, direct=True))
-        self.assertFalse(classes)
-
-        classes = frozenset(self.kb.get_types(self.bond5225))
-        true_classes = {self.bond, OWLThing}
-        self.assertEqual(true_classes, classes)
-
-        # properties
-        object_properties = {self.has_bond, self.has_atom, self.in_structure}
-        self.assertEqual(object_properties, frozenset(self.kb.get_object_properties()))
-
-        data_properties = {self.charge, self.logp, self.lumo, self.has_three}
-        self.assertEqual(data_properties, frozenset(self.kb.get_data_properties()))
-
-        boolean_data_properties = {self.has_three}
-        self.assertEqual(boolean_data_properties, frozenset(self.kb.get_boolean_data_properties()))
-
-        numeric_data_properties = {self.charge, self.logp, self.lumo}
-        self.assertEqual(numeric_data_properties, frozenset(self.kb.get_numeric_data_properties()))
-
-        true_res = frozenset(map(OWLObjectSomeValuesFrom, object_properties, repeat(OWLThing)))
-        res = frozenset(self.kb.most_general_existential_restrictions(domain=OWLThing))
-        self.assertEqual(true_res, res)
-
-    def test_domain_range_retrieval(self):
-        # object properties
-        self.assertEqual(self.compound, self.kb.get_object_property_domains(self.has_atom))
-        self.assertEqual(self.bond, self.kb.get_object_property_domains(self.in_bond))
-
-        self.assertEqual(self.ring_structure, self.kb.get_object_property_ranges(self.in_structure))
-        self.assertEqual(OWLThing, self.kb.get_object_property_domains(self.in_structure))
-        self.assertEqual(self.atom, self.kb.get_object_property_ranges(self.in_bond))
-
-        # data properties
-        self.assertEqual(self.atom, self.kb.get_data_property_domains(self.charge))
-        self.assertEqual(self.compound, self.kb.get_data_property_domains(self.act))
-
-        self.assertEqual({DoubleOWLDatatype}, self.kb.get_data_property_ranges(self.charge))
-        self.assertEqual({BooleanOWLDatatype}, self.kb.get_data_property_ranges(self.has_fife_examples))
-
-    def test_concept_generation(self):
-        # negation from iterables
-        true_ces = {OWLObjectComplementOf(self.bond), self.atom}
-        ces = frozenset(self.generator.negation_from_iterables([self.bond, OWLObjectComplementOf(self.atom)]))
-        self.assertEqual(true_ces, ces)
-
-        # intersection from iterables
-        true_ces = {OWLObjectIntersectionOf([OWLThing, self.bond]), OWLObjectIntersectionOf([OWLThing, self.atom]),
-                    OWLObjectIntersectionOf([OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype), self.bond]),
-                    OWLObjectIntersectionOf([OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype), self.atom])}
-
-        iter1 = [OWLThing, OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype)]
-        iter2 = [self.bond, self.atom]
-        ces = frozenset(self.generator.intersect_from_iterables(iter1, iter2))
-        self.assertEqual(true_ces, ces)
-
-        # union from iterables
-        true_ces = {OWLObjectUnionOf([OWLThing, self.bond]), OWLObjectUnionOf([OWLThing, self.atom]),
-                    OWLObjectUnionOf([OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype), self.bond]),
-                    OWLObjectUnionOf([OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype), self.atom])}
-        iter1 = [OWLThing, OWLDataSomeValuesFrom(self.charge, IntegerOWLDatatype)]
-        iter2 = [self.bond, self.atom]
-        ces = frozenset(self.generator.union_from_iterables(iter1, iter2))
-        self.assertEqual(true_ces, ces)
-
-        # most general existential/universal restrictions
-        true_res = frozenset(map(OWLObjectSomeValuesFrom, self.object_properties, repeat(OWLThing)))
-        res = frozenset(self.kb.most_general_existential_restrictions(domain=OWLThing))
-        self.assertEqual(true_res, res)
-
-        true_res = {OWLObjectAllValuesFrom(filler=OWLThing, property=self.in_bond),
-                    OWLObjectAllValuesFrom(filler=OWLThing, property=self.in_structure)}
-        res = frozenset(self.kb.most_general_universal_restrictions(domain=self.bond))
-        self.assertEqual(true_res, res)
-
-        true_res = {OWLObjectSomeValuesFrom(filler=OWLThing, property=self.has_bond.get_inverse_property()),
-                    OWLObjectSomeValuesFrom(filler=OWLThing, property=self.has_structure.get_inverse_property())}
-        res = frozenset(self.kb.most_general_existential_restrictions_inverse(domain=self.bond))
-        self.assertEqual(true_res, res)
-
-        true_res = frozenset(map(OWLObjectAllValuesFrom,
-                                 map(OWLObjectInverseOf, self.object_properties), repeat(OWLThing)))
-        res = frozenset(self.kb.most_general_universal_restrictions_inverse(domain=OWLThing))
-        self.assertEqual(true_res, res)
-
-        # general functions for concept building
-        ce = self.generator.intersection([self.atom, self.compound, OWLObjectAllValuesFrom(self.in_bond, self.atom)])
-        true_ce = OWLObjectIntersectionOf([self.atom, self.compound, OWLObjectAllValuesFrom(self.in_bond, self.atom)])
-        self.assertEqual(true_res, res)
-
-        ce = self.generator.union([self.atom, self.compound, OWLObjectAllValuesFrom(self.in_bond, self.atom)])
-        true_ce = OWLObjectUnionOf([self.atom, self.compound, OWLObjectAllValuesFrom(self.in_bond, self.atom)])
-        self.assertEqual(true_res, res)
-
-        ce = self.generator.existential_restriction(self.atom, self.has_atom)
-        true_ce = OWLObjectSomeValuesFrom(self.has_atom, self.atom)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.universal_restriction(self.atom, self.has_atom)
-        true_ce = OWLObjectAllValuesFrom(self.has_atom, self.atom)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.has_value_restriction(self.bond5225, self.has_atom)
-        true_ce = OWLObjectHasValue(self.has_atom, self.bond5225)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.min_cardinality_restriction(self.atom, self.has_atom, 5)
-        true_ce = OWLObjectMinCardinality(5, self.has_atom, self.atom)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.max_cardinality_restriction(self.atom, self.has_atom, 20)
-        true_ce = OWLObjectMaxCardinality(20, self.has_atom, self.atom)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.exact_cardinality_restriction(self.atom, self.has_atom, 4)
-        true_ce = OWLObjectExactCardinality(4, self.has_atom, self.atom)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.data_existential_restriction(DoubleOWLDatatype, self.act)
-        true_ce = OWLDataSomeValuesFrom(self.act, DoubleOWLDatatype)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.data_universal_restriction(BooleanOWLDatatype, self.has_fife_examples)
-        true_ce = OWLDataAllValuesFrom(self.has_fife_examples, BooleanOWLDatatype)
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.data_has_value_restriction(OWLLiteral(True), self.has_three)
-        true_ce = OWLDataHasValue(self.has_three, OWLLiteral(True))
-        self.assertEqual(true_ce, ce)
-
-        ce = self.generator.negation(OWLThing)
-        self.assertEqual(OWLNothing, ce)
-
-        ce = self.generator.negation(OWLDataHasValue(self.has_three, OWLLiteral(True)))
-        self.assertEqual(OWLObjectComplementOf(OWLDataHasValue(self.has_three, OWLLiteral(True))), ce)
-
-    def test_repr(self):
-        representation = repr(self.kb)
-        self.assertEqual("KnowledgeBase(path='KGs/Mutagenesis/mutagenesis.owl' <86 classes, 11 properties,"
-                         " 14145 individuals)", representation)
-
-    def test_tbox_abox(self):
-        """
-
-
-        kb = KnowledgeBase(path="KGs/Test/test_ontology.owl")
-        ind1 = OWLNamedIndividual(
-            IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#b'))
-        ind2 = OWLNamedIndividual(
-            IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#a'))
-        dp = OWLDataProperty(
-            IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#dp2'))
-        op1 = OWLObjectProperty(
-            IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1'))
-        op2 = OWLObjectProperty(
-            IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2'))
-        cls1 = OWLClass(IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#N'))
-        cls2 = OWLClass(IRI.create('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#O'))
-
-        ind1_1 = OWLClassAssertionAxiom(individual=OWLNamedIndividual(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'b')),
-            class_expression=OWLClass(
-                IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'B')))
-        ind1_2 = OWLObjectPropertyAssertionAxiom(
-            subject=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'b')),
-            property_=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r7')),
-            object_=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'f')))
-
-        ind2_1 = OWLClassAssertionAxiom(individual=OWLNamedIndividual(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'a')),
-                               class_expression=OWLClass(
-                                   IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#',
-                                       'A')))
-        ind2_2 = OWLClassAssertionAxiom(individual=OWLNamedIndividual(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'a')),
-                               class_expression=OWLClass(
-                                   IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#',
-                                       'B')))
-
-        results = set(kb.abox(ind1, 'axiom'))
-        self.assertEqual(results, {ind1_1, ind1_2})
-        results = set(kb.abox([ind1, ind2], 'axiom'))
-        self.assertEqual(results, {ind1_1, ind1_2, ind2_1, ind2_2})
-
-        ind1_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#b',
-                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#B')
-        ind1_2 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#b',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r7',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#f')
-
-        ind2_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#a',
-                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#A')
-        ind2_2 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#a',
-                  'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#B')
-
-        results = set(kb.abox(ind1, 'iri'))
-        self.assertEqual(results, {ind1_1, ind1_2})
-        results = set(kb.abox([ind1, ind2], 'iri'))
-        self.assertEqual(results, {ind1_1, ind1_2, ind2_1, ind2_1, ind2_2})
-
-        ind1_1 = (OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'b')),
-                  IRI('http://www.w3.org/1999/02/22-rdf-syntax-ns#','type'),
-                  OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')))
-        ind1_2 = (OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'b')),
-                  OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r7')),
-                  OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'f')))
-
-        ind2_1 = (OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'a')),
-                  IRI('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'type'),
-                  OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'B')))
-        ind2_2 = (OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'a')),
-                  IRI('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'type'),
-                  OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'A')))
-
-        results = set(kb.abox(ind1, 'native'))
-        self.assertEqual(results, {ind1_1, ind1_2})
-        results = set(kb.abox([ind1, ind2], 'native'))
-        self.assertEqual(results, {ind1_1, ind1_2, ind2_1, ind2_2, ind2_2})
-
-        r1 = set(kb.abox(mode="axiom"))
-        r2 = set(kb.abox(mode="iri"))
-        r3 = set(kb.abox(mode="native"))
-        self.assertEqual(len(r1), len(r2))
-        self.assertEqual(len(r1), len(r3))
-
-        cls1_1 = OWLEquivalentClassesAxiom(
-            [OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'N')),
-             OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'Q'))])
-        cls2_1 = OWLSubClassOfAxiom(
-            sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'O')),
-            super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'P')))
-
-        results = set(kb.tbox(cls1, 'axiom'))
-        self.assertEqual(results, {cls1_1})
-        results = set(kb.tbox([cls1, cls2], 'axiom'))
-        self.assertEqual(results, {cls1_1, cls2_1})
-
-        cls1_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#N',
-                  'http://www.w3.org/2002/07/owl#equivalentClass',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#Q')
-        cls2_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#O',
-                  'http://www.w3.org/2000/01/rdf-schema#subClassOf',
-                  'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#P')
-
-        results = set(kb.tbox(cls1, 'iri'))
-        self.assertEqual(results, {cls1_1})
-        results = set(kb.tbox([cls1, cls2], 'iri'))
-        self.assertEqual(results, {cls1_1, cls2_1})
-
-        cls1_1 = (OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'N')),
-                  IRI('http://www.w3.org/2002/07/owl#', 'equivalentClass'),
-                  OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'Q')))
-        cls2_1 = (OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'O')),
-                  IRI('http://www.w3.org/2000/01/rdf-schema#', 'subClassOf'),
-                  OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'P')))
-
-        results = set(kb.tbox(cls1, 'native'))
-        self.assertEqual(results, {cls1_1})
-        results = set(kb.tbox([cls1, cls2], 'native'))
-        self.assertEqual(results, {cls1_1, cls2_1})
-
-        op1_1 = OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-                                            super_property=OWLObjectProperty(
-                                                IRI('http://www.w3.org/2002/07/owl#', 'ObjectProperty')),
-                                            annotations=[])
-        op1_2 = OWLObjectPropertyDomainAxiom(
-            OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-            OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')), [])
-        op1_3 = OWLObjectPropertyRangeAxiom(
-            OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-            OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'G')), [])
-        op1_4 = OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                                            super_property=OWLObjectProperty(
-                                                IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#',
-                                                    'r1')), annotations=[])
-
-        op2_1 = OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                                            super_property=OWLObjectProperty(
-                                                IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#',
-                                                    'r1')), annotations=[])
-        op2_2 = OWLObjectPropertyRangeAxiom(
-            OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-            OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'G')), [])
-        op2_3 = OWLObjectPropertyDomainAxiom(
-            OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-            OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')), [])
-        op2_4 = OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                                            super_property=OWLObjectProperty(
-                                                IRI('http://www.w3.org/2002/07/owl#', 'ObjectProperty')),
-                                            annotations=[])
-
-        results = set(kb.tbox(op1, 'axiom'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4})
-        results = set(kb.tbox(op2, 'axiom'))
-        self.assertEqual(results, {op2_1, op2_2, op2_3, op2_4})
-        results = set(kb.tbox([op1, op2], 'axiom'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4, op2_3, op2_2, op2_4})
-
-        op1_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2',
-                 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf',
-                 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1')
-        op1_2 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1',
-                 'http://www.w3.org/2000/01/rdf-schema#range',
-                 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#G')
-        op1_3 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1',
-                 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf', 'http://www.w3.org/2002/07/owl#ObjectProperty')
-        op1_4 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1',
-                 'http://www.w3.org/2000/01/rdf-schema#domain', 'http://www.w3.org/2002/07/owl#Thing')
-
-        op2_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2',
-                 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf', 'http://www.w3.org/2002/07/owl#ObjectProperty')
-        op2_2 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2',
-                 'http://www.w3.org/2000/01/rdf-schema#range',
-                 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#G')
-        op2_3 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2',
-                 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf',
-                 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r1')
-        op2_4 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#r2',
-                 'http://www.w3.org/2000/01/rdf-schema#domain', 'http://www.w3.org/2002/07/owl#Thing')
-
-        results = set(kb.tbox(op1, 'iri'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4})
-        results = set(kb.tbox(op2, 'iri'))
-        self.assertEqual(results, {op2_1, op2_2, op2_3, op2_4})
-        results = set(kb.tbox([op1, op2], 'iri'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4, op2_1, op2_2, op2_4})
-
-        op1_1 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'domain'),
-                 OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')))
-        op1_2 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'range'),
-                 OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'G')))
-        op1_3 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                 OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')))
-        op1_4 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                 OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#', 'ObjectProperty')))
-
-        op2_1 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'domain'),
-                 OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')))
-        op2_2 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                 OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#', 'ObjectProperty')))
-        op2_3 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                 OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r1')))
-        op2_4 = (OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'r2')),
-                 IRI('http://www.w3.org/2000/01/rdf-schema#', 'range'),
-                 OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'G')))
-
-        results = set(kb.tbox(op1, 'native'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4})
-        results = set(kb.tbox(op2, 'native'))
-        self.assertEqual(results, {op2_1, op2_2, op2_3, op2_4})
-        results = set(kb.tbox([op1, op2], 'native'))
-        self.assertEqual(results, {op1_1, op1_2, op1_3, op1_4, op2_1, op2_2, op2_4})
-
-        dp_1 = OWLSubDataPropertyOfAxiom(sub_property=OWLDataProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-                                         super_property=OWLDataProperty(
-                                             IRI('http://www.w3.org/2002/07/owl#', 'DatatypeProperty')), annotations=[])
-        dp_2 = OWLDataPropertyDomainAxiom(
-            OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-            OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')), [])
-        dp_3 = OWLSubDataPropertyOfAxiom(sub_property=OWLDataProperty(
-            IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-                                         super_property=OWLDataProperty(
-                                             IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#',
-                                                 'dp1')), annotations=[])
-
-        results = set(kb.tbox(dp, 'axiom'))
-        self.assertEqual(results, {dp_1, dp_2, dp_3})
-
-        dp_1 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#dp2',
-                'http://www.w3.org/2000/01/rdf-schema#subPropertyOf', 'http://www.w3.org/2002/07/owl#DatatypeProperty')
-        dp_2 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#dp2',
-                'http://www.w3.org/2000/01/rdf-schema#domain', 'http://www.w3.org/2002/07/owl#Thing')
-        dp_3 = ('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#dp2',
-                'http://www.w3.org/2000/01/rdf-schema#subPropertyOf',
-                'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#dp1')
-
-        results = set(kb.tbox(dp, 'iri'))
-        self.assertEqual(results, {dp_1, dp_2, dp_3})
-
-        dp_1 = (OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-                IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                OWLDataProperty(IRI('http://www.w3.org/2002/07/owl#', 'DatatypeProperty')))
-        dp_2 = (OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-                IRI('http://www.w3.org/2000/01/rdf-schema#', 'domain'),
-                OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')))
-        dp_3 = (OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp2')),
-                IRI('http://www.w3.org/2000/01/rdf-schema#', 'subPropertyOf'),
-                OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#', 'dp1')))
-
-        results = set(kb.tbox(dp, 'native'))
-        self.assertEqual(results, {dp_1, dp_2, dp_3})
-
-        r4 = set(kb.tbox(mode="axiom"))
-        r5 = set(kb.tbox(mode="iri"))
-        r6 = set(kb.tbox(mode="native"))
-        self.assertEqual(len(r4), len(r5))
-        self.assertEqual(len(r4), len(r6))
-
-        r7 = set(kb.triples(mode="axiom"))
-        r8 = set(kb.triples(mode="iri"))
-        r9 = set(kb.triples(mode="native"))
-        self.assertEqual(len(r7), len(r8))
-        self.assertEqual(len(r7), len(r9))
-
-        self.assertEqual(len(r7), len(r4) + len(r1))
-        self.assertEqual(len(r8), len(r5) + len(r2))
-        self.assertEqual(len(r9), len(r6) + len(r3))
-        """
\ No newline at end of file
+import argparse
+
+class TestKnowledgeBase:
+    def test_reading_data(self):
+        kb = KnowledgeBase(path="KGs/Family/family-benchmark_rich_background.owl")
+        # All concepts.
+        for i in kb.get_concepts():
+            print(i)
+        print('*' * 100)
+        # All object properties.
+        for i in kb.get_object_properties():
+            print(i)
+        print('*' * 100)
+        # All individuals.
+        for i in kb.all_individuals_set():
+            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():
+            print(i.str)
+        print('*' * 100)
+        # Direct concept hierarchy from Top to Bottom.
+        for concept in kb.class_hierarchy.items():
+            print(f'{concept.str} => {[c.str for c in kb.get_direct_sub_concepts(concept)]}')
diff --git a/tests/test_learners_regression.py b/tests/test_learners_regression.py
index a3d424e6..3cd83db5 100644
--- a/tests/test_learners_regression.py
+++ b/tests/test_learners_regression.py
@@ -4,13 +4,13 @@
 from owlapy.iri import IRI
 
 from ontolearn.knowledge_base import KnowledgeBase
-from ontolearn.concept_learner import EvoLearner, CELOE, OCEL
-from ontolearn.learners import Drill
+from ontolearn.concept_learner import EvoLearner
+from ontolearn.learners import CELOE, OCEL, Drill
 from ontolearn.metrics import F1
 from ontolearn.utils.static_funcs import compute_f1_score
 
 
-class TestConceptLearnerReg:
+class ConceptLearnerReg:
 
     def test_regression_family(self):
         with open('examples/synthetic_problems.json') as json_file:
diff --git a/tests/test_nces.py b/tests/test_nces.py
index 6fd8c2cc..053645ad 100644
--- a/tests/test_nces.py
+++ b/tests/test_nces.py
@@ -11,8 +11,10 @@
 import numpy as np
 
 import warnings
+
 warnings.filterwarnings("ignore")
 
+
 def seed_everything():
     seed = 42
     os.environ['PYTHONHASHSEED'] = str(seed)
@@ -27,43 +29,51 @@ def seed_everything():
         torch.backends.cudnn.benchmark = False
     print('-----Seed Set!-----')
 
+
 seed_everything()
 
+
 class TestNCES(unittest.TestCase):
 
     def test_prediction_quality_family(self):
-        nces = NCES(knowledge_base_path="./NCESData/family/family.owl", quality_func=F1(), num_predictions=100,
-                    path_of_embeddings="./NCESData/family/embeddings/ConEx_entity_embeddings.csv",
-                    learner_names=["LSTM", "GRU", "SetTransformer"])
-        KB = KnowledgeBase(path=nces.knowledge_base_path)
-        dl_parser = DLSyntaxParser(nces.kb_namespace)
-        brother = dl_parser.parse('Brother')
-        daughter = dl_parser.parse('Daughter')
-        pos = set(KB.individuals(brother)).union(set(KB.individuals(daughter)))
-        neg = set(KB.individuals())-set(pos)
-        learning_problem = PosNegLPStandard(pos=pos, neg=neg)
-        node = list(nces.fit(learning_problem).best_predictions)[0]
-        print("Quality:", node.quality)
-        assert node.quality > 0.95
+        knowledge_base_path="./NCESData/family/family.owl"
+        path_of_embeddings="./NCESData/family/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            nces = NCES(knowledge_base_path=knowledge_base_path, quality_func=F1(), num_predictions=100,
+                        path_of_embeddings=path_of_embeddings,
+                        learner_names=["LSTM", "GRU", "SetTransformer"])
+            KB = KnowledgeBase(path=nces.knowledge_base_path)
+            dl_parser = DLSyntaxParser(nces.kb_namespace)
+            brother = dl_parser.parse('Brother')
+            daughter = dl_parser.parse('Daughter')
+            pos = set(KB.individuals(brother)).union(set(KB.individuals(daughter)))
+            neg = set(KB.individuals())-set(pos)
+            learning_problem = PosNegLPStandard(pos=pos, neg=neg)
+            node = list(nces.fit(learning_problem).best_predictions)[0]
+            print("Quality:", node.quality)
+            assert node.quality > 0.95
 
     def test_prediction_quality_mutagenesis(self):
-        nces = NCES(knowledge_base_path="./NCESData/mutagenesis/mutagenesis.owl", quality_func=F1(), num_predictions=100,
-                    path_of_embeddings="./NCESData/mutagenesis/embeddings/ConEx_entity_embeddings.csv",
-                    learner_names=["LSTM", "GRU", "SetTransformer"])
-        KB = KnowledgeBase(path=nces.knowledge_base_path)
-        dl_parser = DLSyntaxParser(nces.kb_namespace)
-        exists_inbond = dl_parser.parse('∃ hasStructure.Benzene')
-        not_bond7 = dl_parser.parse('¬Bond-7')
-        pos = set(KB.individuals(exists_inbond)).intersection(set(KB.individuals(not_bond7)))
-        neg = sorted(set(KB.individuals())-pos)
-        if len(pos) > 500:
-            pos = set(np.random.choice(list(pos), size=min(500, len(pos)), replace=False))
-        neg = set(neg[:min(1000-len(pos), len(neg))])
-        learning_problem = PosNegLPStandard(pos=pos, neg=neg)
-        node = list(nces.fit(learning_problem).best_predictions)[0]
-        print("Quality:", node.quality)
-        assert node.quality > 0.95
-        
+        knowledge_base_path="./NCESData/mutagenesis/mutagenesis.owl"
+        path_of_embeddings="./NCESData/mutagenesis/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            nces = NCES(knowledge_base_path=knowledge_base_path, quality_func=F1(), num_predictions=100,
+                        path_of_embeddings=path_of_embeddings,
+                        learner_names=["LSTM", "GRU", "SetTransformer"])
+            KB = KnowledgeBase(path=nces.knowledge_base_path)
+            dl_parser = DLSyntaxParser(nces.kb_namespace)
+            exists_inbond = dl_parser.parse('∃ hasStructure.Benzene')
+            not_bond7 = dl_parser.parse('¬Bond-7')
+            pos = set(KB.individuals(exists_inbond)).intersection(set(KB.individuals(not_bond7)))
+            neg = sorted(set(KB.individuals()) - pos)
+            if len(pos) > 500:
+                pos = set(np.random.choice(list(pos), size=min(500, len(pos)), replace=False))
+            neg = set(neg[:min(1000 - len(pos), len(neg))])
+            learning_problem = PosNegLPStandard(pos=pos, neg=neg)
+            node = list(nces.fit(learning_problem).best_predictions)[0]
+            print("Quality:", node.quality)
+            assert node.quality > 0.95
+
 if __name__ == "__main__":
     test = TestNCES()
     test.test_prediction_quality_family()
diff --git a/tests/test_nces_trainer.py b/tests/test_nces_trainer.py
index c3a35dd4..23661a8a 100644
--- a/tests/test_nces_trainer.py
+++ b/tests/test_nces_trainer.py
@@ -28,12 +28,15 @@ def seed_everything():
 class TestNCESTrainer(unittest.TestCase):
 
     def test_trainer_family(self):
-        nces = NCES(knowledge_base_path="./NCESData/family/family.owl", num_predictions=100,
-                    path_of_embeddings="./NCESData/family/embeddings/ConEx_entity_embeddings.csv",
-                    load_pretrained=False)
-        with open("./NCESData/family/training_data/Data.json") as f:
-            data = json.load(f)
-        nces.train(list(data.items())[-100:], epochs=5, learning_rate=0.001, save_model=False, record_runtime=False, storage_path=f"./NCES-{time.time()}/")
+        knowledge_base_path="./NCESData/family/family.owl"
+        path_of_embeddings="./NCESData/family/embeddings/ConEx_entity_embeddings.csv"
+        if os.path.exists(knowledge_base_path) and os.path.exists(path_of_embeddings):
+            nces = NCES(knowledge_base_path=knowledge_base_path, num_predictions=100,
+                        path_of_embeddings=path_of_embeddings,
+                        load_pretrained=False)
+            with open("./NCESData/family/training_data/Data.json") as f:
+                data = json.load(f)
+            nces.train(list(data.items())[-100:], epochs=5, learning_rate=0.001, save_model=False, record_runtime=False, storage_path=f"./NCES-{time.time()}/")
 if __name__ == "__main__":
     test = TestNCESTrainer()
     test.test_trainer_family()
diff --git a/tests/test_owl_neural_retrieval.py b/tests/test_owl_neural_retrieval.py
index c61564e4..390fee0c 100644
--- a/tests/test_owl_neural_retrieval.py
+++ b/tests/test_owl_neural_retrieval.py
@@ -40,52 +40,33 @@
 from itertools import chain
 
 
-class Test_Neural_Retrieval:
+class TestNeuralRetrieval:
+    def __init__(self):
+        self.neural_owl_reasoner = TripleStoreNeuralReasoner(path_of_kb="KGs/Family/father.owl", gamma=0.8)
 
     def test_retrieval_single_individual_father_owl(self):
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/father.owl", gamma=0.8
-        )
-        triples_about_anna = {
-            (s, p, o)
-            for s, p, o in neural_owl_reasoner.abox("http://example.com/father#anna")
-        }
+        triples_about_anna = {(s, p, o) for s, p, o in self.neural_owl_reasoner.abox("http://example.com/father#anna")}
 
         sanity_checking = {
-            (
-                OWLNamedIndividual("http://example.com/father#anna"),
-                OWLObjectProperty("http://example.com/father#hasChild"),
-                OWLNamedIndividual("http://example.com/father#heinz"),
-            ),
-            (
-                OWLNamedIndividual("http://example.com/father#anna"),
-                OWLProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
-                OWLClass("http://example.com/father#female"),
-            ),
-            (
-                OWLNamedIndividual("http://example.com/father#anna"),
-                OWLProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
-                OWLClass("http://www.w3.org/2002/07/owl#NamedIndividual"),
-            ),
-        }
+            (OWLNamedIndividual("http://example.com/father#anna"),
+             OWLObjectProperty("http://example.com/father#hasChild"),
+             OWLNamedIndividual("http://example.com/father#heinz")),
+            (OWLNamedIndividual("http://example.com/father#anna"),
+             OWLProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
+             OWLClass("http://example.com/father#female")),
+            (OWLNamedIndividual("http://example.com/father#anna"),
+             OWLProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
+             OWLClass("http://www.w3.org/2002/07/owl#NamedIndividual"))}
 
         assert triples_about_anna == sanity_checking
 
     def test_retrieval_named_concepts_family(self):
-        symbolic_kb = KnowledgeBase(
-            path="KGs/Family/family-benchmark_rich_background.owl"
-        )
-        benchmark_dataset = [
-            (
-                concept,
-                {individual.str for individual in symbolic_kb.individuals(concept)},
-            )
-            for concept in symbolic_kb.get_concepts()
-        ]
+        symbolic_kb = KnowledgeBase(path="KGs/Family/family-benchmark_rich_background.owl")
+        benchmark_dataset = [(concept,{individual.str for individual in symbolic_kb.individuals(concept)})
+                             for concept in symbolic_kb.get_concepts()]
 
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/family-benchmark_rich_background.owl", gamma=0.1
-        )
+        neural_owl_reasoner = TripleStoreNeuralReasoner(path_of_kb="KGs/Family/family-benchmark_rich_background.owl",
+                                                        gamma=0.1)
 
         avg_jaccard_index = 0
         for concept, symbolic_retrieval in benchmark_dataset:
@@ -97,10 +78,6 @@ def test_retrieval_named_concepts_family(self):
         assert avg_jaccard_index / len(benchmark_dataset) == 1.0
 
     def test_de_morgan_male_and_father_father(self):
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/father.owl", gamma=0.8
-        )
-
         male_and_father = OWLObjectIntersectionOf(
             [
                 OWLClass("http://example.com/father#male"),
@@ -109,11 +86,8 @@ def test_de_morgan_male_and_father_father(self):
                     property=OWLObjectProperty("http://example.com/father#hasChild"),
                     filler=OWLObjectOneOf(
                         OWLNamedIndividual("http://example.com/father#anna")
-                    ),
-                ),
-            ]
-        )
-        individuals = set(neural_owl_reasoner.instances(male_and_father))
+                    ))])
+        individuals = set(self.neural_owl_reasoner.instances(male_and_father))
         not_female_or_not_mother = OWLObjectComplementOf(
             OWLObjectUnionOf(
                 [
@@ -133,13 +107,11 @@ def test_de_morgan_male_and_father_father(self):
             )
         )
         print(individuals)
-        individuals_2 = set(neural_owl_reasoner.instances(not_female_or_not_mother))
+        individuals_2 = set(self.neural_owl_reasoner.instances(not_female_or_not_mother))
         assert individuals == individuals_2
 
     def test_de_morgan_male_and_has_daughter_family(self):
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/family-benchmark_rich_background.owl", gamma=0.8
-        )
+
         prefix = "http://www.benchmark.org/family#"
         male_and_has_daughter = OWLObjectIntersectionOf(
             [
@@ -151,7 +123,7 @@ def test_de_morgan_male_and_has_daughter_family(self):
                 ),
             ]
         )
-        individuals = set(neural_owl_reasoner.instances(male_and_has_daughter))
+        individuals = set(self.neural_owl_reasoner.instances(male_and_has_daughter))
         not_male_or_not_has_daughter = OWLObjectComplementOf(
             OWLObjectUnionOf(
                 [
@@ -166,13 +138,10 @@ def test_de_morgan_male_and_has_daughter_family(self):
                 ]
             )
         )
-        individuals_2 = set(neural_owl_reasoner.instances(not_male_or_not_has_daughter))
+        individuals_2 = set(self.neural_owl_reasoner.instances(not_male_or_not_has_daughter))
         assert individuals == individuals_2
 
     def test_de_morgan_not_father_or_brother_family(self):
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/family-benchmark_rich_background.owl", gamma=0.8
-        )
         prefix = "http://www.benchmark.org/family#"
         not_father_or_brother = OWLObjectComplementOf(
             OWLObjectUnionOf(
@@ -182,14 +151,14 @@ def test_de_morgan_not_father_or_brother_family(self):
                 ]
             )
         )
-        individuals = set(neural_owl_reasoner.instances(not_father_or_brother))
+        individuals = set(self.neural_owl_reasoner.instances(not_father_or_brother))
         father_and_brother = OWLObjectIntersectionOf(
             [
                 OWLObjectComplementOf(OWLClass(prefix + "Father")),
                 OWLObjectComplementOf(OWLClass(prefix + "Brother")),
             ]
         )
-        individuals_2 = set(neural_owl_reasoner.instances(father_and_brother))
+        individuals_2 = set(self.neural_owl_reasoner.instances(father_and_brother))
         assert individuals == individuals_2
 
     def test_complement_for_all_named_concepts_family(self):
@@ -216,53 +185,6 @@ def test_complement_for_all_named_concepts_family(self):
             neural_retrieval = {i.str for i in neural_owl_reasoner.instances(concept)}
             assert jaccard_similarity(symbolic_retrieval, neural_retrieval)
 
-    """
-    def test_for_all_family(self):
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/family-benchmark_rich_background.owl", gamma=0.1
-        )
-        symbolic_kb = KnowledgeBase(
-            path="KGs/Family/family-benchmark_rich_background.owl"
-        )
-        named_classes = symbolic_kb.get_concepts()  # Retrieve all named classes
-        properties = symbolic_kb.get_object_properties()  # Retrieve all properties
-
-        with open("match_log.txt", "w") as file:
-            file.write("Matches found in the neural reasoner evaluations:\n")
-
-            # Iterate over all properties and named classes
-            for property in properties:
-                for named_class in named_classes:
-                    # Creating the expressions for ¬∃r.¬C and ∀r.C using property.str
-                    neg_exist_neg = OWLObjectComplementOf(
-                        OWLObjectSomeValuesFrom(
-                            property=OWLObjectProperty(property.str),
-                            filler=OWLObjectComplementOf(named_class),
-                        )
-                    )
-                    all_values = OWLObjectAllValuesFrom(
-                        property=OWLObjectProperty(property.str), filler=named_class
-                    )
-
-                    # Using the neural reasoner to get instances
-                    neural_neg_exist_neg = {
-                        i.str for i in neural_owl_reasoner.instances(neg_exist_neg)
-                    }
-                    neural_all_values = {
-                        i.str for i in neural_owl_reasoner.instances(all_values)
-                    }
-
-                    # Check for matches and write to file if equal
-                    if neural_neg_exist_neg == neural_all_values:
-                        file.write(
-                            f"Property: {property.str}, Class: {named_class.str}\n"
-                        )
-                        file.write(
-                            f"¬∃{property.str}.¬{named_class.str} and ∀{property.str}.{named_class.str} match\n"
-                        )
-                        file.write(f"Matching Instances: {neural_neg_exist_neg}\n\n")
-    """
-
     def test_retrieval_named_concepts_in_abox_family(self):
         symbolic_kb = KnowledgeBase(
             path="KGs/Family/family-benchmark_rich_background.owl"
@@ -443,195 +365,4 @@ def concept_retrieval(retriever_func, c) -> Tuple[Set[str], float]:
                          })
 
         df = pd.DataFrame(data)
-        assert df["Jaccard Similarity"].mean() == 1.0
-
-    def old_regression_concept_combinations_family(self):
-        # Helper functions
-
-        def concept_reducer(concepts, opt):
-            result = set()
-            for i in concepts:
-                for j in concepts:
-                    result.add(opt((i, j)))
-            return result
-
-        def concept_reducer_properties(concepts, opt, cardinality=2):
-            result = set()
-            for i in concepts:
-                for j in object_properties_and_inverse:
-                    if opt == OWLObjectMinCardinality or opt == OWLObjectMaxCardinality:
-                        result.add(opt(cardinality, j, i))
-                        continue
-                    result.add(opt(j, i))
-            return result
-
-        def concept_to_retrieval(concepts, retriever) -> List[Tuple[float, Set[str]]]:
-            results = []
-            for c in concepts:
-                start_time_ = time.time()
-                retrieval = {i.str for i in retriever.individuals(c)}
-                results.append((time.time() - start_time_, retrieval))
-            return results
-
-        def retrieval_eval(expressions, y, yhat, verbose=1):
-            assert len(y) == len(yhat)
-            similarities = []
-            runtime_diff = []
-            number_of_concepts = len(expressions)
-            for expressions, y_report_i, yhat_report_i in zip(expressions, y, yhat):
-                runtime_y_i, y_i = y_report_i
-                runtime_yhat_i, yhat_i = yhat_report_i
-
-                jaccard_sim = jaccard_similarity(y_i, yhat_i)
-                runtime_benefits = runtime_y_i - runtime_yhat_i
-                if verbose > 0:
-                    print(
-                        f"Concept:{expressions}\tTrue Size:{len(y_i)}\tPredicted Size:{len(yhat_i)}\tRetrieval Similarity:{jaccard_sim}\tRuntime Benefit:{runtime_benefits:.3f}"
-                    )
-                similarities.append(jaccard_sim)
-                runtime_diff.append(runtime_benefits)
-            avg_jaccard_sim = sum(similarities) / len(similarities)
-            avg_runtime_benefits = sum(runtime_diff) / len(runtime_diff)
-            return number_of_concepts, avg_jaccard_sim, avg_runtime_benefits
-
-        symbolic_kb = KnowledgeBase(
-            path="KGs/Family/family-benchmark_rich_background.owl"
-        )
-        # symbolic_kb = TripleStore(url="http://localhost:3030/family")
-        neural_owl_reasoner = TripleStoreNeuralReasoner(
-            path_of_kb="KGs/Family/family-benchmark_rich_background.owl", gamma=0.8
-        )
-        object_properties = {i for i in symbolic_kb.get_object_properties()}
-        object_properties_inverse = {
-            i.get_inverse_property() for i in object_properties
-        }
-        object_properties_and_inverse = object_properties.union(
-            object_properties_inverse
-        )
-        # named concepts
-        nc = {i for i in symbolic_kb.get_concepts()}
-        # negated named concepts
-        nnc = {i.get_object_complement_of() for i in nc}
-        # union of named and negated named concepts
-        unnc = nc.union(nnc)
-
-        unions = concept_reducer(nc, opt=OWLObjectUnionOf)
-        intersections = concept_reducer(nc, opt=OWLObjectIntersectionOf)
-        unions_unnc = concept_reducer(unnc, opt=OWLObjectUnionOf)
-        intersections_unnc = concept_reducer(unnc, opt=OWLObjectIntersectionOf)
-        exist_unnc = concept_reducer_properties(unnc, opt=OWLObjectSomeValuesFrom)
-        for_all_unnc = concept_reducer_properties(unnc, opt=OWLObjectAllValuesFrom)
-
-        min_cardinality_unnc_1, min_cardinality_unnc_2, min_cardinality_unnc_3 = (
-            concept_reducer_properties(unnc, opt=OWLObjectMinCardinality, cardinality=i)
-            for i in [1, 2, 3]
-        )
-        max_cardinality_unnc_1, max_cardinality_unnc_2, max_cardinality_unnc_3 = (
-            concept_reducer_properties(unnc, opt=OWLObjectMaxCardinality, cardinality=i)
-            for i in [1, 2, 3]
-        )
-
-        nc_retrieval_results = retrieval_eval(
-            expressions=nc,
-            y=concept_to_retrieval(nc, symbolic_kb),
-            yhat=concept_to_retrieval(nc, neural_owl_reasoner),
-        )
-        unions_nc_retrieval_results = retrieval_eval(
-            expressions=unions,
-            y=concept_to_retrieval(unions, symbolic_kb),
-            yhat=concept_to_retrieval(unions, neural_owl_reasoner),
-        )
-        intersections_nc_retrieval_results = retrieval_eval(
-            expressions=intersections,
-            y=concept_to_retrieval(intersections, symbolic_kb),
-            yhat=concept_to_retrieval(intersections, neural_owl_reasoner),
-        )
-        nnc_retrieval_results = retrieval_eval(
-            expressions=nnc,
-            y=concept_to_retrieval(nnc, symbolic_kb),
-            yhat=concept_to_retrieval(nnc, neural_owl_reasoner),
-        )
-        unnc_retrieval_results = retrieval_eval(
-            expressions=unnc,
-            y=concept_to_retrieval(unnc, symbolic_kb),
-            yhat=concept_to_retrieval(unnc, neural_owl_reasoner),
-        )
-        unions_unnc_retrieval_results = retrieval_eval(
-            expressions=unions_unnc,
-            y=concept_to_retrieval(unions_unnc, symbolic_kb),
-            yhat=concept_to_retrieval(unions_unnc, neural_owl_reasoner),
-        )
-        intersections_unnc_retrieval_results = retrieval_eval(
-            expressions=intersections_unnc,
-            y=concept_to_retrieval(intersections_unnc, symbolic_kb),
-            yhat=concept_to_retrieval(intersections_unnc, neural_owl_reasoner),
-        )
-        exist_unnc_retrieval_results = retrieval_eval(
-            expressions=exist_unnc,
-            y=concept_to_retrieval(exist_unnc, symbolic_kb),
-            yhat=concept_to_retrieval(exist_unnc, neural_owl_reasoner),
-        )
-        for_all_unnc_retrieval_results = retrieval_eval(
-            expressions=for_all_unnc,
-            y=concept_to_retrieval(for_all_unnc, symbolic_kb),
-            yhat=concept_to_retrieval(for_all_unnc, neural_owl_reasoner),
-        )
-
-        (
-            min_cardinality_unnc_1_retrieval_results,
-            min_cardinality_unnc_2_retrieval_results,
-            min_cardinality_unnc_3_retrieval_results,
-        ) = (
-            retrieval_eval(
-                expressions=expressions,
-                y=concept_to_retrieval(expressions, symbolic_kb),
-                yhat=concept_to_retrieval(expressions, neural_owl_reasoner),
-            )
-            for expressions in [
-            min_cardinality_unnc_1,
-            min_cardinality_unnc_2,
-            min_cardinality_unnc_3,
-        ]
-        )
-
-        (
-            max_cardinality_unnc_1_retrieval_results,
-            max_cardinality_unnc_2_retrieval_results,
-            max_cardinality_unnc_3_retrieval_results,
-        ) = (
-            retrieval_eval(
-                expressions=expressions,
-                y=concept_to_retrieval(expressions, symbolic_kb),
-                yhat=concept_to_retrieval(expressions, neural_owl_reasoner),
-            )
-            for expressions in [
-            max_cardinality_unnc_1,
-            max_cardinality_unnc_2,
-            max_cardinality_unnc_3,
-        ]
-        )
-
-        results = {
-            "nc_retrieval_results": nc_retrieval_results,
-            "unions_nc_retrieval_results": unions_nc_retrieval_results,
-            "intersections_nc_retrieval_results": intersections_nc_retrieval_results,
-            "nnc_retrieval_results": nnc_retrieval_results,
-            "unnc_retrieval_results": unnc_retrieval_results,
-            "unions_unnc_retrieval_results": unions_unnc_retrieval_results,
-            "intersections_unnc_retrieval_results": intersections_unnc_retrieval_results,
-            "exist_unnc_retrieval_results": exist_unnc_retrieval_results,
-            "for_all_unnc_retrieval_results": for_all_unnc_retrieval_results,
-            "min_cardinality_unnc_1_retrieval_results": min_cardinality_unnc_1_retrieval_results,
-            "min_cardinality_unnc_2_retrieval_results": min_cardinality_unnc_2_retrieval_results,
-            "min_cardinality_unnc_3_retrieval_results": min_cardinality_unnc_3_retrieval_results,
-            "max_cardinality_unnc_1_retrieval_results": max_cardinality_unnc_1_retrieval_results,
-            "max_cardinality_unnc_2_retrieval_results": max_cardinality_unnc_2_retrieval_results,
-            "max_cardinality_unnc_3_retrieval_results": max_cardinality_unnc_3_retrieval_results,
-        }
-
-        for key, value in results.items():
-            number_of_concepts, avg_jaccard_sim, avg_runtime_benefits = value
-            print(
-                f"Concepts:{number_of_concepts}\tAverage Jaccard Similarity:{avg_jaccard_sim}\tAverage Runtime Benefits:{avg_runtime_benefits}"
-            )
-            assert avg_jaccard_sim == 1.0
+        assert df["Jaccard Similarity"].mean() == 1.0
\ No newline at end of file
diff --git a/tests/test_refinement_operators.py b/tests/test_refinement_operators.py
index de492fc3..f4b8f36f 100644
--- a/tests/test_refinement_operators.py
+++ b/tests/test_refinement_operators.py
@@ -120,7 +120,8 @@ def test_atomic_refinements_union_intersection(self):
         true_refs = {sorter.sort(i) for i in true_refs}
         thing_refs = set(rho.refine(self.generator.thing, max_length=3, current_domain=self.generator.thing))
         thing_refs = {sorter.sort(i) for i in thing_refs}
-        self.assertLessEqual(true_refs, thing_refs)
+        assert len(true_refs)< len(thing_refs)
+
 
         # max_length = 2 so union or intersection refinements should not be generated
         for i in rho.refine(self.generator.thing, max_length=2, current_domain=self.generator.thing):
diff --git a/tests/test_retrieval_eval_examples.py b/tests/test_retrieval_eval_examples.py
new file mode 100644
index 00000000..68d67758
--- /dev/null
+++ b/tests/test_retrieval_eval_examples.py
@@ -0,0 +1,58 @@
+import os
+import unittest
+from examples.retrieval_eval import execute
+from examples.retrieval_eval_under_incomplete import execute as execute2
+import shutil
+
+
+def clear():
+    if os.path.exists("incomplete_father_0_1"):
+        shutil.rmtree("incomplete_father_0_1")
+    if os.path.exists("KGs_Family_father_owl"):
+        shutil.rmtree("KGs_Family_father_owl")
+    if os.path.exists("checkpoints"):
+        shutil.rmtree("checkpoints")
+
+
+class RetrievalTests(unittest.TestCase):
+
+    def test_retrieval_eval(self):
+        class ARGS:
+            def __init__(self):
+                self.path_kg = "KGs/Family/father.owl"
+                self.path_kge_model = None
+                self.endpoint_triple_store = None
+                self.gamma = 0.9
+                self.seed = 1
+                self.min_jaccard_similarity = 0.0
+                self.ratio_sample_nc = 0.2
+                self.ratio_sample_object_prop = 0.1
+                self.num_nominals = 10
+                self.path_report = "incomplete_father_0_1/ALCQHI_Retrieval_Results.csv"
+        args = ARGS()
+        clear()
+        os.mkdir("incomplete_father_0_1")
+        js, f1 = execute(args)
+
+        self.assertEqual(js, 1.0)
+        self.assertEqual(f1, 1.0)
+
+    def test_retrieval_eval_under_incomplete(self):
+        class ARGS:
+            def __init__(self):
+                self.path_kg = "KGs/Family/father.owl"
+                self.seed = 1
+                self.ratio_sample_nc = None
+                self.ratio_sample_object_prop = None
+                self.path_report = "ALCQHI_Retrieval_Results.csv"
+                self.number_of_subgraphs = 1
+                self.ratio = 0.1
+                self.operation = "incomplete"
+                self.sample = "No"
+
+        args = ARGS()
+        results = execute2(args)
+        for r, v in results.items():
+            self.assertGreaterEqual(v, 0.9)
+        clear()
+
diff --git a/tests/test_static_funcs.py b/tests/test_static_funcs.py
new file mode 100644
index 00000000..a461e397
--- /dev/null
+++ b/tests/test_static_funcs.py
@@ -0,0 +1,8 @@
+from ontolearn.utils.static_funcs import compute_f1_score
+class TestStaticFuncs:
+
+    def test_f1(self):
+        assert compute_f1_score(individuals={"A","B","C"},pos={"A","B","C"},neg={"D"})==1.0
+        assert compute_f1_score(individuals={"A","B","C"},pos={"A","B","C"},neg={"D"})==1.0
+
+    # TODO:CD: Add remaining tests
\ No newline at end of file
diff --git a/tests/test_value_splitter.py b/tests/test_value_splitter.py
index 283b8d52..a3522833 100644
--- a/tests/test_value_splitter.py
+++ b/tests/test_value_splitter.py
@@ -3,7 +3,7 @@
 
 from owlready2.prop import DataProperty
 from ontolearn.value_splitter import BinningValueSplitter
-from owlapy.owl_reasoner import FastInstanceCheckerReasoner, OntologyReasoner
+from owlapy.owl_reasoner import StructuralReasoner
 from owlapy.owl_literal import OWLDataProperty, OWLLiteral
 from owlapy.iri import IRI
 from owlapy.owl_ontology_manager import OntologyManager
@@ -28,8 +28,7 @@ class test_float(DataProperty):
         onto._onto.markus.test_int = values_int
         onto._onto.markus.test_float = values_float
 
-        base_reasoner = OntologyReasoner(onto)
-        reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner)
+        reasoner = StructuralReasoner(onto)
 
         test_int_dp = OWLDataProperty(IRI(namespace_, 'test_int'))
         test_float_dp = OWLDataProperty(IRI(namespace_, 'test_float'))
@@ -57,8 +56,7 @@ class test_time(DataProperty):
                            date(1985, 4, 6), date(1999, 9, 9)]
         onto._onto.markus.test_time = values_datetime
 
-        base_reasoner = OntologyReasoner(onto)
-        reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner)
+        reasoner = StructuralReasoner(onto)
 
         test_time_dp = OWLDataProperty(IRI(namespace_, 'test_time'))