1use crate::{
2 ActivityKeyTranslator, HasActivityKey, PetriNetMarkupLanguage,
3 StochasticNondeterministicFiniteAutomaton,
4 ebi_objects::{
5 deterministic_finite_automaton::DeterministicFiniteAutomaton,
6 directly_follows_graph::DirectlyFollowsGraph,
7 directly_follows_model::DirectlyFollowsModel,
8 labelled_petri_net::LabelledPetriNet,
9 lola_net::LolaNet,
10 process_tree::{Node, Operator, ProcessTree},
11 process_tree_markup_language::ProcessTreeMarkupLanguage,
12 stochastic_deterministic_finite_automaton::StochasticDeterministicFiniteAutomaton,
13 stochastic_directly_follows_model::StochasticDirectlyFollowsModel,
14 stochastic_labelled_petri_net::StochasticLabelledPetriNet,
15 stochastic_process_tree::StochasticProcessTree,
16 },
17 marking::Marking,
18};
19use ebi_arithmetic::anyhow::{Error, Result, anyhow};
20use std::collections::HashMap;
21
22macro_rules! tree {
23 ($t:ident) => {
24 impl From<$t> for LabelledPetriNet {
25 fn from(value: $t) -> Self {
26 log::info!("convert (stochastic) process tree to LPN");
27
28 if value.tree.is_empty() {
29 return Self::new_empty_language();
30 }
31
32 let mut result = LabelledPetriNet::new();
33 let translator =
34 ActivityKeyTranslator::new(value.activity_key(), result.activity_key_mut());
35 let source = result.add_place();
36 let sink = result.add_place();
37 result
38 .get_initial_marking_mut()
39 .increase(source, 1)
40 .unwrap();
41
42 value
43 .node_to_lpn(0, &mut result, &translator, source, sink)
44 .unwrap();
45
46 result
47 }
48 }
49
50 impl $t {
51 pub fn node_to_lpn(
52 &self,
53 node: usize,
54 net: &mut LabelledPetriNet,
55 translator: &ActivityKeyTranslator,
56 source: usize,
57 sink: usize,
58 ) -> Result<usize> {
59 match self.tree[node] {
60 Node::Tau => {
61 let transition = net.add_transition(None);
62 net.add_place_transition_arc(source, transition, 1)?;
63 net.add_transition_place_arc(transition, sink, 1)?;
64 Ok(node + 1)
65 }
66 Node::Activity(activity) => {
67 let transition =
68 net.add_transition(Some(translator.translate_activity(&activity)));
69 net.add_place_transition_arc(source, transition, 1)?;
70 net.add_transition_place_arc(transition, sink, 1)?;
71 Ok(node + 1)
72 }
73 Node::Operator(Operator::Concurrent, number_of_children) => {
74 let split = net.add_transition(None);
75 net.add_place_transition_arc(source, split, 1)?;
76 let join = net.add_transition(None);
77 net.add_transition_place_arc(join, sink, 1)?;
78
79 let mut child = node + 1;
80 for _ in 0..number_of_children {
81 let child_source = net.add_place();
82 net.add_transition_place_arc(split, child_source, 1)?;
83 let child_sink = net.add_place();
84 net.add_place_transition_arc(child_sink, join, 1)?;
85 child =
86 self.node_to_lpn(child, net, translator, child_source, child_sink)?;
87 }
88 Ok(child)
89 }
90 Node::Operator(Operator::Interleaved, number_of_children) => {
91 let split = net.add_transition(None);
92 net.add_place_transition_arc(source, split, 1)?;
93 let join = net.add_transition(None);
94 net.add_transition_place_arc(join, sink, 1)?;
95 let milestone = net.add_place();
96 net.add_transition_place_arc(split, milestone, 1)?;
97 net.add_place_transition_arc(milestone, join, 1)?;
98
99 let mut child = node + 1;
100 for _ in 0..number_of_children {
101 let child_source = net.add_place();
102 net.add_transition_place_arc(split, child_source, 1)?;
103
104 let child_start = net.add_transition(None);
105 net.add_place_transition_arc(child_source, child_start, 1)?;
106 net.add_place_transition_arc(milestone, child_start, 1)?;
107
108 let child_source_2 = net.add_place();
109 net.add_transition_place_arc(child_start, child_source_2, 1)?;
110
111 let child_sink = net.add_place();
112 net.add_place_transition_arc(child_sink, join, 1)?;
113
114 let child_stop = net.add_transition(None);
115 net.add_transition_place_arc(child_stop, child_sink, 1)?;
116 net.add_transition_place_arc(child_stop, milestone, 1)?;
117
118 let child_sink_2 = net.add_place();
119 net.add_place_transition_arc(child_sink_2, child_stop, 1)?;
120
121 child = self.node_to_lpn(
122 child,
123 net,
124 translator,
125 child_source_2,
126 child_sink_2,
127 )?;
128 }
129 Ok(child)
130 }
131 Node::Operator(Operator::Loop, number_of_children) => {
132 let start = net.add_transition(None);
133 net.add_place_transition_arc(source, start, 1)?;
134
135 let join = net.add_place();
136 net.add_transition_place_arc(start, join, 1)?;
137
138 let split = net.add_place();
139 let stop = net.add_transition(None);
140 net.add_place_transition_arc(split, stop, 1)?;
141 net.add_transition_place_arc(stop, sink, 1)?;
142
143 let mut child = node + 1;
144 child = self.node_to_lpn(child, net, translator, join, split)?;
145
146 if number_of_children > 1 {
147 for _ in 1..number_of_children {
148 child = self.node_to_lpn(child, net, translator, split, join)?;
149 }
150 } else {
151 let redo = net.add_transition(None);
152 net.add_place_transition_arc(split, redo, 1)?;
153 net.add_transition_place_arc(redo, join, 1)?;
154 }
155
156 Ok(child)
157 }
158 Node::Operator(Operator::Or, number_of_children) => {
159 let start = net.add_transition(None);
160 net.add_place_transition_arc(source, start, 1)?;
161
162 let not_done_first = net.add_place();
163 net.add_transition_place_arc(start, not_done_first, 1)?;
164
165 let done_first = net.add_place();
166 let end = net.add_transition(None);
167 net.add_place_transition_arc(done_first, end, 1)?;
168 net.add_transition_place_arc(end, sink, 1)?;
169
170 let mut child = node + 1;
171 for _ in 0..number_of_children {
172 let child_source = net.add_place();
173 net.add_transition_place_arc(start, child_source, 1)?;
174 let child_sink = net.add_place();
175 net.add_place_transition_arc(child_sink, end, 1)?;
176 let do_child = net.add_place();
177
178 let skip_child = net.add_transition(None);
180 net.add_place_transition_arc(child_source, skip_child, 1)?;
181 net.add_transition_place_arc(skip_child, child_sink, 1)?;
182 net.add_transition_place_arc(skip_child, done_first, 1)?;
183 net.add_place_transition_arc(done_first, skip_child, 1)?;
184
185 let first_do_child = net.add_transition(None);
187 net.add_place_transition_arc(child_source, first_do_child, 1)?;
188 net.add_place_transition_arc(not_done_first, first_do_child, 1)?;
189 net.add_transition_place_arc(first_do_child, done_first, 1)?;
190 net.add_transition_place_arc(first_do_child, do_child, 1)?;
191
192 let later_do_child = net.add_transition(None);
194 net.add_place_transition_arc(child_source, later_do_child, 1)?;
195 net.add_transition_place_arc(later_do_child, do_child, 1)?;
196 net.add_transition_place_arc(later_do_child, done_first, 1)?;
197 net.add_place_transition_arc(done_first, later_do_child, 1)?;
198
199 child =
200 self.node_to_lpn(child, net, translator, do_child, child_sink)?;
201 }
202
203 Ok(child)
204 }
205 Node::Operator(Operator::Sequence, number_of_children) => {
206 let intermediate_nodes = (0..(number_of_children - 1))
207 .map(|_| net.add_place())
208 .collect::<Vec<_>>();
209
210 let mut child = node + 1;
211 for i in 0..number_of_children {
212 let child_entry = if i == 0 {
213 source
214 } else {
215 intermediate_nodes[i - 1]
216 };
217 let child_exit = if i == number_of_children - 1 {
218 sink
219 } else {
220 intermediate_nodes[i]
221 };
222
223 child = $t::node_to_lpn(
224 &self,
225 child,
226 net,
227 translator,
228 child_entry,
229 child_exit,
230 )?;
231 }
232 Ok(child)
233 }
234 Node::Operator(Operator::Xor, number_of_children) => {
235 let mut child = node + 1;
236 for _ in 0..number_of_children {
237 child = $t::node_to_lpn(&self, child, net, translator, source, sink)?;
238 }
239 Ok(child)
240 }
241 }
242 }
243 }
244 };
245}
246
247tree!(ProcessTree);
248tree!(StochasticProcessTree);
249
250impl From<ProcessTreeMarkupLanguage> for LabelledPetriNet {
251 fn from(value: ProcessTreeMarkupLanguage) -> Self {
252 log::info!("convert process tree markup language to LPN");
253 value.tree.into()
254 }
255}
256
257impl From<LolaNet> for LabelledPetriNet {
258 fn from(value: LolaNet) -> Self {
259 log::info!("convert Lola net to LPN");
260 value.0
261 }
262}
263
264impl From<PetriNetMarkupLanguage> for LabelledPetriNet {
265 fn from(value: PetriNetMarkupLanguage) -> Self {
266 log::info!("convert PNML to LPN");
267 value.0
268 }
269}
270
271macro_rules! dfm {
272 ($t:ident) => {
273 impl From<$t> for LabelledPetriNet {
274 fn from(value: $t) -> LabelledPetriNet {
275 log::info!("convert (S)DFM to LPN");
276
277 if value.node_2_activity.is_empty() && !value.has_empty_traces() {
278 return Self::new_empty_language();
280 }
281
282 let mut result = LabelledPetriNet::new();
283 let translator =
284 ActivityKeyTranslator::new(value.activity_key(), result.activity_key_mut());
285 let source = result.add_place();
286 let sink = result.add_place();
287 result
288 .get_initial_marking_mut()
289 .increase(source, 1)
290 .unwrap();
291
292 if value.has_empty_traces() {
296 let transition = result.add_transition(None);
297
298 result
299 .add_place_transition_arc(source, transition, 1)
300 .unwrap();
301 result
302 .add_transition_place_arc(transition, sink, 1)
303 .unwrap();
304 }
305
306 let mut node2place = vec![];
310 for _ in 0..value.node_2_activity.len() {
311 let place = result.add_place();
312 node2place.push(place);
313 }
314
315 for (edge, (source_node, target_node)) in
319 value.sources.iter().zip(value.targets.iter()).enumerate()
320 {
321 if value.can_execute_edge(edge) {
322 let from_place = node2place[*source_node];
323 let to_place = node2place[*target_node];
324 let activity =
325 translator.translate_activity(&value.node_2_activity[*target_node]);
326 let transition = result.add_transition(Some(activity));
327
328 result
329 .add_place_transition_arc(from_place, transition, 1)
330 .unwrap();
331 result
332 .add_transition_place_arc(transition, to_place, 1)
333 .unwrap();
334 }
335 }
336
337 for node in 0..value.node_2_activity.len() {
341 if value.is_start_node(node) {
342 let activity = translator.translate_activity(&value.node_2_activity[node]);
343 let transition = result.add_transition(Some(activity));
344 result
345 .add_place_transition_arc(source, transition, 1)
346 .unwrap();
347 let target_place = node2place[node];
348 result
349 .add_transition_place_arc(transition, target_place, 1)
350 .unwrap();
351 }
352 }
353
354 for node in 0..value.node_2_activity.len() {
358 if value.is_end_node(node) {
359 let transition = result.add_transition(None);
360 let source_place = node2place[node];
361 result
362 .add_place_transition_arc(source_place, transition, 1)
363 .unwrap();
364 result
365 .add_transition_place_arc(transition, sink, 1)
366 .unwrap();
367 }
368 }
369
370 result
371 }
372 }
373 };
374}
375
376dfm!(DirectlyFollowsModel);
377dfm!(StochasticDirectlyFollowsModel);
378
379impl From<StochasticLabelledPetriNet> for LabelledPetriNet {
380 fn from(value: StochasticLabelledPetriNet) -> Self {
381 log::info!("convert SLPN to LPN");
382 Self {
383 activity_key: value.activity_key,
384 initial_marking: value.initial_marking,
385 labels: value.labels,
386 place2output_transitions: value.place2output_transitions,
387 transition2input_places: value.transition2input_places,
388 transition2output_places: value.transition2output_places,
389 transition2input_places_cardinality: value.transition2input_places_cardinality,
390 transition2output_places_cardinality: value.transition2output_places_cardinality,
391 }
392 }
393}
394
395impl From<DeterministicFiniteAutomaton> for LabelledPetriNet {
396 fn from(value: DeterministicFiniteAutomaton) -> Self {
397 log::info!("convert DFA to LPN");
398
399 let source = if let Some(s) = value.initial_state {
400 s
401 } else {
402 return Self {
404 activity_key: value.activity_key,
405 initial_marking: Marking::new(0),
406 labels: vec![None],
407 transition2input_places: vec![vec![]],
408 transition2output_places: vec![vec![]],
409 transition2input_places_cardinality: vec![vec![]],
410 transition2output_places_cardinality: vec![vec![]],
411 place2output_transitions: vec![],
412 };
413 };
414
415 let mut result = LabelledPetriNet::new();
416 let translator =
417 ActivityKeyTranslator::new(value.activity_key(), result.activity_key_mut());
418
419 let mut state2place = vec![];
421 for state in 0..value.number_of_states() {
422 let lpn_place = result.add_place();
423 state2place.push(lpn_place);
424
425 if value.can_terminate_in_state(state) {
427 let lpn_transition = result.add_transition(None);
428 result
429 .add_place_transition_arc(lpn_place, lpn_transition, 1)
430 .unwrap();
431 }
432 }
433
434 result
436 .get_initial_marking_mut()
437 .increase(source, 1)
438 .unwrap();
439
440 for (source, (target, activity)) in value
442 .sources
443 .iter()
444 .zip(value.targets.iter().zip(value.activities.iter()))
445 {
446 let lpn_activity = translator.translate_activity(activity);
448 let lpn_transition = result.add_transition(Some(lpn_activity));
449 let source_place = state2place[*source];
450 let target_place = state2place[*target];
451 result
452 .add_place_transition_arc(source_place, lpn_transition, 1)
453 .unwrap();
454 result
455 .add_transition_place_arc(lpn_transition, target_place, 1)
456 .unwrap();
457 }
458
459 result
460 }
461}
462
463impl TryFrom<process_mining::PetriNet> for LabelledPetriNet {
464 type Error = Error;
465
466 fn try_from(pnml: process_mining::PetriNet) -> Result<Self, Self::Error> {
467 log::info!("convert PNML to LPN");
468
469 let mut result = LabelledPetriNet::new();
470
471 let mut place2index = HashMap::new();
473 for (place_id, _) in pnml.places {
474 let place = result.add_place();
475 place2index.insert(place_id, place);
476 }
477
478 let mut transition2index = HashMap::new();
480 for (transition_id, transition) in &pnml.transitions {
481 let label = match &transition.label {
482 Some(activity) => Some(result.activity_key_mut().process_activity(activity)),
483 None => None,
484 };
485 let transition = result.add_transition(label);
486
487 transition2index.insert(transition_id, transition);
488 }
489
490 for arc in pnml.arcs.iter() {
492 match arc.from_to {
493 process_mining::core::process_models::petri_net::ArcType::PlaceTransition(
494 place_id,
495 transition_id,
496 ) => {
497 let new_place = place2index
498 .get(&place_id)
499 .ok_or(anyhow!("Undeclared place referenced."))?;
500 let new_transition = transition2index
501 .get(&transition_id)
502 .ok_or(anyhow!("undeclared transition referenced"))?;
503 result.add_place_transition_arc(
504 *new_place,
505 *new_transition,
506 arc.weight.into(),
507 )?;
508 }
509 process_mining::core::process_models::petri_net::ArcType::TransitionPlace(
510 transition_id,
511 place_id,
512 ) => {
513 let new_place = place2index
514 .get(&place_id)
515 .ok_or(anyhow!("Undeclared place referenced."))?;
516 let new_transition = transition2index
517 .get(&transition_id)
518 .ok_or(anyhow!("undeclared transition referenced"))?;
519 result.add_transition_place_arc(
520 *new_transition,
521 *new_place,
522 arc.weight.into(),
523 )?;
524 }
525 };
526 }
527
528 if let Some(marking) = pnml.initial_marking.as_ref() {
531 for (place_id, cardinality) in marking {
532 let new_place = place2index
533 .get(&place_id.get_uuid())
534 .ok_or(anyhow!("Undeclared place found in the initial marking."))?;
535 result
536 .get_initial_marking_mut()
537 .increase(*new_place, *cardinality)?;
538 }
539 }
540
541 if let Some(final_markings) = &pnml.final_markings {
543 for final_marking in final_markings.iter() {
546 let mut new_final_marking = Marking::new(result.get_number_of_places());
548 for (place_id, cardinality) in final_marking.iter() {
549 let new_place = place2index
550 .get(&place_id.get_uuid())
551 .ok_or(anyhow!("Undeclared place found."))?;
552 new_final_marking.increase(*new_place, *cardinality)?;
553 }
554
555 for transition in 0..result.transition2input_places.len() {
557 let mut marking = new_final_marking.clone();
558 let mut enabled = true;
559 for (in_place_pos, in_place) in result.transition2input_places[transition]
560 .iter()
561 .enumerate()
562 {
563 if marking.place2token[*in_place]
564 < result.transition2input_places_cardinality[transition][in_place_pos]
565 {
566 enabled = false;
568 break;
569 } else {
570 marking.place2token[*in_place] -=
572 result.transition2input_places_cardinality[transition][in_place_pos]
573 }
574 }
575
576 if enabled {
577 return Err(anyhow!(
578 "This PNML file has a final marking that is not a deadlock. In Ebi, each final marking must be a deadlock. This final marking is {:?}",
579 final_marking
580 ));
581 }
582 }
583 }
584 }
585
586 Ok(result)
587 }
588}
589
590impl From<StochasticDeterministicFiniteAutomaton> for LabelledPetriNet {
591 fn from(value: StochasticDeterministicFiniteAutomaton) -> Self {
592 let dfa: DeterministicFiniteAutomaton = value.into();
593 dfa.into()
594 }
595}
596
597impl From<StochasticNondeterministicFiniteAutomaton> for LabelledPetriNet {
598 fn from(value: StochasticNondeterministicFiniteAutomaton) -> Self {
599 let slpn = StochasticLabelledPetriNet::from(value);
600 slpn.into()
601 }
602}
603
604impl From<DirectlyFollowsGraph> for LabelledPetriNet {
605 fn from(value: DirectlyFollowsGraph) -> Self {
606 <DirectlyFollowsGraph as Into<DirectlyFollowsModel>>::into(value).into()
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use std::fs;
613
614 use crate::{
615 StochasticDeterministicFiniteAutomaton,
616 ebi_objects::{
617 deterministic_finite_automaton::DeterministicFiniteAutomaton,
618 labelled_petri_net::LabelledPetriNet,
619 },
620 };
621
622 #[test]
623 fn dfa_to_lpn() {
624 let fin = fs::read_to_string("testfiles/a-loop.dfa").unwrap();
625 let dfa = fin.parse::<DeterministicFiniteAutomaton>().unwrap();
626 let lpn: LabelledPetriNet = dfa.into();
627
628 let fin2 = fs::read_to_string("testfiles/a-loop.lpn").unwrap();
629 let lpn2 = fin2.parse::<LabelledPetriNet>().unwrap();
630
631 assert_eq!(lpn.to_string(), lpn2.to_string());
632 }
633
634 #[test]
635 fn sdfa_dfa_lpn() {
636 let fin = fs::read_to_string("testfiles/a-livelock-zeroweight.sdfa").unwrap();
637 let sdfa = fin
638 .parse::<StochasticDeterministicFiniteAutomaton>()
639 .unwrap();
640 let _lpn: LabelledPetriNet = sdfa.into();
641 }
642}