use crate::{
ebi_framework::displayable::Displayable, ebi_traits::ebi_trait_semantics::EbiTraitSemantics,
semantics::semantics::Semantics,
};
use ebi_objects::{
StochasticLanguageOfAlignments,
anyhow::{Context, Result, anyhow},
ebi_arithmetic::{Fraction, Zero},
ebi_objects::language_of_alignments::Move,
};
use std::collections::{HashMap, hash_map::Entry};
pub const UNMATCHING: &str = "alignments and model do not belong to one another";
pub trait EscapingEdgesPrecision {
fn escaping_edges_precision(
&self,
alignments: StochasticLanguageOfAlignments,
) -> Result<Fraction>;
}
impl EscapingEdgesPrecision for EbiTraitSemantics {
fn escaping_edges_precision(
&self,
alignments: StochasticLanguageOfAlignments,
) -> Result<Fraction> {
match self {
EbiTraitSemantics::Usize(semantics) => semantics.escaping_edges_precision(alignments),
EbiTraitSemantics::AutomatonState(semantics) => {
semantics.escaping_edges_precision(alignments)
}
EbiTraitSemantics::Marking(semantics) => semantics.escaping_edges_precision(alignments),
EbiTraitSemantics::TreeMarking(semantics) => {
semantics.escaping_edges_precision(alignments)
}
EbiTraitSemantics::BPMNMarking(semantics) => {
semantics.escaping_edges_precision(alignments)
}
EbiTraitSemantics::FspolangMarking(semantics) => {
semantics.escaping_edges_precision(alignments)
}
}
}
}
impl<T, State> EscapingEdgesPrecision for T
where
T: Semantics<SemState = State> + Send + Sync + ?Sized,
State: Displayable,
{
fn escaping_edges_precision(
&self,
alignments: StochasticLanguageOfAlignments,
) -> Result<Fraction> {
let initial_state = self
.get_initial_state()
.ok_or_else(|| anyhow!("{}: model has no initial state", UNMATCHING))?;
let mut nodes = vec![PrefixTreeNode::new(
self.get_enabled_transitions(&initial_state).len(),
)];
for (alignment, probability) in alignments {
let mut state = initial_state.clone();
let mut node_index = 0;
nodes[node_index].probability += &probability;
for movee in alignment {
match movee {
Move::LogMove { .. } => {} Move::ModelMove { transition, .. }
| Move::SynchronousMove { transition, .. }
| Move::SilentMove { transition } => {
if !self.get_enabled_transitions(&state).contains(&transition) {
return Err(anyhow!(
"{}: transition {} is not enabled",
UNMATCHING,
transition
));
}
self.execute_transition(&mut state, transition)
.with_context(|| format!("cannot execute transition"))?;
let new_node_index = nodes.len();
let mut new_node = None;
match nodes[node_index].children.entry(transition) {
Entry::Occupied(occupied_entry) => {
node_index = *occupied_entry.get();
nodes[node_index].probability += &probability;
}
Entry::Vacant(vacant_entry) => {
new_node = Some(PrefixTreeNode {
probability: probability.clone(),
number_of_outgoing_edges_in_model: self
.get_enabled_transitions(&state)
.len(),
children: HashMap::new(),
});
vacant_entry.insert(new_node_index);
}
}
if let Some(new_add) = new_node {
nodes.push(new_add);
}
}
}
}
}
let mut sum_weight_taken = Fraction::zero();
let mut sum_weight_enabled = Fraction::zero();
for node in nodes {
if node.number_of_outgoing_edges_in_model != 0 {
let mut weight_taken = node.probability.clone();
weight_taken *= node.children.len();
sum_weight_taken += weight_taken;
let mut weight_enabled = node.probability;
weight_enabled *= node.number_of_outgoing_edges_in_model;
sum_weight_enabled += weight_enabled;
}
}
sum_weight_taken /= sum_weight_enabled;
Ok(sum_weight_taken)
}
}
struct PrefixTreeNode {
probability: Fraction,
number_of_outgoing_edges_in_model: usize,
children: HashMap<usize, usize>,
}
impl PrefixTreeNode {
pub fn new(number_of_outgoing_edges_in_model: usize) -> Self {
Self {
probability: Fraction::zero(),
number_of_outgoing_edges_in_model: number_of_outgoing_edges_in_model,
children: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use crate::techniques::escaping_edges_precision::EscapingEdgesPrecision;
use ebi_objects::{
StochasticDeterministicFiniteAutomaton, StochasticLanguageOfAlignments,
ebi_arithmetic::Fraction,
};
use std::fs;
#[test]
fn precision_test() {
let fin = fs::read_to_string("testfiles/aa-ab-ba.slang_a-b-c-livelock.sdfa.sali").unwrap();
let sali = fin.parse::<StochasticLanguageOfAlignments>().unwrap();
let fin = fs::read_to_string("testfiles/a-b-c-livelock.sdfa").unwrap();
let sdfa = fin
.parse::<StochasticDeterministicFiniteAutomaton>()
.unwrap();
assert_eq!(
sdfa.escaping_edges_precision(sali).unwrap(),
Fraction::from((17, 20))
);
}
}