use super::{CutFilter, DiscoveryConfig};
use crate::models::EventLog;
use crate::powl_arena::PowlArena;
use std::collections::{HashMap, HashSet};
pub fn discover_from_dfg(
log: &EventLog,
config: &DiscoveryConfig,
arena: &mut PowlArena,
) -> Result<u32, String> {
let dfg_edges = log.get_directly_follows(&config.activity_key);
if dfg_edges.is_empty() {
let activities = log.get_activities(&config.activity_key);
if activities.is_empty() {
return super::base_case::handle_empty_log(arena);
}
if activities.len() == 1 {
return super::base_case::handle_single_activity(arena, &activities[0]);
}
}
let traces = build_traces_from_dfg(&dfg_edges, log, &config.activity_key);
let cut_filter = CutFilter::for_variant(config.variant);
super::inductive_miner(&traces, arena, config, &cut_filter)
}
fn build_traces_from_dfg(
dfg_edges: &[(String, String, usize)],
log: &EventLog,
activity_key: &str,
) -> Vec<Vec<String>> {
use std::collections::{HashMap, HashSet};
let mut adjacency: HashMap<&str, Vec<&str>> = HashMap::new();
let mut all_activities: HashSet<&str> = HashSet::new();
for (src, tgt, _count) in dfg_edges {
adjacency
.entry(src.as_str())
.or_default()
.push(tgt.as_str());
all_activities.insert(src.as_str());
all_activities.insert(tgt.as_str());
}
if adjacency.is_empty() {
return super::group_traces_by_activity_sequence(log, activity_key);
}
let mut traces = Vec::new();
let mut has_incoming: HashSet<&str> = HashSet::new();
for (_src, tgt, _count) in dfg_edges {
has_incoming.insert(tgt.as_str());
}
let start_activities: Vec<&str> = all_activities
.iter()
.filter(|act| !has_incoming.contains(*act))
.cloned()
.collect();
for start in &start_activities {
let mut trace = Vec::new();
let mut visited: HashSet<&str> = HashSet::new();
build_trace_recursive(start, &adjacency, &mut trace, &mut visited);
if !trace.is_empty() {
traces.push(trace);
}
}
if traces.is_empty() {
super::group_traces_by_activity_sequence(log, activity_key)
} else {
traces
}
}
fn build_trace_recursive<'a>(
current: &'a str,
adjacency: &HashMap<&'a str, Vec<&'a str>>,
trace: &mut Vec<String>,
visited: &mut HashSet<&'a str>,
) {
if visited.contains(current) {
return; }
trace.push(current.to_string());
visited.insert(current);
if let Some(nexts) = adjacency.get(current) {
for next in nexts {
build_trace_recursive(next, adjacency, trace, visited);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Attributes;
#[test]
fn test_discover_from_dfg_empty() {
let log = EventLog {
attributes: Attributes::new(),
traces: vec![],
};
let mut arena = PowlArena::new();
let config = DiscoveryConfig::default();
let result = discover_from_dfg(&log, &config, &mut arena);
assert!(result.is_ok());
}
#[test]
fn test_build_traces_from_empty_dfg() {
let dfg_edges = vec![];
let log = EventLog {
attributes: Attributes::new(),
traces: vec![],
};
let traces = build_traces_from_dfg(&dfg_edges, &log, "concept:name");
assert_eq!(traces.len(), 0);
}
}