1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4use libpetri_core::input::In;
5use libpetri_core::output::Out;
6use libpetri_core::petri_net::PetriNet;
7use libpetri_core::place::PlaceRef;
8use libpetri_core::transition::Transition;
9
10use crate::bitmap;
11use crate::compiled_net::CompiledNet;
12
13pub const CONSUME_ONE: u32 = 0;
15pub const CONSUME_N: u32 = 1;
16pub const CONSUME_ALL: u32 = 2;
17pub const CONSUME_ATLEAST: u32 = 3;
18pub const RESET: u32 = 4;
19
20#[derive(Debug, Clone)]
22pub enum SparseMask {
23 Empty,
25 Single { word_index: usize, mask: u64 },
27 Multi {
29 indices: Vec<usize>,
30 masks: Vec<u64>,
31 },
32}
33
34#[derive(Debug)]
39pub struct PrecompiledNet<'c> {
40 compiled: &'c CompiledNet,
41
42 pub(crate) consume_ops: Vec<Vec<u32>>,
44 pub(crate) read_ops: Vec<Vec<usize>>,
46
47 pub(crate) needs_sparse: Vec<SparseMask>,
49 pub(crate) inhibitor_sparse: Vec<SparseMask>,
50
51 pub(crate) earliest_ms: Vec<f64>,
53 pub(crate) latest_ms: Vec<f64>,
54 pub(crate) has_deadline: Vec<bool>,
55 pub(crate) all_immediate: bool,
56 pub(crate) any_deadlines: bool,
57
58 pub(crate) priorities: Vec<i32>,
60 pub(crate) transition_to_priority_index: Vec<usize>,
61 #[allow(dead_code)]
62 pub(crate) priority_levels: Vec<i32>,
63 pub(crate) distinct_priority_count: usize,
64 pub(crate) all_same_priority: bool,
65
66 #[allow(dead_code)]
68 pub(crate) simple_output_place_id: Vec<i32>,
69
70 #[allow(dead_code)]
72 pub(crate) input_place_count: Vec<usize>,
73
74 pub(crate) input_place_mask_words: Vec<Vec<u64>>,
76
77 pub(crate) place_name_arcs: Vec<Arc<str>>,
79 pub(crate) transition_name_arcs: Vec<Arc<str>>,
81 pub(crate) output_place_name_sets: Vec<HashSet<Arc<str>>>,
83}
84
85impl<'c> PrecompiledNet<'c> {
86 pub fn from_compiled(compiled: &'c CompiledNet) -> Self {
88 let tc = compiled.transition_count;
89 let wc = compiled.word_count;
90
91 let mut needs_sparse = Vec::with_capacity(tc);
93 let mut inhibitor_sparse = Vec::with_capacity(tc);
94
95 for tid in 0..tc {
96 needs_sparse.push(compile_sparse(compiled.needs_mask(tid)));
97 inhibitor_sparse.push(compile_sparse(compiled.inhibitor_mask(tid)));
98 }
99
100 let mut consume_ops = Vec::with_capacity(tc);
102 let mut read_ops = Vec::with_capacity(tc);
103 let mut input_place_mask_words = Vec::with_capacity(tc);
104 let mut simple_output_place_id = Vec::with_capacity(tc);
105 let mut input_place_count = Vec::with_capacity(tc);
106
107 for tid in 0..tc {
108 let t = compiled.transition(tid);
109 consume_ops.push(compile_consume_ops(t, compiled));
110 read_ops.push(compile_read_ops(t, compiled));
111 input_place_mask_words.push(compile_input_mask(t, compiled, wc));
112
113 input_place_count.push(t.input_specs().len() + t.reads().len());
114
115 match t.output_spec() {
117 None => simple_output_place_id.push(-2),
118 Some(Out::Place(p)) => {
119 simple_output_place_id.push(compiled.place_id(p.name()).unwrap_or(0) as i32);
120 }
121 Some(_) => simple_output_place_id.push(-1),
122 }
123 }
124
125 let mut earliest_ms = vec![0.0f64; tc];
127 let mut latest_ms = vec![f64::MAX; tc];
128 let mut has_deadline = vec![false; tc];
129 let mut any_deadlines = false;
130 let mut all_immediate = true;
131
132 for tid in 0..tc {
133 let t = compiled.transition(tid);
134 earliest_ms[tid] = t.timing().earliest() as f64;
135 if t.timing().has_deadline() {
136 latest_ms[tid] = t.timing().latest() as f64;
137 has_deadline[tid] = true;
138 any_deadlines = true;
139 }
140 if *t.timing() != libpetri_core::timing::Timing::Immediate {
141 all_immediate = false;
142 }
143 }
144
145 let mut priorities = vec![0i32; tc];
147 let mut distinct_set = std::collections::BTreeSet::new();
148 for (tid, prio) in priorities.iter_mut().enumerate() {
149 *prio = compiled.transition(tid).priority();
150 distinct_set.insert(std::cmp::Reverse(*prio));
151 }
152 let priority_levels: Vec<i32> = distinct_set.into_iter().map(|r| r.0).collect();
153 let distinct_priority_count = priority_levels.len();
154 let all_same_priority = distinct_priority_count <= 1;
155
156 let prio_to_index: HashMap<i32, usize> = priority_levels
157 .iter()
158 .enumerate()
159 .map(|(i, &p)| (p, i))
160 .collect();
161 let transition_to_priority_index: Vec<usize> =
162 (0..tc).map(|tid| prio_to_index[&priorities[tid]]).collect();
163
164 let pc = compiled.place_count;
166 let place_name_arcs: Vec<Arc<str>> = (0..pc)
167 .map(|pid| Arc::clone(compiled.place(pid).name_arc()))
168 .collect();
169 let transition_name_arcs: Vec<Arc<str>> = (0..tc)
170 .map(|tid| Arc::clone(compiled.transition(tid).name_arc()))
171 .collect();
172
173 let output_place_name_sets: Vec<HashSet<Arc<str>>> = (0..tc)
175 .map(|tid| {
176 compiled
177 .transition(tid)
178 .output_places()
179 .iter()
180 .map(|p| Arc::clone(p.name_arc()))
181 .collect()
182 })
183 .collect();
184
185 PrecompiledNet {
186 compiled,
187 consume_ops,
188 read_ops,
189 needs_sparse,
190 inhibitor_sparse,
191 earliest_ms,
192 latest_ms,
193 has_deadline,
194 all_immediate,
195 any_deadlines,
196 priorities,
197 transition_to_priority_index,
198 priority_levels,
199 distinct_priority_count,
200 all_same_priority,
201 simple_output_place_id,
202 input_place_count,
203 input_place_mask_words,
204 place_name_arcs,
205 transition_name_arcs,
206 output_place_name_sets,
207 }
208 }
209
210 pub fn compiled(&self) -> &'c CompiledNet {
214 self.compiled
215 }
216
217 pub fn net(&self) -> &PetriNet {
219 self.compiled.net()
220 }
221
222 pub fn place(&self, pid: usize) -> &PlaceRef {
224 self.compiled.place(pid)
225 }
226
227 pub fn transition(&self, tid: usize) -> &Transition {
229 self.compiled.transition(tid)
230 }
231
232 pub fn place_id(&self, name: &str) -> Option<usize> {
234 self.compiled.place_id(name)
235 }
236
237 pub fn place_count(&self) -> usize {
239 self.compiled.place_count
240 }
241
242 pub fn transition_count(&self) -> usize {
244 self.compiled.transition_count
245 }
246
247 pub fn word_count(&self) -> usize {
249 self.compiled.word_count
250 }
251
252 pub fn can_enable_bitmap(&self, tid: usize, marking_snapshot: &[u64]) -> bool {
256 match &self.needs_sparse[tid] {
258 SparseMask::Empty => {}
259 SparseMask::Single { word_index, mask } => {
260 if *word_index >= marking_snapshot.len()
261 || (marking_snapshot[*word_index] & mask) != *mask
262 {
263 return false;
264 }
265 }
266 SparseMask::Multi { indices, masks } => {
267 for i in 0..indices.len() {
268 let w = indices[i];
269 if w >= marking_snapshot.len() || (marking_snapshot[w] & masks[i]) != masks[i] {
270 return false;
271 }
272 }
273 }
274 }
275
276 match &self.inhibitor_sparse[tid] {
278 SparseMask::Empty => true,
279 SparseMask::Single { word_index, mask } => {
280 *word_index >= marking_snapshot.len() || (marking_snapshot[*word_index] & mask) == 0
281 }
282 SparseMask::Multi { indices, masks } => {
283 for i in 0..indices.len() {
284 let w = indices[i];
285 if w < marking_snapshot.len() && (marking_snapshot[w] & masks[i]) != 0 {
286 return false;
287 }
288 }
289 true
290 }
291 }
292 }
293}
294
295fn compile_sparse(mask_words: &[u64]) -> SparseMask {
298 let mut non_zero_count = 0;
299 let mut last_non_zero = 0;
300 for (w, &word) in mask_words.iter().enumerate() {
301 if word != 0 {
302 non_zero_count += 1;
303 last_non_zero = w;
304 }
305 }
306
307 match non_zero_count {
308 0 => SparseMask::Empty,
309 1 => SparseMask::Single {
310 word_index: last_non_zero,
311 mask: mask_words[last_non_zero],
312 },
313 _ => {
314 let mut indices = Vec::with_capacity(non_zero_count);
315 let mut masks = Vec::with_capacity(non_zero_count);
316 for (w, &word) in mask_words.iter().enumerate() {
317 if word != 0 {
318 indices.push(w);
319 masks.push(word);
320 }
321 }
322 SparseMask::Multi { indices, masks }
323 }
324 }
325}
326
327fn compile_consume_ops(t: &Transition, compiled: &CompiledNet) -> Vec<u32> {
328 let mut ops = Vec::new();
329
330 for in_spec in t.input_specs() {
331 let pid = compiled.place_id(in_spec.place_name()).unwrap();
332 match in_spec {
333 In::One { .. } => {
334 ops.push(CONSUME_ONE);
335 ops.push(pid as u32);
336 }
337 In::Exactly { count, .. } => {
338 ops.push(CONSUME_N);
339 ops.push(pid as u32);
340 ops.push(*count as u32);
341 }
342 In::All { .. } => {
343 ops.push(CONSUME_ALL);
344 ops.push(pid as u32);
345 }
346 In::AtLeast { minimum, .. } => {
347 ops.push(CONSUME_ATLEAST);
348 ops.push(pid as u32);
349 ops.push(*minimum as u32);
350 }
351 }
352 }
353
354 for r in t.resets() {
355 let pid = compiled.place_id(r.place.name()).unwrap();
356 ops.push(RESET);
357 ops.push(pid as u32);
358 }
359
360 ops
361}
362
363fn compile_read_ops(t: &Transition, compiled: &CompiledNet) -> Vec<usize> {
364 t.reads()
365 .iter()
366 .map(|r| compiled.place_id(r.place.name()).unwrap())
367 .collect()
368}
369
370fn compile_input_mask(t: &Transition, compiled: &CompiledNet, word_count: usize) -> Vec<u64> {
371 let mut mask = vec![0u64; word_count];
372 for in_spec in t.input_specs() {
373 let pid = compiled.place_id(in_spec.place_name()).unwrap();
374 bitmap::set_bit(&mut mask, pid);
375 }
376 mask
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use libpetri_core::input::one;
383 use libpetri_core::output::out_place;
384 use libpetri_core::place::Place;
385
386 fn simple_chain_net() -> PetriNet {
387 let p1 = Place::<i32>::new("p1");
388 let p2 = Place::<i32>::new("p2");
389 let p3 = Place::<i32>::new("p3");
390
391 let t1 = Transition::builder("t1")
392 .input(one(&p1))
393 .output(out_place(&p2))
394 .build();
395 let t2 = Transition::builder("t2")
396 .input(one(&p2))
397 .output(out_place(&p3))
398 .build();
399
400 PetriNet::builder("chain").transitions([t1, t2]).build()
401 }
402
403 #[test]
404 fn compile_basic() {
405 let net = simple_chain_net();
406 let compiled = CompiledNet::compile(&net);
407 let prog = PrecompiledNet::from_compiled(&compiled);
408
409 assert_eq!(prog.place_count(), 3);
410 assert_eq!(prog.transition_count(), 2);
411 assert!(prog.word_count() >= 1);
412 assert!(prog.all_immediate);
413 assert!(prog.all_same_priority);
414 }
415
416 #[test]
417 fn sparse_enablement() {
418 let net = simple_chain_net();
419 let compiled = CompiledNet::compile(&net);
420 let prog = PrecompiledNet::from_compiled(&compiled);
421
422 let mut snapshot = vec![0u64; prog.word_count()];
423
424 assert!(!prog.can_enable_bitmap(0, &snapshot));
426 assert!(!prog.can_enable_bitmap(1, &snapshot));
427
428 let p1_id = prog.place_id("p1").unwrap();
430 bitmap::set_bit(&mut snapshot, p1_id);
431 assert!(prog.can_enable_bitmap(0, &snapshot));
432 assert!(!prog.can_enable_bitmap(1, &snapshot));
433
434 let p2_id = prog.place_id("p2").unwrap();
436 bitmap::set_bit(&mut snapshot, p2_id);
437 assert!(prog.can_enable_bitmap(0, &snapshot));
438 assert!(prog.can_enable_bitmap(1, &snapshot));
439 }
440
441 #[test]
442 fn consume_ops_compiled() {
443 let net = simple_chain_net();
444 let compiled = CompiledNet::compile(&net);
445 let prog = PrecompiledNet::from_compiled(&compiled);
446
447 assert_eq!(prog.consume_ops[0].len(), 2);
449 assert_eq!(prog.consume_ops[0][0], CONSUME_ONE);
450 }
451
452 #[test]
453 fn priority_partitioning() {
454 let p = Place::<()>::new("p");
455 let out = Place::<()>::new("out");
456
457 let t_high = Transition::builder("t_high")
458 .input(one(&p))
459 .output(out_place(&out))
460 .priority(10)
461 .build();
462 let t_low = Transition::builder("t_low")
463 .input(one(&p))
464 .output(out_place(&out))
465 .priority(1)
466 .build();
467
468 let net = PetriNet::builder("prio")
469 .transitions([t_high, t_low])
470 .build();
471 let compiled = CompiledNet::compile(&net);
472 let prog = PrecompiledNet::from_compiled(&compiled);
473
474 assert_eq!(prog.distinct_priority_count, 2);
475 assert!(!prog.all_same_priority);
476 assert_eq!(prog.priority_levels[0], 10); assert_eq!(prog.priority_levels[1], 1);
478 }
479
480 #[test]
481 fn inhibitor_sparse() {
482 let p1 = Place::<i32>::new("p1");
483 let p2 = Place::<i32>::new("p2");
484 let p_inh = Place::<i32>::new("inh");
485
486 let t = Transition::builder("t1")
487 .input(one(&p1))
488 .output(out_place(&p2))
489 .inhibitor(libpetri_core::arc::inhibitor(&p_inh))
490 .build();
491
492 let net = PetriNet::builder("test").transition(t).build();
493 let compiled = CompiledNet::compile(&net);
494 let prog = PrecompiledNet::from_compiled(&compiled);
495
496 let mut snapshot = vec![0u64; prog.word_count()];
497 let p1_id = prog.place_id("p1").unwrap();
498 let inh_id = prog.place_id("inh").unwrap();
499
500 bitmap::set_bit(&mut snapshot, p1_id);
501 assert!(prog.can_enable_bitmap(0, &snapshot));
502
503 bitmap::set_bit(&mut snapshot, inh_id);
504 assert!(!prog.can_enable_bitmap(0, &snapshot));
505 }
506}