ontocore-reasoner 0.14.0

OntoLogos-backed reasoner facade for OntoCore (ontocore-*)
Documentation
use crate::adapter::{ReasonerAdapter, ReasonerId};
use crate::auto::AutoAdapter;
use crate::dl::DlAdapter;
use crate::el::ElAdapter;
use crate::error::{ReasonerError, Result};
use crate::input::ReasonerInput;
use crate::rdfs::RdfsAdapter;
use crate::result::{ClassificationResult, ExplanationRequest, ExplanationResult};
use crate::rl::RlAdapter;
use ontologos_profile::scanner::scan_constructs;

pub fn adapter_for(id: ReasonerId) -> Result<Box<dyn ReasonerAdapter>> {
    let adapter: Box<dyn ReasonerAdapter> = match id {
        ReasonerId::El => Box::new(ElAdapter),
        ReasonerId::Rl => Box::new(RlAdapter),
        ReasonerId::Rdfs => Box::new(RdfsAdapter),
        ReasonerId::Dl => Box::new(DlAdapter),
        ReasonerId::Auto => Box::new(AutoAdapter),
    };
    Ok(adapter)
}

pub fn classify(
    profile: ReasonerId,
    input: &ReasonerInput,
    auto_detect: bool,
) -> Result<ClassificationResult> {
    let mut warnings = profile_warnings(&input.ontology, profile, auto_detect)?;
    if auto_detect {
        if let Some(suggested) = suggest_profile(&input.ontology) {
            if suggested != profile.as_str() {
                warnings.push(crate::result::ReasonerWarning {
                    code: "profile_suggestion".to_string(),
                    message: format!(
                        "ontology may be better suited to profile '{suggested}' (selected: {})",
                        profile.as_str()
                    ),
                    suggested_profile: Some(suggested),
                });
            }
        }
    }
    let adapter = adapter_for(profile)?;
    let mut result = adapter.classify(input)?;
    result.warnings.extend(warnings);
    Ok(result)
}

pub fn explain(
    profile: ReasonerId,
    input: &ReasonerInput,
    request: &ExplanationRequest,
) -> Result<ExplanationResult> {
    let adapter = adapter_for(profile)?;
    adapter.explain(input, request)
}

fn profile_warnings(
    ontology: &ontologos_core::Ontology,
    profile: ReasonerId,
    auto_detect: bool,
) -> Result<Vec<crate::result::ReasonerWarning>> {
    if !auto_detect {
        return Ok(Vec::new());
    }
    let report = ontologos_profile::detect_profile(ontology)
        .map_err(|e| ReasonerError::Classify(e.to_string()))?;
    let mut warnings = Vec::new();
    for diag in report.diagnostics {
        warnings.push(crate::result::ReasonerWarning {
            code: "profile_construct".to_string(),
            message: diag.message,
            suggested_profile: report.detected.map(|p| format!("{p:?}").to_ascii_lowercase()),
        });
    }
    if profile == ReasonerId::El {
        let constructs = scan_constructs(ontology);
        for diag in ontologos_profile::el_diagnostics(&constructs) {
            warnings.push(crate::result::ReasonerWarning {
                code: "el_construct".to_string(),
                message: diag.message,
                suggested_profile: Some("el".to_string()),
            });
        }
    }
    Ok(warnings)
}

fn suggest_profile(ontology: &ontologos_core::Ontology) -> Option<String> {
    let report = ontologos_profile::detect_profile(ontology).ok()?;
    report.detected.map(|p| match p {
        ontologos_profile::OwlProfile::El => "el".to_string(),
        ontologos_profile::OwlProfile::Rl => "rl".to_string(),
        ontologos_profile::OwlProfile::Ql => "el".to_string(),
        ontologos_profile::OwlProfile::Dl => "dl".to_string(),
    })
}