use crate::types::Receipt;
use std::collections::HashMap;
use wasm4pm::ilp_discovery::{
compute_simplicity, discover_ilp_petri_net_from_log, discover_optimized_dfg_from_log,
};
use wasm4pm::models::{AttributeValue, Event, EventLog, Trace};
use wasm4pm::process_tree::discover_simple_process_tree_from_log;
pub const ACTIVITY_KEY: &str = "concept:name";
pub fn project_to_event_log(receipt: &Receipt) -> EventLog {
let events: Vec<Event> = receipt
.events
.iter()
.map(|ev| {
let mut attrs: HashMap<String, AttributeValue> = HashMap::new();
attrs.insert(
ACTIVITY_KEY.to_string(),
AttributeValue::String(ev.event_type.clone()),
);
Event { attributes: attrs }
})
.collect();
EventLog {
attributes: HashMap::new(),
traces: vec![Trace {
attributes: HashMap::new(),
events,
}],
}
}
pub fn discover_process_tree(receipt: &Receipt) -> String {
let log = project_to_event_log(receipt);
discover_simple_process_tree_from_log(&log, ACTIVITY_KEY)
}
pub fn conformance_metrics(receipt: &Receipt) -> (f64, f64) {
let log = project_to_event_log(receipt);
let (_net, fitness, activity_coverage) = discover_ilp_petri_net_from_log(&log, ACTIVITY_KEY);
(fitness, activity_coverage)
}
pub fn discover_dfg_summary(receipt: &Receipt) -> (usize, usize, usize, usize) {
let log = project_to_event_log(receipt);
let dfg = discover_optimized_dfg_from_log(&log, ACTIVITY_KEY, 1.0, 1.0);
(
dfg.nodes.len(),
dfg.edges.len(),
dfg.start_activities.len(),
dfg.end_activities.len(),
)
}
pub fn discover_from_admitted(admitted: &crate::types::AdmittedReceipt) -> String {
discover_process_tree(&admitted.value)
}
pub fn quality_metrics_from_admitted(admitted: &crate::types::AdmittedReceipt) -> (f64, f64, f64) {
quality_metrics(&admitted.value)
}
pub fn quality_metrics(receipt: &Receipt) -> (f64, f64, f64) {
let log = project_to_event_log(receipt);
let (net, fitness, activity_coverage) = discover_ilp_petri_net_from_log(&log, ACTIVITY_KEY);
let simplicity = compute_simplicity(net.places.len(), net.transitions.len(), net.arcs.len());
(fitness, activity_coverage, simplicity)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ocel::{build_event, object_ref, SeqCounter};
fn receipt_with(activities: &[&str]) -> Receipt {
let mut asm = crate::chain::ChainAssembler::new();
let mut counter = SeqCounter::new();
for (i, act) in activities.iter().enumerate() {
let ev = build_event(
*act,
vec![object_ref(&format!("obj-{i}"), "artifact")],
act.as_bytes(),
&mut counter,
)
.expect("build event");
asm.append(ev).expect("append");
}
asm.finalize()
}
#[test]
fn discovers_a_model_mentioning_the_receipt_activities() {
let receipt = receipt_with(&["create", "transform", "release"]);
let model = discover_process_tree(&receipt);
assert!(
model.contains("create"),
"discovered model must mention 'create'; got: {model}"
);
assert!(
model.contains("transform"),
"discovered model must mention 'transform'; got: {model}"
);
assert!(
model.contains("release"),
"discovered model must mention 'release'; got: {model}"
);
}
#[test]
fn projection_preserves_event_count() {
let receipt = receipt_with(&["a", "b", "c", "d"]);
let log = project_to_event_log(&receipt);
assert_eq!(log.traces.len(), 1, "one receipt → one trace");
assert_eq!(
log.traces[0].events.len(),
4,
"every receipt event projects to one log event"
);
}
#[test]
fn conformance_metrics_are_real_numbers_from_replay() {
let receipt = receipt_with(&["create", "transform", "release"]);
let (fitness, activity_coverage) = conformance_metrics(&receipt);
assert!(
(0.0..=1.0).contains(&fitness),
"fitness must be a real number in [0,1]; got {fitness}"
);
assert!(
(0.0..=1.0).contains(&activity_coverage),
"activity_coverage must be a real number in [0,1]; got {activity_coverage}"
);
assert!(
fitness > 0.5,
"a log replayed on its own discovered net should fit well; got {fitness}"
);
}
#[test]
fn simplicity_is_a_real_number_from_the_discovered_net() {
let receipt = receipt_with(&["create", "transform", "release"]);
let (_f, _p, simplicity) = quality_metrics(&receipt);
assert!(
(0.0..=1.0).contains(&simplicity),
"simplicity must be a real number in [0,1]; got {simplicity}"
);
}
}