ontology-tools / py-horned-owl

A library for Web Ontology Language in Python created using a bridge from horned-owl to python using PyO3.

Home Page:https://ontology-tools.github.io/py-horned-owl/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Provide equivalent to OWL Reasoner API

cmungall opened this issue · comments

Motivation: I would like to use the Rust version of @balhoff's Whelk, whelk.rs

Previously I was thinking the way to go would be to write a pyo3 wrapper onto whelk.rs. However, it may make more sense to have a generic wrapper for reasoners, with py-horned-owl providing the interface, just as there is one reasoner API for the OWLAPI

(it might be nice to have some features the owlapi ones lack though, that give access to some of the neater features of whelk)

@cmungall @phillord Do you think it might make sense to implement the actual reasoner interface and associated data transer at the level of horned-owl (directly within Rust) and then wrap the interactions with the reasoner within py-horned-owl to provide a python interface around it?

To my mind this is something that we should do at a rust level. Plugging in whelk-rs should be simple to do, although doing it in a performant way might be more challenging.

I don't know enough about the APIs to other reasoners (other than via the OWL API interface) to know how easy a generic interface would be. My own inclination, following on from the general approach I have taken with horned would be to plug in whelk first and then worry about making a more general interface when and if it is needed. That has worked quite well.

I created a first draft of an interface (b-gehrke/horned-owl src/reasoner.rs), implemented it for whelk (b-gehrke/whelk-rs src/whelk/reasoner.rs), and integrated it in py-horned-owl (branch reasoner src/reasoner.rs). The file test/reasoner.py contains an example on how to use it from python.

@cmungall @phillord @balhoff What do you think of it? I would be happy if you could try it out and share your thoughts and opinions.

With this design, reasoners only work with immutable ontologies, while the OWL API supports incremental reasoning. I'm not sure on how to realize this concept in Rust, but I came up with a few approaches:

  • We could follow the OWL API design and have ontologies emit events when they change. Reasoners would then subscribe to these events and react accordingly. But events seem rather complicated in Rust and appear to be mostly replaced by other structures.
  • We make reasoners own an ontology. Any updates to the ontology must be made through the reasoner (e.g. reasoner.insert_axiom(...)). With this we'd have a 1:1 mapping of reasoners to ontologies which might be not be ideal.
  • We make ontologies own reasoners. A reasoner can be instantiated and can be added to an ontology (e.g. ontology.add_reasoner(r)). The ontology would then have to update its reasoners whenever they change.

What are your thoughts on this problem and the approaches?

Thank you for your feedback.

I've been looking in the OntologyIndex system, but I'm not sure I understand the concept entirely.

What is the relation of OntologyIndex and Ontology? In my understanding, an Ontology relies on one or more OntologyIndex to store Axioms/Components efficiently for lookup. As a user I would only care about using an Ontology implementation that fits my use case best - without knowing or caring about the indexes used. Is that right?

How would we add additional (reasoner-) indexes to an ontology instance?

Regarding your other points:

  • Changing inferred_axioms to return an iterator does not appear to be a problem.

  • The decision of where to locate the reasoning interface is ultimately yours. I think, if it remains such a slim interface it's easier to keep it in horned-owl. It would also ensure, the two don't go out of sync if the interface of horned-owl changes. Also, I'm worrying some problems might arise when we put it in a sub crate as some things are only possible within the same crate. But I'm not very familiar with Rust yet and I do not fully understand this limitation or if it could have any impact on the future development of reasoners / the reasoning interface of horned-owl.

Thank you

I've updated the horned-owl and whelk-rs forks to represent the changes and make Whelk an ontology index. But trying to use it, I run into problems. In py-horned-owl I would do something like

type WhelkIndexedOntology = TwoIndexedOntology<
    ArcStr,
    Arc<AnnotatedComponent<ArcStr>>,
    SetIndex<ArcStr, Arc<AnnotatedComponent<ArcStr>>>,
    Whelk<ArcStr>>;

#[pyclass(unsendable)]
pub struct PyWhelk(WhelkIndexedOntology);

and build expose all rust methods and additional convenience methods on this python-mapped PyWhelk class. But this leads to a problem: The reasoning methods require a mutable reference but we can only get an immutable reference the the Whelk instance with TwoIndexedOntology::j. What do you think about adding a function exposing a mutable reference to an index? Do you have other ideas?

The reason that we don't do this at the moment is that this would give an easy way to make the two indexes inconsistent; so it could be added, but with some appropriately scary documentation to say if you do change thing don't expect the other indexes to work.

Another possibility would be to allow the Whelk index to own the link to the Whelk reasoner object; or just implement OntologyIndex over the Whelk reasoner instance.