Skip to content

Conversation

@Balramt
Copy link
Contributor

@Balramt Balramt commented Nov 5, 2025

This pull request introduces support for contrastive (fact–foil) explanations in SyncReasoner, using the updated Java explanation generator. It also includes integration tests to validate end-to-end behavior with a real ontology and HermiT reasoner.
What was added :

  • Implemented get_contrastive_explanation() in SyncReasoner
  • tests/test_contrastive_explanation.py
  • /examples/ontology_contrastive_explanation.py

@Balramt Balramt requested a review from alkidbaci November 5, 2025 09:21
Comment on lines 1136 to 1139
j_manager = self.ontology.owlapi_manager # OWLOntologyManager (java)
j_ontology = self.ontology.owlapi_ontology # OWLOntology (java)
mapper = self.ontology.mapper # bridge mapper
j_reasoner = getattr(self, "owlapi_reasoner", None)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you have to store them in local variables when you can use self.x directly?
Also why call getattr(self, "owlapi_reasoner", None). The owlapi reasoner is stored under self._owlapi_reasoner and is never None.

# ---------------------------------------------------------
# 3. (Optional) warm up the shared reasoner we already have
# ---------------------------------------------------------
if j_reasoner is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

the owlapi_reasoner is never None

Comment on lines 1161 to 1164
if str(reasoner_name).lower() == "hermit":
j_choice = ReasonerChoice.HERMIT
else:
j_choice = ReasonerChoice.ELK
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should use the same reasoner that the user has defined (stored self.reasoner_name). For example the user may have specified Pellet but your code will default to ELK for ExplenationConfig.

Copy link
Collaborator

@alkidbaci alkidbaci left a comment

Choose a reason for hiding this comment

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

  1. When I try to run the example script ontology_contrastive_explanation.py I get a java exception:

    Traceback (most recent call last):
      File "/home/alkid/PycharmProjects/owlapy/examples/ontology_contrastive_explanation.py", line 64, in <module>
        main()
      File "/home/alkid/PycharmProjects/owlapy/examples/ontology_contrastive_explanation.py", line 26, in main
        ontology = SyncOntology(ontology_path)
      File "/home/alkid/PycharmProjects/owlapy/owlapy/owl_ontology.py", line 1021, in __init__
        self.owlapi_ontology = self.owlapi_manager.loadOntologyFromOntologyDocument(File(file_path))
    java.lang.java.lang.NullPointerException: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "parserFactory" is null
    

    This is happening when trying to initialize a SyncOntology object. The exception does not happen in the develop branch.

  2. The tests are not passing successfully. Please see into that.

  3. I have left you some comments in the code changes

  4. Your tests only ensure type and format correctness but not what the result actually contains. For example, instead of just checking self.assertIsInstance(result["common"], set), you can also check self.assertCountEqual(result["common"],expected_result) where expected_result is a set containing the expected result.

  5. Please move the methods you have implemented (get_contrastive_explanation and create_justifications) after the __init__ of SyncReasoner. Ideally you put them at the end, which indicates a natural order of implementation time.

  6. Please rename the jar dependency contrastive-explanations-0.3-SNAPSHOT-jar-with-dependencies.jar (remove the "-SNAPSHOT-jar-with-dependencies" part)

@Balramt Balramt force-pushed the feature/abduction-sync-reasoner branch from 429e5b7 to ceb51cf Compare January 4, 2026 06:25
@alkidbaci
Copy link
Collaborator

@copilot Check if all the changes requested are taken care of, otherwise open a new PR and complete them.

Copy link

Copilot AI commented Jan 6, 2026

@alkidbaci I've opened a new pull request, #174, to work on those changes. Once the pull request is ready, I'll request review from you.

@alkidbaci
Copy link
Collaborator

It appears that the jar dependency that you have added is overwriting some of the existing java packages.
Can you show me the source of that dependency @Balramt

@Balramt
Copy link
Contributor Author

Balramt commented Jan 7, 2026

We do need de.tu-dresden.inf.lat.evee:evee-elk-proof-extractor-owlapi4:0.3 (OWLAPI4-based) for proof extraction.

Running mvn dependency:tree -Dverbose shows that this dependency pulls in ELK OWLAPI4 artifacts (and transitively OWLAPI 4.5.9), while the owlapy project itself depends on OWLAPI 5.x. Since both OWLAPI 4 and OWLAPI 5 use overlapping org.semanticweb.owlapi.* packages, this can lead to class shadowing when building the fat jar.

de.tu-dresden.inf.lat.evee:evee-elk-proof-extractor-owlapi4:0.3
+- org.semanticweb.elk:elk-owlapi4:0.5.0-SNAPSHOT
| +- net.sourceforge.owlapi:owlapi-api:4.5.9
| +- net.sourceforge.owlapi:owlapi-impl:4.5.9

Then contrast it with our owlapy project OWLAPI5 usage:

anonymized:contrastive-explanations:jar:0.3-SNAPSHOT
+- net.sourceforge.owlapi:owlapi-distribution:jar:5.1.20:compile

@alkidbaci
Copy link
Collaborator

Well I believe this is a problem with no apparent solution then. The JVM can only be started once and the dependencies are set at the moment of initiation. The conflicting distributions of OWLAPI should not coexist in the same JVM session.

@alkidbaci alkidbaci marked this pull request as draft January 8, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants