1use crate::{
2 ActivityKey, ActivityKeyTranslator, CompressedEventLog, CompressedEventLogXes,
3 DirectlyFollowsModel, EventLogCsv, EventLogTraceAttributes, EventLogXes, NumberOfTraces,
4 ProcessTree, ProcessTreeMarkupLanguage, StochasticDirectlyFollowsModel,
5 StochasticNondeterministicFiniteAutomaton, StochasticProcessTree,
6 activity_key::has_activity_key::HasActivityKey,
7 ebi_objects::{
8 compressed_event_log_trace_attributes::CompressedEventLogTraceAttributes,
9 deterministic_finite_automaton::DeterministicFiniteAutomaton,
10 event_log::EventLog,
11 event_log_python::EventLogPython,
12 finite_language::FiniteLanguage,
13 finite_stochastic_language::FiniteStochasticLanguage,
14 process_tree::{
15 execute_transition, get_enabled_transitions, get_initial_state,
16 get_transition_activity, is_final_state,
17 },
18 stochastic_deterministic_finite_automaton::StochasticDeterministicFiniteAutomaton,
19 },
20};
21use ebi_arithmetic::ebi_number::{Signed, Zero};
22use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
23
24impl From<FiniteLanguage> for DeterministicFiniteAutomaton {
25 fn from(value: FiniteLanguage) -> Self {
26 log::info!("convert finite language into a DFA");
27 let mut result = DeterministicFiniteAutomaton::new();
28 result.set_activity_key(value.activity_key().clone());
29
30 if value.number_of_traces().is_zero() {
31 result.set_initial_state(None);
32 } else {
33 for trace in value.traces.iter() {
34 let mut state = result.initial_state.unwrap();
35
36 for activity in trace {
37 state = result.take_or_add_transition(state, *activity);
38 }
39
40 result.set_final_state(state, true);
41 }
42 }
43
44 result
45 }
46}
47
48impl From<StochasticDeterministicFiniteAutomaton> for DeterministicFiniteAutomaton {
49 fn from(value: StochasticDeterministicFiniteAutomaton) -> Self {
50 log::info!("convert SDFA into DFA");
51 let final_states = value
52 .terminating_probabilities
53 .iter()
54 .map(|p| p.is_positive())
55 .collect();
56
57 Self {
58 activity_key: value.activity_key,
59 initial_state: value.initial_state,
60 sources: value.sources,
61 targets: value.targets,
62 activities: value.activities,
63 final_states: final_states,
64 }
65 }
66}
67
68impl From<DirectlyFollowsModel> for DeterministicFiniteAutomaton {
69 fn from(value: DirectlyFollowsModel) -> Self {
70 log::info!("convert DFM into DFA");
71
72 if value.node_2_activity.is_empty() && !value.has_empty_traces() {
73 return Self {
75 activity_key: ActivityKey::new(),
76 initial_state: None,
77 sources: vec![],
78 targets: vec![],
79 activities: vec![],
80 final_states: vec![],
81 };
82 }
83
84 let number_of_nodes = value.node_2_activity.len();
85
86 let DirectlyFollowsModel {
88 sources,
89 targets,
90 node_2_activity,
91 empty_traces,
92 start_nodes,
93 end_nodes,
94 ..
95 } = value;
96 let activities = targets
97 .iter()
98 .map(|target| node_2_activity[*target])
99 .collect();
100
101 let mut final_states = end_nodes;
103 final_states.push(empty_traces);
104
105 let mut result = Self {
107 activity_key: value.activity_key,
108 initial_state: Some(number_of_nodes),
109 sources,
110 targets,
111 activities,
112 final_states,
113 };
114
115 for start_node in 0..number_of_nodes {
117 if start_nodes[start_node] {
118 result
119 .add_transition(number_of_nodes, node_2_activity[start_node], start_node)
120 .unwrap(); }
122 }
123
124 result
125 }
126}
127
128impl From<StochasticDirectlyFollowsModel> for DeterministicFiniteAutomaton {
129 fn from(value: StochasticDirectlyFollowsModel) -> Self {
130 Into::<DirectlyFollowsModel>::into(value).into()
131 }
132}
133
134impl From<FiniteStochasticLanguage> for DeterministicFiniteAutomaton {
135 fn from(value: FiniteStochasticLanguage) -> Self {
136 Into::<FiniteLanguage>::into(value).into()
137 }
138}
139
140macro_rules! via_lang {
141 ($t:ident) => {
142 impl From<$t> for DeterministicFiniteAutomaton {
143 fn from(value: $t) -> Self {
144 Into::<FiniteLanguage>::into(value).into()
145 }
146 }
147 };
148}
149
150via_lang!(EventLog);
151via_lang!(EventLogTraceAttributes);
152via_lang!(EventLogCsv);
153via_lang!(EventLogXes);
154via_lang!(EventLogPython);
155via_lang!(CompressedEventLog);
156via_lang!(CompressedEventLogTraceAttributes);
157via_lang!(CompressedEventLogXes);
158
159impl From<ProcessTree> for DeterministicFiniteAutomaton {
160 fn from(tree: ProcessTree) -> Self {
161 log::info!("convert process tree to DFA");
162
163 let initial_marking = if let Some(marking) = get_initial_state(&tree) {
164 marking
165 } else {
166 return Self {
168 activity_key: ActivityKey::new(),
169 initial_state: None,
170 sources: vec![],
171 targets: vec![],
172 activities: vec![],
173 final_states: vec![],
174 };
175 };
176
177 let mut result = DeterministicFiniteAutomaton::new();
178 let translator = ActivityKeyTranslator::new(tree.activity_key(), result.activity_key_mut());
179
180 let mut markingset2node = HashMap::new();
181
182 let source_node = result.initial_state.unwrap(); let initial_markingset = BTreeSet::from([initial_marking]);
185 markingset2node.insert(initial_markingset.clone(), source_node);
186 let mut queue = VecDeque::new();
187 queue.push_back(initial_markingset);
188
189 while let Some(markingset) = queue.pop_front() {
190 let node = markingset2node[&markingset];
191
192 let mut activity2markings = HashMap::new();
194 {
195 let mut inner_queue = VecDeque::new();
196 let mut inner_visited = HashSet::new();
197 inner_queue.extend(markingset.clone());
198 inner_visited.extend(markingset);
199
200 while let Some(marking) = inner_queue.pop_front() {
202 if is_final_state(&tree, &marking) {
203 result.set_final_state(node, true);
204 } else {
205 for transition in get_enabled_transitions(&tree, &marking) {
206 let mut target_marking = marking.clone();
207 execute_transition(&tree, &mut target_marking, transition).unwrap(); if let Some(activity) = get_transition_activity(&tree, transition) {
210 activity2markings
212 .entry(translator.translate_activity(&activity))
213 .or_insert_with(|| BTreeSet::new())
214 .insert(target_marking);
215 } else {
216 if inner_visited.insert(target_marking.clone()) {
218 inner_queue.push_back(target_marking);
219 }
220 }
221 }
222 }
223 }
224 }
225
226 for (activity, markingset) in activity2markings {
228 let target_node = markingset2node
229 .entry(markingset.clone())
230 .or_insert_with(|| {
231 queue.push_back(markingset);
232 result.add_state()
233 });
234
235 result.add_transition(node, activity, *target_node).unwrap(); }
237 }
238
239 result
240 }
241}
242
243macro_rules! via_tree {
244 ($t:ident) => {
245 impl From<$t> for DeterministicFiniteAutomaton {
246 fn from(value: $t) -> Self {
247 let tree = ProcessTree::from(value);
248 tree.into()
249 }
250 }
251 };
252}
253
254via_tree!(StochasticProcessTree);
255via_tree!(ProcessTreeMarkupLanguage);
256
257impl From<StochasticNondeterministicFiniteAutomaton> for DeterministicFiniteAutomaton {
258 fn from(snfa: StochasticNondeterministicFiniteAutomaton) -> Self {
259 log::info!("convert SNFA to DFA");
260
261 let initial_marking = if let Some(marking) = snfa.initial_state {
262 marking
263 } else {
264 return Self {
266 activity_key: ActivityKey::new(),
267 initial_state: None,
268 sources: vec![],
269 targets: vec![],
270 activities: vec![],
271 final_states: vec![],
272 };
273 };
274
275 let mut result = DeterministicFiniteAutomaton::new();
276 let translator = ActivityKeyTranslator::new(snfa.activity_key(), result.activity_key_mut());
277
278 let mut markingset2node = HashMap::new();
279
280 let source_node = result.initial_state.unwrap(); let initial_markingset = BTreeSet::from([initial_marking]);
283 markingset2node.insert(initial_markingset.clone(), source_node);
284 let mut queue = VecDeque::new();
285 queue.push_back(initial_markingset);
286
287 while let Some(markingset) = queue.pop_front() {
288 let node = markingset2node[&markingset];
289
290 let mut activity2markings = HashMap::new();
292 {
293 let mut inner_queue = VecDeque::new();
294 let mut inner_visited = HashSet::new();
295 inner_queue.extend(markingset.clone());
296 inner_visited.extend(markingset);
297
298 while let Some(marking) = inner_queue.pop_front() {
300 if snfa.get_termination_probability(marking).is_positive() {
301 result.set_final_state(node, true);
302 } else {
303 for (_, target_marking, label, _) in snfa.outgoing_edges(marking) {
304 if let Some(activity) = label {
305 activity2markings
307 .entry(translator.translate_activity(&activity))
308 .or_insert_with(|| BTreeSet::new())
309 .insert(*target_marking);
310 } else {
311 if inner_visited.insert(target_marking.clone()) {
313 inner_queue.push_back(*target_marking);
314 }
315 }
316 }
317 }
318 }
319 }
320
321 for (activity, markingset) in activity2markings {
323 let target_node = markingset2node
324 .entry(markingset.clone())
325 .or_insert_with(|| {
326 queue.push_back(markingset);
327 result.add_state()
328 });
329
330 result.add_transition(node, activity, *target_node).unwrap(); }
332 }
333
334 result
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use crate::{
341 DeterministicFiniteAutomaton, ProcessTree, StochasticDirectlyFollowsModel,
342 StochasticNondeterministicFiniteAutomaton, StochasticProcessTree,
343 };
344 use std::fs;
345
346 #[test]
347 fn tree_to_dfa() {
348 let fin = fs::read_to_string("testfiles/seq(a-xor(b-c)).sptree").unwrap();
349 let tree: ProcessTree = fin.parse::<StochasticProcessTree>().unwrap().into();
350
351 let dfa = DeterministicFiniteAutomaton::from(tree);
352 assert_eq!(
353 format!("{}", dfa),
354 "{
355\"initialState\": 0,
356\"transitions\": [
357{\"from\":0,\"to\":1,\"label\":\"a\"},
358{\"from\":1,\"to\":2,\"label\":\"b\"},
359{\"from\":1,\"to\":2,\"label\":\"c\"}
360], \"finalStates\": [
3612
362]}
363"
364 );
365 }
366
367 #[test]
368 fn snfa_to_dfa() {
369 let fin = fs::read_to_string("testfiles/aa-ab-ba.snfa").unwrap();
370 let snfa = fin
371 .parse::<StochasticNondeterministicFiniteAutomaton>()
372 .unwrap();
373
374 let dfa = DeterministicFiniteAutomaton::from(snfa);
375
376 assert_eq!(dfa.number_of_states(), 4);
377 assert_eq!(dfa.get_sources().len(), 5);
378 }
379
380 #[test]
381 fn dfm_to_dfa() {
382 let fin = fs::read_to_string("testfiles/aa-ab-ba.sdfm").unwrap();
383 let sdfm = fin.parse::<StochasticDirectlyFollowsModel>().unwrap();
384
385 let dfa = DeterministicFiniteAutomaton::from(sdfm);
386
387 assert_eq!(dfa.number_of_states(), 3);
388 assert_eq!(dfa.sources.len(), 5);
389 }
390}