use crate::{
ebi_framework::displayable::Displayable,
ebi_traits::ebi_trait_stochastic_semantics::EbiTraitStochasticSemantics,
stochastic_semantics::stochastic_semantics::StochasticSemantics,
techniques::align::transform_alignment,
};
use ebi_objects::{
Activity, LanguageOfAlignments,
anyhow::{Result, anyhow},
ebi_arithmetic::{Fraction, One, OneMinus, Zero},
};
use ebi_optimisation::astar;
use std::ops::{Add, AddAssign};
#[derive(Clone, Debug)]
pub struct StochasticWeightedCost {
cost: u32,
probability: Fraction,
stochastic_weighted_cost: Fraction,
}
impl Zero for StochasticWeightedCost {
fn zero() -> Self {
StochasticWeightedCost {
cost: 0,
probability: Fraction::one(),
stochastic_weighted_cost: Fraction::zero(),
}
}
fn is_zero(&self) -> bool {
self.stochastic_weighted_cost.is_zero()
}
}
impl Add for StochasticWeightedCost {
type Output = Self;
fn add(self, other: Self) -> Self {
let probability = &self.probability * &other.probability;
let cost = self.cost + other.cost;
StochasticWeightedCost {
cost: cost,
probability: probability.clone(),
stochastic_weighted_cost: &probability.clone().one_minus() * cost,
}
}
}
impl AddAssign for StochasticWeightedCost {
fn add_assign(&mut self, other: Self) {
self.cost += other.cost;
self.probability *= other.probability;
}
}
impl Ord for StochasticWeightedCost {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let self_stochastic_cost = &self.probability.clone().one_minus() * self.cost;
let other_stochastic_cost = &other.probability.clone().one_minus() * other.cost;
self_stochastic_cost.cmp(&other_stochastic_cost)
}
}
impl PartialOrd for StochasticWeightedCost {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for StochasticWeightedCost {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == std::cmp::Ordering::Equal
}
}
impl Eq for StochasticWeightedCost {}
pub trait ExplainTrace {
fn explain_trace(
&self,
trace: &Vec<Activity>,
balance: &Fraction,
) -> Result<LanguageOfAlignments>;
}
impl ExplainTrace for EbiTraitStochasticSemantics {
fn explain_trace(
&self,
trace: &Vec<Activity>,
balance: &Fraction,
) -> Result<LanguageOfAlignments> {
match self {
EbiTraitStochasticSemantics::Usize(sem) => sem.explain_trace(trace, balance),
EbiTraitStochasticSemantics::AutomatonState(sem) => sem.explain_trace(trace, balance),
EbiTraitStochasticSemantics::Marking(sem) => sem.explain_trace(trace, balance),
EbiTraitStochasticSemantics::TreeMarking(sem) => sem.explain_trace(trace, balance),
}
}
}
impl<State: Displayable> dyn StochasticSemantics<StoSemState = State, SemState = State, AliState = State, LivState = State> {
pub fn explain_trace(
&self,
trace: &Vec<Activity>,
_balance: &Fraction,
) -> Result<LanguageOfAlignments> {
if let Some(initial_state) = self.get_initial_state() {
let start = (0, initial_state);
let successors = |(trace_index, state): &(usize, State)| {
let mut result: Vec<((usize, State), StochasticWeightedCost)> = vec![];
if trace_index < &trace.len() {
result.push((
(trace_index + 1, state.clone()),
StochasticWeightedCost {
cost: 10000,
probability: Fraction::one(),
stochastic_weighted_cost: Fraction::zero(),
},
));
}
for transition in self.get_enabled_transitions(&state) {
let total_weight = self
.get_total_weight_of_enabled_transitions(&state)
.unwrap();
let mut new_state = state.clone();
let _ = self.execute_transition(&mut new_state, transition);
let transition_weight = self.get_transition_weight(&state, transition).unwrap();
let transition_probability = transition_weight / &total_weight;
if let Some(activity) = self.get_transition_activity(transition, state) {
result.push((
(*trace_index, new_state.clone()),
StochasticWeightedCost {
cost: 10000,
probability: transition_probability.clone(),
stochastic_weighted_cost: &transition_probability
.clone()
.one_minus()
* 10000,
},
));
if trace_index < &trace.len() && activity == trace[*trace_index] {
result.push((
(trace_index + 1, new_state),
StochasticWeightedCost {
cost: 0,
probability: transition_probability,
stochastic_weighted_cost: Fraction::zero(),
},
));
}
} else {
result.push((
(*trace_index, new_state),
StochasticWeightedCost {
cost: 1,
probability: transition_probability.clone(),
stochastic_weighted_cost: transition_probability
.clone()
.one_minus(),
},
));
}
}
result
};
let heuristic = |_astate: &(usize, State)| StochasticWeightedCost::zero();
let success = |(trace_index, state): &(usize, State)| {
trace_index == &trace.len() && self.is_final_state(&state)
};
match astar::astar(&start, successors, heuristic, success) {
Some((path, _cost)) => {
let moves = transform_alignment(self, &trace, path)?;
let mut alignments = LanguageOfAlignments::new(self.activity_key().clone());
alignments.push(moves);
Ok(alignments)
}
None => Err(anyhow!("no alignment found")),
}
} else {
Err(anyhow!("Cannot align from an empty language."))
}
}
}