process_mining/alphappp/
auto_parameters.rs

1use crate::{
2    event_log::activity_projection::EventLogActivityProjection,
3    petri_net::petri_net_struct::{PetriNet, Transition},
4};
5
6use super::full::{alphappp_discover_petri_net, AlphaPPPConfig};
7const AUTO_CONFIGS: &[AlphaPPPConfig] = &[
8    AlphaPPPConfig {
9        balance_thresh: 0.6,
10        fitness_thresh: 0.4,
11        replay_thresh: 0.0,
12        log_repair_skip_df_thresh_rel: 4.0,
13        log_repair_loop_df_thresh_rel: 4.0,
14        absolute_df_clean_thresh: 1,
15        relative_df_clean_thresh: 0.01,
16    },
17    AlphaPPPConfig {
18        balance_thresh: 0.6,
19        fitness_thresh: 0.4,
20        replay_thresh: 0.0,
21        log_repair_skip_df_thresh_rel: 2.0,
22        log_repair_loop_df_thresh_rel: 2.0,
23        absolute_df_clean_thresh: 1,
24        relative_df_clean_thresh: 0.01,
25    },
26    AlphaPPPConfig {
27        balance_thresh: 0.4,
28        fitness_thresh: 0.6,
29        replay_thresh: 0.0,
30        log_repair_skip_df_thresh_rel: 4.0,
31        log_repair_loop_df_thresh_rel: 4.0,
32        absolute_df_clean_thresh: 1,
33        relative_df_clean_thresh: 0.01,
34    },
35    AlphaPPPConfig {
36        balance_thresh: 0.4,
37        fitness_thresh: 0.6,
38        replay_thresh: 0.0,
39        log_repair_skip_df_thresh_rel: 2.0,
40        log_repair_loop_df_thresh_rel: 2.0,
41        absolute_df_clean_thresh: 1,
42        relative_df_clean_thresh: 0.01,
43    },
44    AlphaPPPConfig {
45        balance_thresh: 0.4,
46        fitness_thresh: 0.6,
47        replay_thresh: 0.0,
48        log_repair_skip_df_thresh_rel: 2.0,
49        log_repair_loop_df_thresh_rel: 2.0,
50        absolute_df_clean_thresh: 5,
51        relative_df_clean_thresh: 0.05,
52    },
53    AlphaPPPConfig {
54        balance_thresh: 0.1,
55        fitness_thresh: 0.8,
56        replay_thresh: 0.0,
57        log_repair_skip_df_thresh_rel: 2.0,
58        log_repair_loop_df_thresh_rel: 2.0,
59        absolute_df_clean_thresh: 5,
60        relative_df_clean_thresh: 0.05,
61    },
62    AlphaPPPConfig {
63        balance_thresh: 0.25,
64        fitness_thresh: 0.75,
65        replay_thresh: 0.0,
66        log_repair_skip_df_thresh_rel: 2.0,
67        log_repair_loop_df_thresh_rel: 2.0,
68        absolute_df_clean_thresh: 25,
69        relative_df_clean_thresh: 0.1,
70    },
71    AlphaPPPConfig {
72        balance_thresh: 0.1,
73        fitness_thresh: 0.8,
74        replay_thresh: 0.0,
75        log_repair_skip_df_thresh_rel: 4.0,
76        log_repair_loop_df_thresh_rel: 4.0,
77        absolute_df_clean_thresh: 1,
78        relative_df_clean_thresh: 0.01,
79    },
80    AlphaPPPConfig {
81        balance_thresh: 0.1,
82        fitness_thresh: 0.8,
83        replay_thresh: 0.0,
84        log_repair_skip_df_thresh_rel: 2.0,
85        log_repair_loop_df_thresh_rel: 2.0,
86        absolute_df_clean_thresh: 1,
87        relative_df_clean_thresh: 0.01,
88    },
89    AlphaPPPConfig {
90        balance_thresh: 0.1,
91        fitness_thresh: 0.9,
92        replay_thresh: 0.0,
93        log_repair_skip_df_thresh_rel: 4.0,
94        log_repair_loop_df_thresh_rel: 4.0,
95        absolute_df_clean_thresh: 1,
96        relative_df_clean_thresh: 0.01,
97    },
98    AlphaPPPConfig {
99        balance_thresh: 0.1,
100        fitness_thresh: 0.9,
101        replay_thresh: 0.0,
102        log_repair_skip_df_thresh_rel: 2.0,
103        log_repair_loop_df_thresh_rel: 2.0,
104        absolute_df_clean_thresh: 1,
105        relative_df_clean_thresh: 0.01,
106    },
107];
108
109/// Automatically select parameters for Alpha+++ and discover a [`PetriNet`] using the chosen parameters
110///
111/// Currently, tests out multiple paramater configurations and selects a best one based on the discovered Petri net
112pub fn alphappp_discover_with_auto_parameters(
113    log_proj: &EventLogActivityProjection,
114) -> (AlphaPPPConfig, PetriNet) {
115    let mut best: Option<(AlphaPPPConfig, f32, PetriNet)> = None;
116    for c in AUTO_CONFIGS {
117        let (pn, _) = alphappp_discover_petri_net(log_proj, *c);
118        let score = score_discovered_pn(&pn, c);
119        match best {
120            Some((_, best_score, _)) => {
121                if score > best_score {
122                    best = Some((*c, score, pn));
123                }
124            }
125            None => {
126                best = Some((*c, score, pn));
127            }
128        }
129    }
130    let (best_config, best_score, best_pn) = best.unwrap();
131    println!(
132        "Best score: {:.2} with config {:?}",
133        best_score, best_config
134    );
135    println!(
136        "Resulting net has {} arcs, {} transitions and {} places",
137        best_pn.arcs.len(),
138        best_pn.transitions.len(),
139        best_pn.places.len()
140    );
141    (best_config, best_pn)
142}
143
144fn score_discovered_pn(pn: &PetriNet, config: &AlphaPPPConfig) -> f32 {
145    fn is_transition_well_connected(pn: &PetriNet, t: &Transition) -> bool {
146        if t.label.is_some() {
147            let preset_connected = pn
148                .preset_of_transition(t.into())
149                .into_iter()
150                .filter(|p| {
151                    pn.is_in_initial_marking(p)
152                        || pn.is_in_a_final_marking(p)
153                        || pn
154                            .preset_of_place(*p)
155                            .into_iter()
156                            .filter(|ot_id| {
157                                pn.transitions
158                                    .get(&ot_id.get_uuid())
159                                    .unwrap()
160                                    .label
161                                    .is_some()
162                            })
163                            .count()
164                            >= 1
165                })
166                .count()
167                >= 1;
168
169            let postset_connected = pn
170                .postset_of_transition(t.into())
171                .into_iter()
172                .filter(|p| {
173                    pn.is_in_initial_marking(p)
174                        || pn.is_in_a_final_marking(p)
175                        || pn
176                            .postset_of_place(*p)
177                            .into_iter()
178                            .filter(|ot_id| {
179                                pn.transitions
180                                    .get(&ot_id.get_uuid())
181                                    .unwrap()
182                                    .label
183                                    .is_some()
184                            })
185                            .count()
186                            >= 1
187                })
188                .count()
189                >= 1;
190
191            preset_connected && postset_connected
192        } else {
193            false
194        }
195    }
196    let num_disconnected_trans = pn
197        .transitions
198        .clone()
199        .into_iter()
200        .filter(|(_, t)| !is_transition_well_connected(pn, t))
201        .count();
202
203    return config.fitness_thresh
204        * (1.0 - config.balance_thresh)
205        * (1.0
206            - (num_disconnected_trans as f32
207                / pn.transitions
208                    .iter()
209                    .filter(|(_, t)| t.label.is_some())
210                    .count() as f32))
211            .powf(2.0);
212}