Skip to main content

owl_dl_core/
transform.rs

1//! Structural transformation (Tseitin-style fresh-name introduction).
2//!
3//! ## Scope in Phase 1 v0
4//!
5//! This module is a **deliberate stub**. It exposes a stable
6//! [`transform`] function that the Phase 1 pipeline (`nnf -> told ->
7//! definitions -> absorb -> transform`) can call today, even though the
8//! current body is the identity. Phase 4 will fill it in.
9//!
10//! ## Why deferred
11//!
12//! Tseitin-style transformation introduces fresh atomic class names for
13//! complex sub-concepts that recur frequently — converting axioms like
14//! `A ⊑ ∃R.(C ⊓ D ⊓ E)` into `A ⊑ ∃R.X` plus `X ≡ C ⊓ D ⊓ E`. The
15//! tableau then labels nodes with the fresh `X` instead of carrying the
16//! conjunction.
17//!
18//! For the **standard tableau** we picked in strategy v2 §3.2, two
19//! pre-existing pieces of work already do most of what Tseitin buys:
20//!
21//! 1. [`crate::ConceptPool`] interns every distinct sub-expression to
22//!    a single [`crate::ConceptId`]. Logical equality is O(1) integer
23//!    comparison; sub-concept sharing is automatic.
24//! 2. [`crate::absorb`] converts most `⊤ ⊑ φ` axioms into triggers —
25//!    `ConceptRule` / `NominalRule` / `RoleRule` — that fire only when
26//!    needed, regardless of the structural complexity of φ.
27//!
28//! [`crate::Definitions`] (lazy unfolding) handles the remaining
29//! case where keeping a named atom in labels is preferable to expanding
30//! it.
31//!
32//! ## Phase 4 plan
33//!
34//! When this is revisited:
35//!
36//! - Walk every `ConceptId` reachable from the absorbed `TBox`.
37//! - Identify sub-expressions appearing in `N ≥ k` distinct contexts.
38//! - Synthesize a fresh class IRI in a `_TS_n` namespace, add to the
39//!   vocabulary, and emit a [`crate::Definitions`] entry binding it to
40//!   the body.
41//! - Rewrite the absorbed `TBox` to use the fresh name in place of the
42//!   original sub-expression.
43//!
44//! Implementation will live here, with the existing [`transform`] entry
45//! point evolving to do the work (or returning a richer struct that
46//! also carries the new vocabulary / definition entries).
47
48use crate::absorb::AbsorbedTBox;
49
50/// Phase 1 v0: identity. See module-level docs for what Phase 4 will
51/// add.
52#[must_use]
53pub fn transform(tbox: AbsorbedTBox) -> AbsorbedTBox {
54    tbox
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::ConceptPool;
61    use crate::absorb::{AbsorbedTBox, ConceptRule};
62    use crate::ir::ClassId;
63
64    #[test]
65    fn identity_preserves_concept_rules() {
66        let mut pool = ConceptPool::new();
67        let a = pool.atomic(ClassId::new(0));
68        let input = AbsorbedTBox {
69            concept_rules: vec![ConceptRule {
70                trigger: ClassId::new(1),
71                conclusion: a,
72            }],
73            ..AbsorbedTBox::default()
74        };
75        let cloned = input.clone();
76        let output = transform(input);
77        assert_eq!(output, cloned);
78    }
79
80    #[test]
81    fn identity_preserves_empty_tbox() {
82        let output = transform(AbsorbedTBox::default());
83        assert_eq!(output, AbsorbedTBox::default());
84    }
85}