You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ogit_bridge): multi-parent OntologySchema for OWL multi-inheritance
OWL permits a class to declare multiple `rdfs:subClassOf` triples
(multi-inheritance); biomedical ontologies (FMA's ~75k anatomical
classes, ChEBI, GO) use it extensively. Production `OntologySchema`
previously stored a single `parent: Option<Box<str>>` and the
`from_triples` loop silently overwrote on each new `subClassOf`
triple — the second declared parent won, the first was discarded
without warning.
Identified during the FMA hydrator spec review
(lance-graph/.claude/specs/pr-d-1-fma-owl-hydrator.md): Pattern D
hydrates OWL ontologies through this code path, and silently losing
half a class's inheritance edges would corrupt any downstream
closure-based reasoning (is_ancestor, type-gated propagation).
Changes:
1. `EntityClass` gains `extra_parents: Vec<Box<str>>` — additional
parents beyond the first-observed. `parent: Option<Box<str>>`
keeps the first observed parent (back-compat for single-parent
consumers that read `.parent` directly).
2. `EntityClass::parents()` accessor — iterator over every parent
in source order. Use this in preference to reading `.parent`
when the caller's logic must cover multi-inheritance.
3. `from_triples` no longer silently overwrites: the first
subClassOf populates `parent`; later ones append to
`extra_parents` (dedup against the existing primary + extras).
4. `is_ancestor` walks the multi-parent DAG via BFS instead of the
linear parent chain — this is the case the previous
implementation silently missed. MAX_VISITS=4096 caps total work
(defensive guard against cycles); single-parent chains still
terminate at parent==None in the original O(depth) shape.
3 new regression tests:
- `is_ancestor_multi_parent_direct` — two `subClassOf` triples on
one class; both parents reachable.
- `is_ancestor_multi_parent_transitive_through_second_parent` —
the bug case: ancestor only reachable through the second parent
chain. Would fail on the previous implementation.
- `entity_class_parents_iterator_yields_all` — parents() surfaces
every declared parent.
All 18 ogit_bridge::schema tests pass; lib fmt + clippy clean.
0 commit comments