Skip to content

owl:propertyChainAxiom: documented as materialized, never implemented in the reasoner — docs correction, and a scoped path if we ever want the rule #85

Description

@styk-tv

Filed for the record and deliberately P2. Nothing here blocks anything today.

What was measured

The rule-set docs list owl:propertyChainAxiom among the rules that materialize in practice. On 0.6.22 it does not fire. Canonical OWL spec example, otherwise-empty graph:

@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix ex:  <http://example.org/pc#> .
ex:hasParent  a owl:ObjectProperty .
ex:hasBrother a owl:ObjectProperty .
ex:hasUncle   a owl:ObjectProperty ; owl:propertyChainAxiom ( ex:hasParent ex:hasBrother ) .
ex:alice ex:hasParent ex:bob .  ex:bob ex:hasBrother ex:carl .
asserted triples              10
rdf:first / rdf:rest present   4      the collection parsed correctly
materialize(g,'owl-rl')       11 inferred
reasoner_errors               NONE
ex:alice ex:hasUncle ?x       NOTHING

It reports success and does not do the thing. No error, healthy JSONB. A caller cannot distinguish fired from ignored without asserting the expected triple and checking for it.

Tested three ways before filing: inline in a mixed fixture, standalone with every property typed owl:ObjectProperty, and the minimal canonical case above. Six other entailments derive correctly in the same graph — hasValue, someValuesFrom, TransitiveProperty, inverseOf, equivalentClass, subClassOf transitivity — so the reasoner is running and the fixture is sound.

Cause — not a fork, not a version

My first guess was the pinned fork. That was wrong. The crate source settles it:

reasonable, across lib/ cli/ python/
  propertyChainAxiom / property_chain     0 mentions
  hasValue                               18
  inverseOf                              11
  TransitiveProperty                      3

Never implemented, in any version. The engine behaves exactly as built. The docs describe OWL 2 RL the profile — which does include property chains — rather than the rules the crate implements.

Ask, in order of cost

  1. Correct the docs. Separate "OWL 2 RL, the specification" from "the rules this engine implements", and name property chains as out of scope. Cheapest, and it is the part that actually cost something: a downstream design decision was made on the strength of the measurement, and had the docs been trusted instead, a vocabulary would have shipped that silently derives nothing.
  2. Optionally raise it upstream on the reasoner crate with this fixture attached. Someone who is not us may implement it.
  3. Only if module composition later demands it — a scoped 2-hop rule. The general n-hop case is genuinely hard in Datalog; 2-hop is a fixed-arity join and covers essentially every practical use. Not recommended now.

Workaround in use

A SPARQL sequence path is not available as a fallback:

?x ex:hasParent/ex:hasBrother ?z   ERROR: sparql: blank-node object in query not supported
?x ex:hasParent* ?z                works
?x ex:hasParent ?y . ?y ex:hasBrother ?z   works

The error message is misleading — that query contains no blank node; a sequence path is presumably desugared to an intermediate bnode and the check fires on the desugared form. * paths work, so it is specific to sequence.

We are proceeding with the explicit one-hop join and asserting the closure where needed. Correct, forfeits the for free property, and costs one triple per instance. At the current number of modules that is not measurable.

A self-testing fixture is available on request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2deferred / contingentdocumentationDocs / process documentation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions