use crate::{
ebi_traits::ebi_trait_finite_stochastic_language::EbiTraitFiniteStochasticLanguage,
math::distances::TriangularDistanceMatrix,
};
use ebi_objects::{
FiniteLanguage,
anyhow::{Result, anyhow},
ebi_arithmetic::{Fraction, One, Zero, f},
};
pub fn medoid<T>(log: &T, number_of_traces: &usize) -> Result<FiniteLanguage>
where
T: EbiTraitFiniteStochasticLanguage + ?Sized,
{
let activity_key = log.activity_key().clone();
let mut result = FiniteLanguage::new_hashmap();
log::info!("Computing {} medoid traces", number_of_traces);
let distances = TriangularDistanceMatrix::new(log);
if number_of_traces.is_one() {
if let Some(trace_index) = medoid_single(log, &distances) {
result.insert(
log.iter_traces()
.nth(trace_index)
.unwrap()
.to_owned(),
);
return Ok((activity_key, result).into());
} else {
return Err(anyhow!(
"1 trace was requested, but the stochastic language contains none."
));
}
}
if log.number_of_traces() < *number_of_traces {
return Err(anyhow!(
"{} traces were requested, but the stochastic language contains only {} traces.",
number_of_traces,
log.number_of_traces()
));
}
let mut sum_distance = sum_distances(log, &distances);
let mut list = Vec::new();
while list.len() < *number_of_traces {
let mut min_pos = 0;
for i in 1..sum_distance.len() {
if sum_distance[i] < sum_distance[min_pos] {
min_pos = i;
}
}
list.push(min_pos);
sum_distance[min_pos] = f!(2);
}
list.sort();
let mut list_i = 0;
for (i1, trace1) in log.iter_traces().enumerate() {
if list_i < list.len() && i1 == list[list_i] {
result.insert(trace1.to_vec());
list_i += 1;
}
}
Ok((activity_key, result).into())
}
pub fn medoid_single<T>(log: &T, distances: &TriangularDistanceMatrix) -> Option<usize>
where
T: EbiTraitFiniteStochasticLanguage + ?Sized,
{
let sum_distance = sum_distances(log, distances);
if distances.len() == 0 {
return None;
}
let mut min_pos = 0;
let mut min_value = &Fraction::one();
for (pos, value) in sum_distance.iter().enumerate() {
if value < &min_value {
min_pos = pos;
min_value = value;
}
}
return Some(min_pos);
}
pub fn sum_distances<T>(log: &T, distances: &TriangularDistanceMatrix) -> Vec<Fraction>
where
T: EbiTraitFiniteStochasticLanguage + ?Sized,
{
let mut sum_distance = vec![Fraction::zero(); log.number_of_traces()];
for (i, j, _, distance) in distances {
let mut distance_i = distance.as_ref().clone();
distance_i *= log.iter_probabilities().nth(j).unwrap();
sum_distance[i] += &distance_i;
let mut distance_j = distance.as_ref().clone();
distance_j *= log.iter_probabilities().nth(i).unwrap();
sum_distance[j] += distance_j;
}
sum_distance
}
#[cfg(test)]
mod tests {
use std::fs;
use ebi_objects::FiniteStochasticLanguage;
use crate::techniques::medoid;
#[test]
fn medoid() {
let fin = fs::read_to_string("testfiles/aa-ab-ba.slang").unwrap();
let slang = fin.parse::<FiniteStochasticLanguage>().unwrap();
let fout = fs::read_to_string("testfiles/ba.lang").unwrap();
let medoid = medoid::medoid(&slang, &1).unwrap();
assert_eq!(fout, medoid.to_string())
}
}