petri_net_simulation/
helpers.rs

1use std::collections::HashSet;
2
3use pns::{Net, Node, Pid, Tid};
4
5struct CountSearchInfo {
6    pids: fn(&Node<Pid>) -> &[Pid],
7    tids: fn(&Node<Tid>) -> &[Tid],
8    used: HashSet<Tid>,
9}
10
11#[inline(always)]
12fn minimal_count(
13    net: &Net,
14    tid: Tid,
15    info: &mut CountSearchInfo,
16    alt: &mut CountSearchInfo,
17) -> Option<usize> {
18    info.used.insert(tid);
19    let mut current_minimal_count = None;
20    'outer: for &pid in (info.pids)(net.transition(tid)) {
21        let mut count = net.initial_token_count(pid);
22        for &tid in (info.tids)(net.place(pid)) {
23            if alt.used.contains(&tid) {
24                continue;
25            }
26            if info.used.contains(&tid) {
27                continue 'outer;
28            }
29            let Some(add) = minimal_count(net, tid, info, alt) else {
30                continue 'outer;
31            };
32            count += add;
33        }
34        for &tid in (alt.tids)(net.place(pid)) {
35            if info.used.contains(&tid) {
36                continue;
37            }
38            if alt.used.contains(&tid) {
39                continue 'outer;
40            }
41            let Some(add) = minimal_count(net, tid, alt, info) else {
42                continue 'outer;
43            };
44            count += add;
45        }
46        if let Some(minimal_count) = &mut current_minimal_count {
47            if count <= *minimal_count {
48                *minimal_count = count;
49            }
50        } else {
51            current_minimal_count = Some(count);
52        }
53    }
54
55    current_minimal_count
56}
57
58/// Calculate how often a transaction could theoretically be fired.
59pub fn maximum_fire_count(net: &Net, tid: Tid) -> Option<usize> {
60    minimal_count(
61        net,
62        tid,
63        &mut CountSearchInfo {
64            pids: Node::prev,
65            tids: Node::prev,
66            used: HashSet::new(),
67        },
68        &mut CountSearchInfo {
69            pids: Node::next,
70            tids: Node::next,
71            used: HashSet::new(),
72        },
73    )
74}
75
76/// Calculate how often a transaction could theoretically be unfired.
77pub fn maximum_unfire_count(net: &Net, tid: Tid) -> Option<usize> {
78    minimal_count(
79        net,
80        tid,
81        &mut CountSearchInfo {
82            pids: Node::next,
83            tids: Node::next,
84            used: HashSet::new(),
85        },
86        &mut CountSearchInfo {
87            pids: Node::prev,
88            tids: Node::prev,
89            used: HashSet::new(),
90        },
91    )
92}