Getting Started =============== Install ------- **Python package** (pre-built wheels, no Rust toolchain required): .. code-block:: bash pip install reasonable # Python 3.9+ **CLI binary** (requires a Rust toolchain): .. code-block:: bash cargo install reasonable-cli # or build from source after cloning: cargo build -p reasonable-cli --release # binary lands at: ./target/release/reasonable Basic usage ----------- Pass one or more Turtle or N3 files to the ``reasonable`` binary. The reasoner loads all triples, runs OWL 2 RL materialisation, then writes the result to ``output.ttl`` (override with ``-o``): .. code-block:: console reasonable ontology.ttl data.ttl -o result.ttl You can mix as many input files as needed. The reasoner reads each file in order and treats all triples as a single combined graph. Python quickstart ----------------- Load from files on disk: .. code-block:: python import reasonable r = reasonable.PyReasoner() r.load_file("ontology.ttl") r.load_file("data.ttl") triples = r.reason() # list of (subject, predicate, object) rdflib nodes print(len(triples)) Load from an existing rdflib graph: .. code-block:: python import rdflib import reasonable g = rdflib.Graph() g.parse("ontology.ttl") g.parse("data.ttl") r = reasonable.PyReasoner() r.from_graph(g) triples = r.reason() # collect into a new graph result = rdflib.Graph() for triple in triples: result.add(triple) Incremental reasoning --------------------- After the first ``reason()`` call the reasoner tracks which triples it has already processed. Calling ``reason()`` again is incremental — only newly added triples are processed. Use ``update_graph()`` when your data changes. It replaces the base triples and automatically picks incremental or full re-materialisation: .. code-block:: python r = reasonable.PyReasoner() r.from_graph(ontology + initial_data) r.reason() # full materialisation # data changes over time… new_data.add(triple_a) new_data.remove(triple_b) removed = r.update_graph(ontology + new_data) r.reason() # full re-mat when removed=True, else incremental ``update_graph()`` returns ``True`` when removals were detected (which forces a full re-materialisation on the next ``reason()`` call) and ``False`` otherwise. Building from source -------------------- A Rust toolchain (via `rustup `_) is required for everything; `uv `_ and Python 3.9+ are additionally needed for the Python bindings. .. code-block:: bash make build # release CLI binary make test # Rust test suite make dev-python-library # build Python extension into python/.venv make test-python # build + run pytest make build-python-library # distributable wheel → python/dist/ Building the docs ----------------- .. code-block:: bash cd docs uv sync uv run sphinx-build -M html . _build open _build/html/index.html