An open API service indexing awesome lists of open source software.

https://github.com/gtfierro/reasonable

OWL 2 Reasoner built on DataFrog
https://github.com/gtfierro/reasonable

Last synced: about 1 month ago
JSON representation

OWL 2 Reasoner built on DataFrog

Awesome Lists containing this project

README

          

# Reasonable

![Nightly Build](https://github.com/gtfierro/reasonable/workflows/Nightly%20Build/badge.svg)
![Build](https://github.com/gtfierro/reasonable/workflows/Build/badge.svg)
[![PyPI version](https://badge.fury.io/py/reasonable.svg)](https://badge.fury.io/py/reasonable)

An OWL 2 RL reasoner with reasonable performance

## Performance

Comparing performance of `reasonable` with [OWLRL](https://github.com/RDFLib/OWL-RL) and [Allegro](https://franz.com/agraph/support/documentation/current/materializer.html). Evaluation consisted of loading Brick models of different sizes into the respective reasoning engine and timing how long it took to produce the materialization. `reasonable` is about 7x faster than Allegro and 38x faster than OWLRL on this workload.

![benchmark](img/benchmark.png)

## How to Use

### Command Line Interface

You can download a static build of the command line tool [here](https://github.com/gtfierro/reasonable/releases/download/nightly/reasonable-static) (this is automatically built from the latest master).

Usage as follows:

```
$ reasonable -h
An OWL 2 RL reasoner with reasonable performance

Usage: reasonable [OPTIONS] ...

Arguments:
...

Options:
-o, --output-file [default: output.ttl]
-h, --help Print help
-V, --version Print version
```

Example:

```
$ reasonable example_models/ontologies/Brick.n3 example_models/small1.n3 -o myout.ttl
[2023-07-04T15:31:52Z INFO reasonable] Loading file example_models/ontologies/Brick.n3
[2023-07-04T15:31:52Z INFO reasonable::reasoner] Loaded 14803 triples from file example_models/ontologies/Brick.n3
[2023-07-04T15:31:52Z INFO reasonable] Loading file example_models/small1.n3
[2023-07-04T15:31:52Z INFO reasonable::reasoner] Loaded 14 triples from file example_models/small1.n3
[2023-07-04T15:31:52Z INFO reasonable] Starting reasoning
[2023-07-04T15:32:11Z INFO reasonable] Reasoning completed in 18.66sec
[2023-07-04T15:32:11Z INFO reasonable] Writing to myout.ttl
```

### Diagnostics & CLI Flags

The reasoner records OWL 2 RL rule violations as diagnostics (not errors in the log). Use the CLI to view or enforce them:

- Reporting format: `--error-format text|json|ndjson` (default: text)
- Fail the run on rules: `--fail-on cax-dw,prp-pdw` (repeat or comma-separated; codes like `OWLRL.CAX_DW` also supported)
- Limit output: `--max-diagnostics N`; summary only: `--summary-only`

Common diagnostic codes:
- `OWLRL.CAX_DW`: Individual typed as two disjoint classes
- `OWLRL.PRP_PDW`: Pair violates propertyDisjointWith
- `OWLRL.PRP_ASYP`: Asymmetric property used both directions
- `OWLRL.PRP_IRP`: Irreflexive property used with same subject/object
- `OWLRL.CLS_NOTHING`: Individual typed as owl:Nothing

Example strict run:

```
$ reasonable model.ttl --error-format json --fail-on cax-dw,prp-pdw -o out.ttl
```

### Python

To facilitate usage, we use the [pyo3](https://pyo3.rs/) project to generate Python 3.x bindings to this project.
Installing these *should* be as easy as `pip install reasonable`.

See also the [`brickschema`](https://github.com/BrickSchema/py-brickschema) package for working with Brick models. The package provides a generic interface to this reasoner and several others.

Usage looks like:

```python
import reasonable

# import triples from an rdflib Graph
import rdflib
g = rdflib.Graph()
g.parse("example_models/ontologies/Brick.n3", format="n3")
g.parse("example_models/small1.n3", format="n3")

r = reasonable.PyReasoner()
r.from_graph(g)
triples = r.reason()
print("from rdflib:", len(triples))

# import triples from files on disk
r = reasonable.PyReasoner()
r.load_file("example_models/ontologies/Brick.n3")
r.load_file("example_models/small1.n3")
triples = r.reason()
print("from files:", len(triples))
```

### Rust

See [Rust docs](https://docs.rs/reasonable)

Example of usage from Rust:

```rust
use ::reasonable::owl::Reasoner;
use std::env;
use std::time::Instant;
use log::info;

fn main() {
env_logger::init();
let mut r = Reasoner::new();
env::args().skip(1).map(|filename| {
info!("Loading file {}", &filename);
r.load_file(&filename).unwrap()
}).count();
let reasoning_start = Instant::now();
info!("Starting reasoning");
r.reason();
info!("Reasoning completed in {:.02}sec", reasoning_start.elapsed().as_secs_f64());
r.dump_file("output.ttl").unwrap();
}
```

## OWL 2 Rules

Using rule definitions from [here](https://www.w3.org/TR/owl2-profiles/#Reasoning_in_OWL_2_RL_and_RDF_Graphs_using_Rules).

**TODO**: implement RDF/RDFS entailment semantics as described [here](https://www.w3.org/TR/rdf11-mt/)

**Note**: haven't implemented rules that produce exceptions; waiting to determine the best way of handling these errors.

### Equality Semantics

|Completed| Rule name | Notes |
|---------|----------|-------|
| no | `eq-ref` | implementation is very inefficient; causes lots of flux |
| **yes**| `eq-sym` | |
| **yes**| `eq-trans` | |
| **yes**| `eq-rep-s` | |
| **yes**| `eq-rep-p` | |
| **yes**| `eq-rep-o` | |
| no | `eq-diff1` | throws exception |
| no | `eq-diff2` | throws exception |
| no | `eq-diff3` | throws exception |

### Property Axiom Semantics

|Completed| Rule name | Notes |
|---------|----------|-------|
| no | `prp-ap` | |
| **yes** | `prp-dom` | |
| **yes** | `prp-rng` | |
| **yes** | `prp-fp` | |
| **yes** | `prp-ifp` | |
| **yes** | `prp-irp` | throws exception |
| **yes** | `prp-symp` | |
| **yes** | `prp-asyp` | throws exception |
| **yes** | `prp-trp` | |
| **yes** | `prp-spo1` | |
| no | `prp-spo2` | |
| **yes** | `prp-eqp1` | |
| **yes** | `prp-eqp2` | |
| **yes** | `prp-pdw` | throws exception |
| no | `prp-adp` | throws exception |
| **yes** | `prp-inv1` | |
| **yes** | `prp-inv2` | |
| no | `prp-key` | |
| no | `prp-npa1` | throws exception |
| no | `prp-npa2` | throws exception |

### Class Semantics

|Completed| Rule name | Notes |
|---------|----------|-------|
| **yes**| `cls-thing` | |
| **yes**| `cls-nothing1` | |
| **yes**| `cls-nothing2` | throws exception |
| **yes**| `cls-int1` | |
| **yes**| `cls-int2` | |
| **yes**| `cls-uni` | |
| **yes**| `cls-com` | throws exception |
| **yes**| `cls-svf1` | |
| **yes**| `cls-svf2` | |
| **yes**| `cls-avf` | |
| **yes**| `cls-hv1` | |
| **yes**| `cls-hv2` | |
| no | `cls-maxc1` | throws exception |
| no | `cls-maxc2` | |
| no | `cls-maxqc1` | throws exception |
| no | `cls-maxqc2` | throws exception |
| no | `cls-maxqc3` | |
| no | `cls-maxqc4` | |
| no | `cls-oo` | |

### Class Axiom Semantics

|Completed| Rule name | Notes |
|---------|----------|-------|
| **yes**| `cax-sco` | |
| **yes**| `cax-eqc1` | |
| **yes**| `cax-eqc2` | |
| **yes**| `cax-dw` | throws exception |
| no | `cax-adc` | throws exception |

### Other

- no datatype semantics for now

## Development Notes

To publish new versions of `reasonable`, tag a commit with the version (e.g. `v1.3.2`) and push the tag to GitHub. This will execute the `publish` action which builds an uploads to PyPi.