pnets_shrunk/reducers/standard/
simple_loop_agglomeration.rs

1use indexed_vec::IndexVec;
2
3use pnets::standard::Net;
4use pnets::{arc, PlaceId, TransitionId};
5
6use crate::modifications::{Agglomeration, Modification};
7use crate::reducers::reduce::ConservativeReduce;
8use crate::reducers::Reduce;
9
10#[derive(Clone)]
11struct SimpleLoopAgglomerationGraphNode {
12    connected: Vec<(TransitionId, PlaceId)>,
13    num: Option<usize>,
14    accessible_num: Option<usize>,
15    in_stack: bool,
16}
17
18#[derive(Default)]
19struct Partition {
20    transitions: Vec<TransitionId>,
21    places: Vec<PlaceId>,
22}
23
24impl Partition {
25    pub fn add(&mut self, tr: TransitionId, pl: PlaceId) {
26        match self.transitions.binary_search(&tr) {
27            Ok(_) => {} // element already in vector @ `pos`
28            Err(pos) => self.transitions.insert(pos, tr),
29        }
30        match self.places.binary_search(&pl) {
31            Ok(_) => {} // element already in vector @ `pos`
32            Err(pos) => self.places.insert(pos, pl),
33        }
34    }
35}
36
37/// Implementation of the Tarjan algorithm on petri network
38/// 
39/// We apply this algorithm on the graph formed by all transition which has only one consumption and
40/// one production (those transitions correspond to those found in the SLA reduction)
41struct SimpleLoopAgglomerationGraph {
42    pub nodes: IndexVec<TransitionId, SimpleLoopAgglomerationGraphNode>,
43    partitions: Vec<Partition>,
44    current_num: usize,
45    stack: Vec<(TransitionId, PlaceId)>,
46}
47
48impl SimpleLoopAgglomerationGraph {
49    pub fn new(node_count: usize) -> Self {
50        let nodes: IndexVec<TransitionId, SimpleLoopAgglomerationGraphNode> = IndexVec::from_elem_n(
51            SimpleLoopAgglomerationGraphNode {
52                connected: vec![],
53                num: None,
54                accessible_num: None,
55                in_stack: false,
56            },
57            node_count,
58        );
59        Self {
60            nodes,
61            partitions: vec![],
62            current_num: 0,
63            stack: vec![],
64        }
65    }
66
67    fn walk(&mut self, net: &Net, source_tr: TransitionId) {
68        let source_transition = &net[source_tr];
69
70        // We know that source_tr has only one input/output place, with a weight of 1
71        if (source_transition.consume.len() != 1)
72            || source_transition.produce.len() != 1
73            || (source_transition.consume.iter().next().unwrap().1 != 1)
74            || (source_transition.produce.iter().next().unwrap().1 != 1)
75        {
76            return;
77        }
78        self.nodes[source_tr].num = Some(self.current_num);
79        self.nodes[source_tr].accessible_num = Some(self.current_num);
80        self.nodes[source_tr].in_stack = true;
81        self.current_num += 1;
82
83        let pl = match source_transition.produce.iter().next() {
84            Some(&(pl, _)) => pl,
85            _ => return,
86        };
87        let place = &net[pl];
88        self.stack.push((source_tr, pl));
89        for &tr in place.consumed_by.iter().filter_map(|(tr, _)| {
90            if net[*tr].consume.len() == 1
91                && net[*tr].produce.len() == 1
92                && (source_transition.consume.iter().next().unwrap().1 == 1)
93                && (source_transition.produce.iter().next().unwrap().1 == 1)
94            {
95                Some(tr)
96            } else {
97                None
98            }
99        }) {
100            if self.nodes[tr].num.is_none() {
101                self.walk(net, tr);
102                self.nodes[source_tr].accessible_num = Some(
103                    self.nodes[source_tr]
104                        .accessible_num
105                        .unwrap()
106                        .min(self.nodes[tr].accessible_num.unwrap()),
107                );
108            } else if self.nodes[tr].in_stack {
109                self.nodes[source_tr].accessible_num = Some(
110                    self.nodes[source_tr]
111                        .accessible_num
112                        .unwrap()
113                        .min(self.nodes[tr].num.unwrap()),
114                );
115            }
116        }
117
118        if self.nodes[source_tr].accessible_num == self.nodes[source_tr].num {
119            let mut component = Partition::default();
120            while let Some((tr, pl)) = self.stack.pop() {
121                component.add(tr, pl);
122                if tr == source_tr {
123                    break;
124                }
125            }
126            if component.transitions.len() > 1 && component.places.len() > 1 {
127                self.partitions.push(component);
128            }
129        }
130    }
131
132    pub fn compute_partitions(&mut self, net: &Net) {
133        for n in (0..self.nodes.len()).map(|n| TransitionId::from(n)) {
134            if self.nodes[n].num.is_none() {
135                self.walk(net, n);
136            }
137        }
138    }
139}
140
141/// Remove simple loopd from the network and replace them by a unique place
142///
143/// See Definition 6, page 8 [STTT](https://doi.org/10.1007/s10009-019-00519-1)
144pub struct SimpleLoopAgglomeration;
145
146impl ConservativeReduce<Net> for SimpleLoopAgglomeration {}
147
148impl Reduce<Net> for SimpleLoopAgglomeration {
149    fn reduce(net: &mut Net, modifications: &mut Vec<Modification>) {
150        // Compute partitions of the transition graph
151        let mut graph = SimpleLoopAgglomerationGraph::new(net.transitions.len());
152        graph.compute_partitions(net);
153
154        // Each partition can be replaced with a new agglomerated place
155        for partition in &graph.partitions {
156            let new_place = net.create_place();
157            // Copy all arcs and merge initial marking in the new place
158            let mut arcs = vec![];
159            let mut initial = 0;
160            for &pl in &partition.places {
161                arcs.extend(net[pl].get_arcs());
162                initial += net[pl].initial;
163                net.delete_place(pl);
164            }
165            net[new_place].initial = initial;
166            for original_arc in &arcs {
167                match *original_arc {
168                    arc::Kind::Consume(_, tr, w) => {
169                        net.add_arc(arc::Kind::Consume(new_place, tr, w)).unwrap();
170                    }
171                    arc::Kind::Produce(_, tr, w) => {
172                        net.add_arc(arc::Kind::Produce(new_place, tr, w)).unwrap();
173                    }
174                    _ => {}
175                }
176            }
177            modifications.push(Modification::Agglomeration(Agglomeration {
178                deleted_places: partition.places.iter().map(|pl| (*pl, 1)).collect(),
179                new_place,
180                constant: 0,
181                factor: 1,
182            }));
183        }
184    }
185}