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
- Host: GitHub
- URL: https://github.com/gtfierro/reasonable
- Owner: gtfierro
- License: bsd-3-clause
- Created: 2019-10-31T18:06:06.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2025-09-10T19:23:51.000Z (3 months ago)
- Last Synced: 2025-10-19T10:44:20.236Z (about 1 month ago)
- Language: Rust
- Size: 5.21 MB
- Stars: 93
- Watchers: 7
- Forks: 9
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-semantic-web - reasonable - OWL 2 Reasoner built on DataFrog (Reasoners / Educational)
README
# Reasonable


[](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.

## 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.