use std::any::Any;
use ontologos_core::{Ontology, OntologyRevision, Profile, Reasoner, ReasonerSession};
use reasonable::reasoner::Reasoner as ReasonableReasoner;
use crate::{
Error, MergeLimits, MergeReport, apply_rdfs_fallbacks, apply_reasonable_fallbacks,
core_to_triples, core_to_triples_for_axioms, merge_triples_into_ontology_with_limits, perf,
};
pub struct ReasonableSession {
reasoner: ReasonableReasoner,
last_revision: OntologyRevision,
warmed: bool,
profile: Profile,
}
impl std::fmt::Debug for ReasonableSession {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReasonableSession")
.field("last_revision", &self.last_revision)
.field("warmed", &self.warmed)
.field("profile", &self.profile)
.finish_non_exhaustive()
}
}
impl ReasonableSession {
#[must_use]
pub fn new_for_profile(profile: Profile) -> Self {
Self {
reasoner: ReasonableReasoner::new(),
last_revision: OntologyRevision::default(),
warmed: false,
profile,
}
}
#[must_use]
pub fn new() -> Self {
Self::new_for_profile(Profile::Rl)
}
#[must_use]
pub fn is_warmed(&self) -> bool {
self.warmed
}
#[must_use]
pub fn last_revision(&self) -> OntologyRevision {
self.last_revision
}
}
impl Default for ReasonableSession {
fn default() -> Self {
Self::new()
}
}
impl ReasonerSession for ReasonableSession {
fn profile(&self) -> Profile {
self.profile
}
fn clear(&mut self) {
*self = Self::new_for_profile(self.profile);
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaterializeOutcome {
pub merge: MergeReport,
pub full_rebuild: bool,
}
fn session_stale(session: &ReasonableSession, ontology: &Ontology) -> bool {
session.warmed && session.last_revision != ontology.revision()
}
pub type MaterializeSessionResult =
std::result::Result<(MaterializeOutcome, ReasonableSession), Box<(Error, ReasonableSession)>>;
pub fn materialize_with_session(
ontology: &mut Ontology,
mut session: ReasonableSession,
incremental: bool,
limits: MergeLimits,
) -> MaterializeSessionResult {
let perf = perf::perf_enabled();
let mut timings = perf::BridgePerfTimings::default();
let started = perf.then(std::time::Instant::now);
let dirty = ontology.dirty().clone();
let stale = session_stale(&session, ontology);
if dirty.has_removals() {
let _ = ontology.strip_inferred_axioms();
}
let use_incremental =
incremental && session.warmed && dirty.is_dirty() && !dirty.has_removals() && !stale;
let full_rebuild =
!incremental || !session.warmed || dirty.has_removals() || stale || !use_incremental;
if full_rebuild {
let triples_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.core_to_triples_s));
let triples = match core_to_triples(ontology) {
Ok(t) => t,
Err(e) => return Err(Box::new((e, session))),
};
drop(triples_timer);
session.reasoner = ReasonableReasoner::new();
session.reasoner.load_triples(triples);
let reason_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.reasonable_reason_s));
session.reasoner.reason();
drop(reason_timer);
session.warmed = true;
} else if use_incremental {
let triples_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.core_to_triples_s));
let delta = match core_to_triples_for_axioms(ontology, dirty.added()) {
Ok(d) => d,
Err(e) => return Err(Box::new((e, session))),
};
drop(triples_timer);
if delta.is_empty() {
ontology.clear_dirty();
session.last_revision = ontology.revision();
return Ok((
MaterializeOutcome {
merge: MergeReport::default(),
full_rebuild: false,
},
session,
));
}
session.reasoner.load_triples(delta);
let reason_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.reasonable_reason_s));
session.reasoner.reason();
drop(reason_timer);
} else {
ontology.clear_dirty();
session.last_revision = ontology.revision();
return Ok((
MaterializeOutcome {
merge: MergeReport::default(),
full_rebuild: false,
},
session,
));
}
let output = session.reasoner.view_output().to_vec();
let diagnostics = session.reasoner.diagnostics();
let merge_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.merge_s));
let merge =
match merge_triples_into_ontology_with_limits(ontology, &output, diagnostics, limits) {
Ok(m) => m,
Err(e) => return Err(Box::new((e, session))),
};
drop(merge_timer);
let post_timer = perf.then(|| perf::PhaseTimer::start(&mut timings.postprocess_s));
let post = if session.profile == Profile::Rdfs {
apply_rdfs_fallbacks(ontology)
} else {
apply_reasonable_fallbacks(ontology)
};
drop(post_timer);
if let Err(e) = post {
return Err(Box::new((e, session)));
}
if let Some(start) = started {
timings.total_s = start.elapsed().as_secs_f64();
}
if perf {
eprintln!("{}", serde_json::to_string(&timings).unwrap_or_default());
}
session.last_revision = ontology.revision();
ontology.clear_dirty();
Ok((
MaterializeOutcome {
merge,
full_rebuild,
},
session,
))
}
pub fn take_reasonable_session(reasoner: &mut Reasoner, profile: Profile) -> ReasonableSession {
reasoner
.take_session()
.and_then(|mut boxed| {
boxed
.as_any_mut()
.downcast_mut::<ReasonableSession>()
.map(std::mem::take)
})
.filter(|s| s.profile() == profile)
.unwrap_or_else(|| ReasonableSession::new_for_profile(profile))
}
pub fn downcast_reasonable_session(
session: &mut dyn ReasonerSession,
) -> Option<&mut ReasonableSession> {
session.as_any_mut().downcast_mut::<ReasonableSession>()
}