1use itertools::Itertools;
4use std::collections::HashMap;
5
6use crate::dbl::modal::model::{ModalDblModel, ModalOb};
7use crate::dbl::theory::Unital;
8use crate::one::category::FgCategory;
9use crate::stdlib::analyses::petri::transition_interface;
10use crate::zero::QualifiedName;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14#[cfg(feature = "serde-wasm")]
15use tsify::Tsify;
16
17#[derive(Clone)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "serde-wasm", derive(Tsify))]
21#[cfg_attr(
22 feature = "serde-wasm",
23 tsify(into_wasm_abi, from_wasm_abi, hashmap_as_object)
24)]
25pub struct ReachabilityProblemData {
26 pub tokens: HashMap<QualifiedName, i32>,
28
29 pub forbidden: HashMap<QualifiedName, i32>,
31}
32
33pub fn subreachability(m: &ModalDblModel<Unital>, data: ReachabilityProblemData) -> bool {
37 let ob_vec: Vec<_> = m.ob_generators().sorted().collect();
42 let ob_inv: HashMap<_, _> = ob_vec.iter().enumerate().map(|(x, y)| (y.clone(), x)).collect();
43 let n_p = ob_vec.len();
44 if n_p == 0 {
45 return true;
46 }
47
48 let hom_vec: Vec<_> = m.mor_generators().sorted().collect();
50
51 let hom_inv: HashMap<_, _> = hom_vec.iter().enumerate().map(|(x, y)| (y.clone(), x)).collect();
52 let n_t = hom_vec.len();
53
54 let mut i_mat = vec![vec![0; n_t]; n_p];
56 let mut o_mat = vec![vec![0; n_t]; n_p];
57
58 for e in m.mor_generators() {
59 let e_idx = *hom_inv.get(&e).unwrap();
60 let (inputs, outputs) = transition_interface(m, &e);
61 for ob in inputs {
62 if let ModalOb::Generator(u) = ob {
63 i_mat[*ob_inv.get(&u).unwrap()][e_idx] += 1;
64 }
65 }
66 for ob in outputs {
67 if let ModalOb::Generator(u) = ob {
68 o_mat[*ob_inv.get(&u).unwrap()][e_idx] += 1;
69 }
70 }
71 }
72 let (i_mat_, o_mat_) = (&i_mat, &o_mat);
73
74 let mut f: Vec<Vec<_>> =
77 vec![ob_vec.iter().map(|u| *data.forbidden.get(u).unwrap_or(&0)).collect()];
78 let init: Vec<_> = ob_vec.iter().map(|u| *data.tokens.get(u).unwrap_or(&0)).collect();
79
80 loop {
83 let pre: Vec<Vec<_>> = (0..n_t)
86 .flat_map(|t| {
87 f.iter().map(move |v| {
88 (0..n_p).map(move |p| {
89 std::cmp::max(i_mat_[p][t], v[p] - (o_mat_[p][t] - i_mat_[p][t]))
90 })
91 })
92 })
93 .map(|z| z.collect())
94 .collect();
95
96 let newstuff: Vec<Vec<_>> = pre
98 .into_iter()
99 .filter(|v| f.iter().all(|old| (0..n_p).any(|p| v[p] < old[p])))
100 .unique()
101 .collect();
102
103 if newstuff.is_empty() {
105 break;
106 }
107
108 f.retain(|v| newstuff.iter().all(|n| (0..n_p).any(|p| v[p] < n[p])));
110 f.extend(newstuff);
111 }
112
113 let init_in_forbbiden = f.iter().any(|v| (0..n_p).all(|p| v[p] <= init[p]));
116 !init_in_forbbiden
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::dbl::{model::*, theory::*};
123 use crate::stdlib::th_sym_monoidal_category;
124 use crate::zero::name;
125 use std::rc::Rc;
126
127 #[test]
158 fn validate_subreachability() {
159 let th = Rc::new(th_sym_monoidal_category());
161 let (ob_type, op) = (ModalObType::new(name("Object")), name("tensor"));
162 let mut model = ModalDblModel::new(th);
163 let (p1, p2, p3, t1, t2, t3) =
164 (name("p1"), name("p2"), name("p3"), name("t1"), name("t2"), name("t3"));
165 model.add_ob(p1.clone(), ob_type.clone());
166 model.add_ob(p2.clone(), ob_type.clone());
167 model.add_ob(p3.clone(), ob_type.clone());
168 let [x, y, z] = [p1.clone(), p2.clone(), p3.clone()].map(ModalOb::from);
169 model.add_mor(
170 t1,
171 ModalOb::App(ModalOb::List(List::Symmetric, vec![]).into(), op.clone()),
172 ModalOb::App(ModalOb::List(List::Symmetric, vec![x.clone()]).into(), op.clone()),
173 ModalMorType::Zero(ob_type.clone()),
174 );
175 model.add_mor(
176 t2,
177 ModalOb::App(
178 ModalOb::List(List::Symmetric, vec![x.clone(), y.clone()]).into(),
179 op.clone(),
180 ),
181 ModalOb::App(ModalOb::List(List::Symmetric, vec![z.clone()]).into(), op.clone()),
182 ModalMorType::Zero(ob_type.clone()),
183 );
184 model.add_mor(
185 t3,
186 ModalOb::App(ModalOb::List(List::Symmetric, vec![z.clone()]).into(), op.clone()),
187 ModalOb::App(ModalOb::List(List::Symmetric, vec![y.clone()]).into(), op.clone()),
188 ModalMorType::Zero(ob_type),
189 );
190
191 fn test_input(m: &ModalDblModel<Unital>, x1: i32, x2: i32, x3: i32, expect: bool) {
193 let (p1, p2, p3) = (name("p1"), name("p2"), name("p3"));
194 let forbidden = HashMap::from_iter([(p1.clone(), 0), (p2.clone(), 0), (p3.clone(), 2)]);
195
196 let data = ReachabilityProblemData {
197 tokens: HashMap::from_iter([(p1, x1), (p2, x2), (p3, x3)]),
198 forbidden: forbidden.clone(),
199 };
200 assert_eq!(subreachability(m, data), expect);
201 }
202
203 test_input(&model, 0, 0, 2, false);
204 test_input(&model, 0, 1, 1, false);
205 test_input(&model, 0, 2, 0, false);
206 test_input(&model, 1, 0, 1, true);
207 test_input(&model, 1, 1, 0, true);
208 }
209}