use super::{EntailmentEngine, EntailmentError, Triple, TripleStore};
use crate::entailment::rdf_entailment::{rdfs_iri, rdf_iri};
const OWL_NS: &str = "http://www.w3.org/2002/07/owl#";
#[inline]
fn owl_iri(local: &str) -> String {
format!("{OWL_NS}{local}")
}
#[derive(Debug, Clone)]
pub struct OwlRlRuleSet {
pub class_restrictions: bool,
pub property_characteristics: bool,
pub equivalent_properties: bool,
pub class_axioms: bool,
}
impl Default for OwlRlRuleSet {
fn default() -> Self {
Self {
class_restrictions: true,
property_characteristics: true,
equivalent_properties: true,
class_axioms: true,
}
}
}
#[derive(Debug, Clone)]
pub struct OwlRlEntailmentEngine {
pub rule_set: OwlRlRuleSet,
}
impl OwlRlEntailmentEngine {
pub fn new() -> Self {
Self {
rule_set: OwlRlRuleSet::default(),
}
}
pub fn with_rule_set(rule_set: OwlRlRuleSet) -> Self {
Self { rule_set }
}
fn maybe_add(store: &TripleStore, result: &mut Vec<Triple>, t: Triple) {
if !store.contains(&t.subject, &t.predicate, &t.object)
&& !result.iter().any(|r| r == &t)
{
result.push(t);
}
}
fn rule_cax_sco(store: &TripleStore, result: &mut Vec<Triple>) {
let sub_class = rdfs_iri("subClassOf");
let rdf_type = rdf_iri("type");
let class_pairs: Vec<(String, String)> = store
.get_all_p(&sub_class)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (c, d) in &class_pairs {
if c == d {
continue;
}
for triple in store.get_all_p(&rdf_type) {
if &triple.object == c {
let inferred = Triple::new(&triple.subject, &rdf_type, d);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_cax_eqc1(store: &TripleStore, result: &mut Vec<Triple>) {
let equiv_class = owl_iri("equivalentClass");
let sub_class = rdfs_iri("subClassOf");
let rdf_type = rdf_iri("type");
let equiv_pairs: Vec<(String, String)> = store
.get_all_p(&equiv_class)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (c, d) in &equiv_pairs {
let t1 = Triple::new(c, &sub_class, d);
Self::maybe_add(store, result, t1);
let t2 = Triple::new(d, &sub_class, c);
Self::maybe_add(store, result, t2);
for triple in store.get_all_p(&rdf_type) {
if &triple.object == c {
let inferred = Triple::new(&triple.subject, &rdf_type, d);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_cax_eqc2(store: &TripleStore, result: &mut Vec<Triple>) {
let equiv_class = owl_iri("equivalentClass");
let rdf_type = rdf_iri("type");
let equiv_pairs: Vec<(String, String)> = store
.get_all_p(&equiv_class)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (c, d) in &equiv_pairs {
for triple in store.get_all_p(&rdf_type) {
if &triple.object == d {
let inferred = Triple::new(&triple.subject, &rdf_type, c);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_prp_symp(store: &TripleStore, result: &mut Vec<Triple>) {
let rdf_type = rdf_iri("type");
let symmetric = owl_iri("SymmetricProperty");
let sym_props: Vec<String> = store
.get_all_p(&rdf_type)
.into_iter()
.filter(|t| t.object == symmetric)
.map(|t| t.subject.clone())
.collect();
for p in &sym_props {
for triple in store.get_all_p(p) {
let inferred = Triple::new(&triple.object, p, &triple.subject);
Self::maybe_add(store, result, inferred);
}
}
}
fn rule_prp_trp(store: &TripleStore, result: &mut Vec<Triple>) {
let rdf_type = rdf_iri("type");
let transitive = owl_iri("TransitiveProperty");
let trans_props: Vec<String> = store
.get_all_p(&rdf_type)
.into_iter()
.filter(|t| t.object == transitive)
.map(|t| t.subject.clone())
.collect();
for p in &trans_props {
let pairs: Vec<(String, String)> = store
.get_all_p(p)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (x, y1) in &pairs {
for (y2, z) in &pairs {
if y1 == y2 && x != z {
let inferred = Triple::new(x, p, z);
Self::maybe_add(store, result, inferred);
}
}
}
}
}
fn rule_prp_eqp1(store: &TripleStore, result: &mut Vec<Triple>) {
let equiv_prop = owl_iri("equivalentProperty");
let sub_prop = rdfs_iri("subPropertyOf");
let equiv_pairs: Vec<(String, String)> = store
.get_all_p(&equiv_prop)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (p, q) in &equiv_pairs {
let t1 = Triple::new(p, &sub_prop, q);
Self::maybe_add(store, result, t1);
let t2 = Triple::new(q, &sub_prop, p);
Self::maybe_add(store, result, t2);
for triple in store.get_all_p(p) {
let inferred = Triple::new(&triple.subject, q, &triple.object);
Self::maybe_add(store, result, inferred);
}
}
}
fn rule_prp_eqp2(store: &TripleStore, result: &mut Vec<Triple>) {
let equiv_prop = owl_iri("equivalentProperty");
let equiv_pairs: Vec<(String, String)> = store
.get_all_p(&equiv_prop)
.into_iter()
.map(|t| (t.subject.clone(), t.object.clone()))
.collect();
for (p, q) in &equiv_pairs {
for triple in store.get_all_p(q) {
let inferred = Triple::new(&triple.subject, p, &triple.object);
Self::maybe_add(store, result, inferred);
}
}
}
fn rule_cls_hv1(store: &TripleStore, result: &mut Vec<Triple>) {
let has_value = owl_iri("hasValue");
let on_property = owl_iri("onProperty");
let rdf_type = rdf_iri("type");
let restrictions: Vec<(String, String, String)> = {
let mut v = Vec::new();
for hv_triple in store.get_all_p(&has_value) {
let x = &hv_triple.subject;
let y = &hv_triple.object;
for op_triple in store.get_by_sp(x, &on_property) {
v.push((x.clone(), y.clone(), op_triple.object.clone()));
}
}
v
};
for (x, y, p) in &restrictions {
for triple in store.get_by_po(&rdf_type, x) {
let u = &triple.subject;
let inferred = Triple::new(u, p, y);
Self::maybe_add(store, result, inferred);
}
}
}
fn rule_cls_hv2(store: &TripleStore, result: &mut Vec<Triple>) {
let has_value = owl_iri("hasValue");
let on_property = owl_iri("onProperty");
let rdf_type = rdf_iri("type");
let restrictions: Vec<(String, String, String)> = {
let mut v = Vec::new();
for hv_triple in store.get_all_p(&has_value) {
let x = &hv_triple.subject;
let y = &hv_triple.object;
for op_triple in store.get_by_sp(x, &on_property) {
v.push((x.clone(), y.clone(), op_triple.object.clone()));
}
}
v
};
for (x, y, p) in &restrictions {
for triple in store.get_by_po(p, y) {
let u = &triple.subject;
let inferred = Triple::new(u, &rdf_type, x);
Self::maybe_add(store, result, inferred);
}
}
}
fn rule_cls_svf1(store: &TripleStore, result: &mut Vec<Triple>) {
let some_values_from = owl_iri("someValuesFrom");
let on_property = owl_iri("onProperty");
let rdf_type = rdf_iri("type");
let restrictions: Vec<(String, String, String)> = {
let mut v = Vec::new();
for svf_triple in store.get_all_p(&some_values_from) {
let x = &svf_triple.subject;
let y = &svf_triple.object;
for op_triple in store.get_by_sp(x, &on_property) {
v.push((x.clone(), y.clone(), op_triple.object.clone()));
}
}
v
};
for (x, y, p) in &restrictions {
for p_triple in store.get_all_p(p) {
let u = &p_triple.subject;
let v = &p_triple.object;
if store.contains(v, &rdf_type, y) {
let inferred = Triple::new(u, &rdf_type, x);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_cls_avf(store: &TripleStore, result: &mut Vec<Triple>) {
let all_values_from = owl_iri("allValuesFrom");
let on_property = owl_iri("onProperty");
let rdf_type = rdf_iri("type");
let restrictions: Vec<(String, String, String)> = {
let mut v = Vec::new();
for avf_triple in store.get_all_p(&all_values_from) {
let x = &avf_triple.subject;
let y = &avf_triple.object;
for op_triple in store.get_by_sp(x, &on_property) {
v.push((x.clone(), y.clone(), op_triple.object.clone()));
}
}
v
};
for (x, y, p) in &restrictions {
for type_triple in store.get_by_po(&rdf_type, x) {
let u = &type_triple.subject;
for p_triple in store.get_by_sp(u, p) {
let v = &p_triple.object;
let inferred = Triple::new(v, &rdf_type, y);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_cls_int2(store: &TripleStore, result: &mut Vec<Triple>) {
let intersection_of = owl_iri("intersectionOf");
let rdf_type = rdf_iri("type");
let first_pred = rdf_iri("first");
let rest_pred = rdf_iri("rest");
let rdf_nil = rdf_iri("nil");
for int_triple in store.get_all_p(&intersection_of) {
let c = &int_triple.subject;
let mut list_node = int_triple.object.clone();
let mut members: Vec<String> = Vec::new();
while list_node != rdf_nil {
let firsts = store.get_by_sp(&list_node, &first_pred);
if firsts.is_empty() {
break;
}
members.push(firsts[0].object.clone());
let rests = store.get_by_sp(&list_node, &rest_pred);
if rests.is_empty() {
break;
}
list_node = rests[0].object.clone();
}
if members.is_empty() {
continue;
}
for type_triple in store.get_by_po(&rdf_type, c) {
let y = &type_triple.subject;
for member in &members {
let inferred = Triple::new(y, &rdf_type, member);
Self::maybe_add(store, result, inferred);
}
}
}
}
fn rule_cls_int1(store: &TripleStore, result: &mut Vec<Triple>) {
let intersection_of = owl_iri("intersectionOf");
let rdf_type = rdf_iri("type");
let first_pred = rdf_iri("first");
let rest_pred = rdf_iri("rest");
let rdf_nil = rdf_iri("nil");
for int_triple in store.get_all_p(&intersection_of) {
let c = &int_triple.subject;
let mut list_node = int_triple.object.clone();
let mut members: Vec<String> = Vec::new();
while list_node != rdf_nil {
let firsts = store.get_by_sp(&list_node, &first_pred);
if firsts.is_empty() {
break;
}
members.push(firsts[0].object.clone());
let rests = store.get_by_sp(&list_node, &rest_pred);
if rests.is_empty() {
break;
}
list_node = rests[0].object.clone();
}
if members.is_empty() {
continue;
}
let first_typed: Vec<String> = store
.get_by_po(&rdf_type, &members[0])
.into_iter()
.map(|t| t.subject.clone())
.collect();
for y in first_typed {
let all_typed = members
.iter()
.all(|m| store.contains(&y, &rdf_type, m));
if all_typed {
let inferred = Triple::new(&y, &rdf_type, c);
Self::maybe_add(store, result, inferred);
}
}
}
}
}
impl Default for OwlRlEntailmentEngine {
fn default() -> Self {
Self::new()
}
}
impl EntailmentEngine for OwlRlEntailmentEngine {
fn entail(&self, store: &TripleStore) -> Result<Vec<Triple>, EntailmentError> {
let mut result: Vec<Triple> = Vec::new();
if self.rule_set.class_axioms {
Self::rule_cax_sco(store, &mut result);
}
if self.rule_set.class_axioms {
Self::rule_cax_eqc1(store, &mut result);
Self::rule_cax_eqc2(store, &mut result);
}
if self.rule_set.property_characteristics {
Self::rule_prp_symp(store, &mut result);
Self::rule_prp_trp(store, &mut result);
}
if self.rule_set.equivalent_properties {
Self::rule_prp_eqp1(store, &mut result);
Self::rule_prp_eqp2(store, &mut result);
}
if self.rule_set.class_restrictions {
Self::rule_cls_hv1(store, &mut result);
Self::rule_cls_hv2(store, &mut result);
Self::rule_cls_svf1(store, &mut result);
Self::rule_cls_avf(store, &mut result);
Self::rule_cls_int1(store, &mut result);
Self::rule_cls_int2(store, &mut result);
}
Ok(result)
}
}
impl super::EntailmentRegime for OwlRlEntailmentEngine {
fn name(&self) -> &str {
"OWL 2 RL Entailment"
}
fn entail(
&self,
triples: &[super::RichEntailmentTriple],
) -> Vec<super::RichEntailmentTriple> {
let mut store = TripleStore::new();
for rt in triples {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
store.add(Triple::new(&s, &rt.predicate, &o));
}
let flat_results = match EntailmentEngine::entail(self, &store) {
Ok(v) => v,
Err(_) => return Vec::new(),
};
flat_results
.into_iter()
.filter_map(|t| {
let already = triples.iter().any(|rt| {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
s == t.subject && rt.predicate == t.predicate && o == t.object
});
if already {
None
} else {
Some(super::RichEntailmentTriple::named_triple(
&t.subject,
&t.predicate,
&t.object,
))
}
})
.collect()
}
fn is_consistent(&self, _triples: &[super::RichEntailmentTriple]) -> bool {
true
}
}
fn rich_term_to_str(term: &super::EntailmentTerm) -> String {
match term {
super::EntailmentTerm::NamedNode(s) | super::EntailmentTerm::BlankNode(s) => s.clone(),
super::EntailmentTerm::Literal { value, .. } => value.clone(),
}
}
impl super::EntailmentRegime for super::rdf_entailment::RdfEntailmentEngine {
fn name(&self) -> &str {
"RDF Entailment"
}
fn entail(
&self,
triples: &[super::RichEntailmentTriple],
) -> Vec<super::RichEntailmentTriple> {
let mut store = TripleStore::new();
for rt in triples {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
store.add(Triple::new(&s, &rt.predicate, &o));
}
let flat_results = match EntailmentEngine::entail(self, &store) {
Ok(v) => v,
Err(_) => return Vec::new(),
};
flat_results
.into_iter()
.filter_map(|t| {
let already = triples.iter().any(|rt| {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
s == t.subject && rt.predicate == t.predicate && o == t.object
});
if already {
None
} else {
Some(super::RichEntailmentTriple::named_triple(
&t.subject,
&t.predicate,
&t.object,
))
}
})
.collect()
}
}
impl super::EntailmentRegime for super::rdfs_entailment::RdfsEntailmentEngine {
fn name(&self) -> &str {
"RDFS Entailment"
}
fn entail(
&self,
triples: &[super::RichEntailmentTriple],
) -> Vec<super::RichEntailmentTriple> {
let mut store = TripleStore::new();
for rt in triples {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
store.add(Triple::new(&s, &rt.predicate, &o));
}
let flat_results = match EntailmentEngine::entail(self, &store) {
Ok(v) => v,
Err(_) => return Vec::new(),
};
flat_results
.into_iter()
.filter_map(|t| {
let already = triples.iter().any(|rt| {
let s = rich_term_to_str(&rt.subject);
let o = rich_term_to_str(&rt.object);
s == t.subject && rt.predicate == t.predicate && o == t.object
});
if already {
None
} else {
Some(super::RichEntailmentTriple::named_triple(
&t.subject,
&t.predicate,
&t.object,
))
}
})
.collect()
}
}